diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/ALSA-Configuration.txt linux/Documentation/ALSA/ALSA-Configuration.txt --- linux-2.4.21-rc1.orig/Documentation/ALSA/ALSA-Configuration.txt 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/ALSA-Configuration.txt 2003-02-25 08:58:57.000000000 -0700 @@ -0,0 +1,1219 @@ + + Advanced Linux Sound Architecture - Driver + ========================================== + Configuration guide + + +Kernel Configuration +==================== + +To enable the ALSA support, at least you need to build the kernel with +the primary sound card support (CONFIG_SOUND). Since ALSA can emulate +the OSS, you don't have to choose any of the OSS/Free modules. Please +enable "OSS API emulation" (CONFIG_SND_OSSEMUL) and both OSS mixer and +PCM supports if you want to run the OSS application with the ALSA. + +When you want to support the WaveTable functionality on some cards +such like SB Live!, you need to enable "Sequencer support" +(CONFIG_SND_SEQUENCER). + +For getting more verbose debug messages, turn on "Verbose printk" and +"Debug" options. For checking the memory leaks, you can turn on +"Debug memory" option, too. "Debug detection" will put more +additional checks for the detection of cards. + +Please note that all the ALSA ISA drivers support Linux isapnp API (if +the card supports). You don't need to configure the PnP via +isapnptools. + + +Module parameters +================= + + A user can modify or set parameters at the load time of the module. If + the module supports more cards and you have got more than one card + of the same type, you may simply specify more values for the parameter, + delimited by commas. + + Note that module option names were changed in 0.9.0rc4. The 'snd_' + prefix was removed. + + Module snd + ---------- + + The module snd is the ALSA core module, which is used by all ALSA + card drivers. This takes the global options for creating devices, + etc. + + major - major # for sound driver + - default is 116 + cards_limit + - specifies card limit # (1-8) + - good for kmod support if you do not want to search + for soundcards which are not installed in your system + device_mode + - specifies permission mask for dynamic sound device filesystem + - default value = 0666 + - for example 'device_mode=0660' + device_gid + - specifies GID number for dynamic sound device filesystem + - default value = 0 (root) + device_uid + - specifies UID number for dynamic sound device filesystem + - default value = 0 (root) + + + Module snd-pcm-oss + ------------------ + + The PCM OSS emulation module. + This module takes the options to change the mapping of devices. + + dsp_map - PCM device number maps assigned to the 1st OSS device. + (default: 0) + adsp_map - PCM device number maps assigned to the 2st OSS device. + (default: 1) + nonblock_open - Don't block opening busy PCM devices. + + For example, when dsp_map=2, /dev/dsp will be mapped to PCM #2 of + the card #0. Similarly, when adsp_map=0, /dev/adsp will be mapped + to PCM #0 of the card #0. + For changing the second or later card, specify the option with + commas, such like "dsp_map=0,1". + + nonblock_open option is used to change the behavior of the PCM + regarding opening the device. When this option is non-zero, + opening a busy OSS PCM device won't be blocked but return + immediately with EAGAIN (just like O_NONBLOCK flag). + + Module snd-rawmidi + ------------------ + + This module takes the options to change the mapping of OSS + devices like snd-pcm-oss module. + + midi_map - MIDI device number maps assigned to the 1st OSS device. + (default: 0) + amidi_map - MIDI device number maps assigned to the 2st OSS device. + (default: 1) + + Global parameters for top soundcard modules + ------------------------------------------- + + Each of top-level soundcard module takes some general options, + + index - 0-7 - index (slot #) for soundcard + - if not set or -1, first free index (slot #) is assigned + id - user identification for card (up to 15 chars) + - default expression is 'card' (for example card1) + - value is used for /proc/asound filesystem + - this value can be used by applications for identification + of card if user does not want identify card with index number + enable - enable card (only first card is enabled by default) + + Module snd-ad1816a + ------------------ + + Module for soundcards based on Analog Devices AD1816A/AD1815 ISA chips. + + port - port # for AD1816A chip (PnP setup) + mpu_port - port # for MPU-401 UART (PnP setup) + fm_port - port # for OPL3 (PnP setup) + irq - IRQ # for AD1816A chip (PnP setup) + mpu_irq - IRQ # for MPU-401 UART (PnP setup) + dma1 - first DMA # for AD1816A chip (PnP setup) + dma2 - second DMA # for AD1816A chip (PnP setup) + + Module supports up to 8 cards, autoprobe and PnP. + + Module snd-ad1848 + ----------------- + + Module for soundcards based on AD1848/AD1847/CS4248 ISA chips. + + port - port # for AD1848 chip + irq - IRQ # for AD1848 chip + dma1 - DMA # for AD1848 chip (0,1,3) + + Module supports up to 8 cards. This module does not support autoprobe + thus main port must be specified!!! Other ports are optional. + + Module snd-ali5451 + ------------------ + + Module for ALi M5451 PCI chip. + + pcm_channels - Number of hardware channels assigned for PCM + + Module supports autoprobe and multiple chips (max 8). + + The power-management is supported. + + Module snd-als100 + ----------------- + + Module for soundcards based on Avance Logic ALS100/ALS120 ISA chips. + + port - port # for ALS100 (SB16) chip (PnP setup) + irq - IRQ # for ALS100 (SB16) chip (PnP setup) + dma8 - 8-bit DMA # for ALS100 (SB16) chip (PnP setup) + dma16 - 16-bit DMA # for ALS100 (SB16) chip (PnP setup) + mpu_port - port # for MPU-401 UART (PnP setup) + mpu_irq - IRQ # for MPU-401 (PnP setup) + fm_port - port # for OPL3 FM (PnP setup) + + Module supports up to 8 cards, autoprobe and PnP. + + Module snd-als4000 + ------------------ + + Module for soundcards based on Avance Logic ALS4000 PCI chip. + + joystick_port - port # for legacy joystick support. + default: 0x200 for the 1st card. + 0 = disabled + + Module supports up to 8 cards, autoprobe and PnP. + + Module snd-azt2320 + ------------------ + + Module for soundcards based on Aztech System AZT2320 ISA chip (PnP only). + + port - port # for AZT2320 chip (PnP setup) + wss_port - port # for WSS (PnP setup) + mpu_port - port # for MPU-401 UART (PnP setup) + fm_port - FM port # for AZT2320 chip (PnP setup) + irq - IRQ # for AZT2320 (WSS) chip (PnP setup) + mpu_irq - IRQ # for MPU-401 UART (PnP setup) + dma1 - 1st DMA # for AZT2320 (WSS) chip (PnP setup) + dma2 - 2nd DMA # for AZT2320 (WSS) chip (PnP setup) + + Module supports up to 8 cards, PnP and autoprobe. + + Module snd-cmi8330 + ------------------ + + Module for soundcards based on C-Media CMI8330 ISA chips. + + wssport - port # for CMI8330 chip (WSS) + wssirq - IRQ # for CMI8330 chip (WSS) + wssdma - first DMA # for CMI8330 chip (WSS) + sbport - port # for CMI8330 chip (SB16) + sbirq - IRQ # for CMI8330 chip (SB16) + sbdma8 - 8bit DMA # for CMI8330 chip (SB16) + sbdma16 - 16bit DMA # for CMI8330 chip (SB16) + + Module supports up to 8 cards and autoprobe. + + Module snd-cmipci + ----------------- + + Module for C-Media CMI8338 and 8738 PCI soundcards. + + mpu_port - 0x300 (default),0x310,0x320,0x330, -1 (diable) + fm_port - 0x388 (default), -1 (disable) + soft_ac3 - Sofware-conversion of raw SPDIF packets (model 033 only) + (default = 1) + + Module supports autoprobe and multiple chips (max 8). + + Module snd-cs4231 + ----------------- + + Module for soundcards based on CS4231 ISA chips. + + port - port # for CS4231 chip + mpu_port - port # for MPU-401 UART (optional), -1 = disable + irq - IRQ # for CS4231 chip + mpu_irq - IRQ # for MPU-401 UART + dma1 - first DMA # for CS4231 chip + dma2 - second DMA # for CS4231 chip + + Module supports up to 8 cards. This module does not support autoprobe + thus main port must be specified!!! Other ports are optional. + + The power-management is supported. + + Module snd-cs4232 + ----------------- + + Module for soundcards based on CS4232/CS4232A ISA chips. + + port - port # for CS4232 chip (PnP setup - 0x534) + cport - control port # for CS4232 chip (PnP setup - 0x120,0x210,0xf00) + mpu_port - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable + fm_port - FM port # for CS4232 chip (PnP setup - 0x388), -1 = disable + irq - IRQ # for CS4232 chip (5,7,9,11,12,15) + mpu_irq - IRQ # for MPU-401 UART (9,11,12,15) + dma1 - first DMA # for CS4232 chip (0,1,3) + dma2 - second DMA # for Yamaha CS4232 chip (0,1,3), -1 = disable + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + Module supports up to 8 cards. This module does not support autoprobe + thus main port must be specified!!! Other ports are optional. + + The power-management is supported. + + Module snd-cs4236 + ----------------- + + Module for soundcards based on CS4235/CS4236/CS4236B/CS4237B/ + CS4238B/CS4239 ISA chips. + + port - port # for CS4236 chip (PnP setup - 0x534) + cport - control port # for CS4236 chip (PnP setup - 0x120,0x210,0xf00) + mpu_port - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable + fm_port - FM port # for CS4236 chip (PnP setup - 0x388), -1 = disable + irq - IRQ # for CS4236 chip (5,7,9,11,12,15) + mpu_irq - IRQ # for MPU-401 UART (9,11,12,15) + dma1 - first DMA # for CS4236 chip (0,1,3) + dma2 - second DMA # for CS4236 chip (0,1,3), -1 = disable + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + Module supports up to 8 cards. This module does not support autoprobe + (if ISA PnP is not used) thus main port and control port must be + specified!!! Other ports are optional. + + The power-management is supported. + + Module snd-cs4281 + ----------------- + + Module for Cirrus Logic CS4281 soundchip. + + dual_codec - Secondary codec ID (0 = disable, default) + + Module supports up to 8 cards. + + The power-management is supported. + + Module snd-cs46xx + ----------------- + + Module for PCI soundcards based on CS4610/CS4612/CS4614/CS4615/CS4622/ + CS4624/CS4630/CS4280 PCI chips. + + external_amp - Force to enable external amplifer. + thinkpad - Force to enable Thinkpad's CLKRUN control. + mmap_valid - Support OSS mmap mode (default = 0). + + Module supports up to 8 cards and autoprobe. + Usually external amp and CLKRUN controls are detected automatically + from PCI sub vendor/device ids. If they don't work, give the options + above explicitly. + + The power-management is supported. + + Module snd-dt019x + ----------------- + + Module for Diamond Technologies DT-019X / Avance Logic ALS-007 (PnP + only) + + port - Port # (PnP setup) + mpu_port - Port # for MPU-401 (PnP setup) + fm_port - Port # for FM OPL-3 (PnP setup) + irq - IRQ # (PnP setup) + mpu_irq - IRQ # for MPU-401 (PnP setup) + dma8 - DMA # (PnP setup) + + Module supports up to 8 cards. This module is enabled only with + ISA PnP support. + + Module snd-dummy + ---------------- + + Module for the dummy soundcard. This soundcard doesn't do any output + or input, but you may use this module for any application which + requires a soundcard (like RealPlayer). + + Module snd-emu10k1 + ------------------ + + Module for EMU10K1/EMU10k2 based PCI soundcards. + * Sound Blaster Live! + * Sound Blaster PCI 512 + * Emu APS (partially supported) + * Sound Blaster Audigy + + extin - bitmap of available external inputs for FX8010 (see bellow) + extout - bitmap of available external outputs for FX8010 (see bellow) + seq_ports - allocated sequencer ports (4 by default) + max_synth_voices - limit of voices used for wavetable (64 by default) + max_buffer_size - specifies the maximum size of wavetable/pcm buffers + given in MB unit. Default value is 128. + enable_ir - enable IR + + Module supports up to 8 cards and autoprobe. + + Input & Output configurations [extin/extout] + * Creative Card wo/Digital out [0x0003/0x1f03] + * Creative Card w/Digital out [0x0003/0x1f0f] + * Creative Card w/Digital CD in [0x000f/0x1f0f] + * Creative Card wo/Digital out + LiveDrive [0x3fc3/0x1fc3] + * Creative Card w/Digital out + LiveDrive [0x3fc3/0x1fcf] + * Creative Card w/Digital CD in + LiveDrive [0x3fcf/0x1fcf] + * Creative Card wo/Digital out + Digital I/O 2 [0x0fc3/0x1f0f] + * Creative Card w/Digital out + Digital I/O 2 [0x0fc3/0x1f0f] + * Creative Card w/Digital CD in + Digital I/O 2 [0x0fcf/0x1f0f] + * Creative Card 5.1/w Digital out + LiveDrive [0x3fc3/0x1fff] + * Creative Card all ins and outs [0x3fff/0x1fff] + + Module snd-ens1370 + ------------------ + + Module for Ensoniq AudioPCI ES1370 PCI soundcards. + * SoundBlaster PCI 64 + * SoundBlaster PCI 128 + + Module supports up to 8 cards and autoprobe. + + Module snd-ens1371 + ------------------ + + Module for Ensoniq AudioPCI ES1371 PCI soundcards. + * SoundBlaster PCI 64 + * SoundBlaster PCI 128 + * SoundBlaster Vibra PCI + + Module supports up to 8 cards and autoprobe. + + Module snd-es968 + ---------------- + + Module for soundcards based on ESS ES968 chip (PnP only). + + port - port # for ES968 (SB8) chip (PnP setup) + irq - IRQ # for ES968 (SB8) chip (PnP setup) + dma1 - DMA # for ES968 (SB8) chip (PnP setup) + + Module supports up to 8 cards, PnP and autoprobe. + + Module snd-es1688 + ----------------- + + Module for ESS AudioDrive ES-1688 and ES-688 soundcards. + + port - port # for ES-1688 chip (0x220,0x240,0x260) + mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default) + irq - IRQ # for ES-1688 chip (5,7,9,10) + mpu_irq - IRQ # for MPU-401 port (5,7,9,10) + dma8 - DMA # for ES-1688 chip (0,1,3) + + Module supports up to 8 cards and autoprobe (without MPU-401 port). + + Module snd-es18xx + ----------------- + + Module for ESS AudioDrive ES-18xx soundcards. + + port - port # for ES-18xx chip (0x220,0x240,0x260) + mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default) + fm_port - port # for FM (optional, not used) + irq - IRQ # for ES-18xx chip (5,7,9,10) + dma1 - first DMA # for ES-18xx chip (0,1,3) + dma2 - first DMA # for ES-18xx chip (0,1,3) + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + Module supports up to 8 cards ISA PnP and autoprobe (without MPU-401 port + if native ISA PnP routines are not used). + When dma2 is equal with dma1, the driver works as half-duplex. + + The power-management is supported. + + Module snd-es1938 + ----------------- + + Module for soundcards based on ESS Solo-1 (ES1938,ES1946) chips. + + Module supports up to 8 cards and autoprobe. + + Module snd-es1968 + ----------------- + + Module for soundcards based on ESS Maestro-1/2/2E (ES1968/ES1978) chips. + + total_bufsize - total buffer size in kB (1-4096kB) + pcm_substreams_p - playback channels (1-8, default=2) + pcm_substreams_c - capture channels (1-8, default=0) + clock - clock (0 = auto-detection) + use_pm - support the power-management (0 = off, 1 = on, + 2 = auto) + + Module supports up to 8 cards and autoprobe. + + The power-management is supported. + + Module snd-fm801 + ---------------- + + Module for ForteMedia FM801 based PCI soundcards. + + Module supports up to 8 cards and autoprobe. + + Module snd-gusclassic + --------------------- + + Module for Gravis UltraSound Classic soundcard. + + port - port # for GF1 chip (0x220,0x230,0x240,0x250,0x260) + irq - IRQ # for GF1 chip (3,5,9,11,12,15) + dma1 - DMA # for GF1 chip (1,3,5,6,7) + dma2 - DMA # for GF1 chip (1,3,5,6,7,-1=disable) + joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V) + voices - GF1 voices limit (14-32) + pcm_voices - reserved PCM voices + + Module supports up to 8 cards and autoprobe. + + Module snd-gusextreme + --------------------- + + Module for Gravis UltraSound Extreme (Synergy ViperMax) soundcard. + + port - port # for ES-1688 chip (0x220,0x230,0x240,0x250,0x260) + gf1_port - port # for GF1 chip (0x210,0x220,0x230,0x240,0x250,0x260,0x270) + mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable + irq - IRQ # for ES-1688 chip (5,7,9,10) + gf1_irq - IRQ # for GF1 chip (3,5,9,11,12,15) + mpu_irq - IRQ # for MPU-401 port (5,7,9,10) + dma8 - DMA # for ES-1688 chip (0,1,3) + dma1 - DMA # for GF1 chip (1,3,5,6,7) + joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V) + voices - GF1 voices limit (14-32) + pcm_voices - reserved PCM voices + + Module supports up to 8 cards and autoprobe (without MPU-401 port). + + Module snd-gusmax + ----------------- + + Module for Gravis UltraSound MAX soundcard. + + port - port # for GF1 chip (0x220,0x230,0x240,0x250,0x260) + irq - IRQ # for GF1 chip (3,5,9,11,12,15) + dma1 - DMA # for GF1 chip (1,3,5,6,7) + dma2 - DMA # for GF1 chip (1,3,5,6,7,-1=disable) + joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V) + voices - GF1 voices limit (14-32) + pcm_voices - reserved PCM voices + + Module supports up to 8 cards and autoprobe. + + Module snd-hdsp + --------------- + + Module for RME Hammerfall DSP audio interface(s) + + precise_ptr - Enable precise pointer (doesn't work reliably). + (default = 0) + line_outs_monitor - Send all input and playback streams to line outs + by default. (default = 0) + force_firmware - Force a reload of the I/O box firmware + (default = 0) + + Module supports up to 8 cards. + + Module snd-ice1712 + ------------------ + + Module for Envy24 (ICE1712) based PCI soundcards. + * MidiMan M Audio Delta 1010 + * MidiMan M Audio Delta 1010LT + * MidiMan M Audio Delta DiO 2496 + * MidiMan M Audio Delta 66 + * MidiMan M Audio Delta 44 + * MidiMan M Audio Delta 410 + * MidiMan M Audio Audiophile 2496 + * TerraTec EWS 88MT + * TerraTec EWS 88D + * TerraTec EWX 24/96 + * TerraTec DMX 6Fire + * Hoontech SoundTrack DSP 24 + * Hoontech SoundTrack DSP 24 Value + * Hoontech SoundTrack DSP 24 Media 7.1 + + omni - Omni I/O support for MidiMan M-Audio Delta44/66 + + Module supports up to 8 cards and autoprobe. Note: The consumer part + is not used with all Envy24 based cards (for example in the MidiMan Delta + serie). + + Module snd-ice1724 + ------------------ + + Module for Envy24HT (VT/ICE1724) based PCI soundcards. + * MidiMan M Audio Revolution 7.1 + * AMP Ltd AUDIO2000 + + Module supports up to 8 cards and autoprobe. + + Module snd-intel8x0 + ------------------- + + Module for AC'97 motherboards from Intel and compatibles. + * Intel i810/810E, i815, i820, i830, i84x, MX440 + * SiS 7012 (SiS 735) + * NVidia NForce, NForce2 + * AMD AMD768, AMD8111 + * ALi m5455 + + ac97_clock - AC'97 codec clock base (0 = auto-detect) + joystick_port - Joystick port # (0 = disabled, 0x200) + mpu_port - MPU401 port # (0 = disabled, 0x330,0x300) + + Module supports autoprobe and multiple bus-master chips (max 8). + Note: the latest driver supports auto-detection of chip clock. + if you still encounter too fast playback, specify the clock + explicitly via the module option "ac97_clock=41194". + + The joystick and MPU-401 are supported only certain hardwares. + MPU401 is experimental, It doesn't work perfectly. + + The power-management is supported. + + Module snd-interwave + -------------------- + + Module for Gravis UltraSound PnP, Dynasonic 3-D/Pro, STB Sound Rage 32 + and other soundcards based on AMD InterWave (tm) chip. + + port - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260) + irq - IRQ # for InterWave chip (3,5,9,11,12,15) + dma1 - DMA # for InterWave chip (0,1,3,5,6,7) + dma2 - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable) + joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V) + midi - 1 = MIDI UART enable, 0 = MIDI UART disable (default) + pcm_voices - reserved PCM voices for the synthesizer (default 2) + effect - 1 = InterWave effects enable (default 0); + requires 8 voices + + Module supports up to 8 cards, autoprobe and ISA PnP. + + Module snd-interwave-stb + ------------------------ + + Module for UltraSound 32-Pro (soundcard from STB used by Compaq) + and other soundcards based on AMD InterWave (tm) chip with TEA6330T + circuit for extended control of bass, treble and master volume. + + port - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260) + port_tc - tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380) + irq - IRQ # for InterWave chip (3,5,9,11,12,15) + dma1 - DMA # for InterWave chip (0,1,3,5,6,7) + dma2 - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable) + joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V) + midi - 1 = MIDI UART enable, 0 = MIDI UART disable (default) + pcm_voices - reserved PCM voices for the synthesizer (default 2) + effect - 1 = InterWave effects enable (default 0); + requires 8 voices + + Module supports up to 8 cards, autoprobe and ISA PnP. + + Module snd-korg1212 + ------------------- + + Module for Korg 1212 IO PCI card + + Module supports up to 8 cards. + + Module snd-maestro3 + ------------------- + + Module for Allegro/Maestro3 chips + + external_amp - enable external amp (enabled by default) + amp_gpio - GPIO pin number for external amp (0-15) or + -1 for default pin (8 for allegro, 1 for + others) + + Module supports autoprobe and multiple chips (max 8). + Note: the binding of amplifier is dependent on hardware. + If there is no sound even though all channels are unmuted, try to + specify other gpio connection via amp_gpio option. + For example, a Panasonic notebook might need "amp_gpio=0x0d" + option. + + The power-management is supported. + + Module snd-mpu401 + ----------------- + + Module for MPU-401 UART devices. + + port - port number or -1 (disable) + irq - IRQ number or -1 (disable) + + Module supports multiple devices (max 8). + + Module snd-mtpav + ---------------- + + Module for MOTU MidiTimePiece AV multiport MIDI (on the parallel + port). + + port - I/O port # for MTPAV (0x378,0x278, default=0x378) + irq - IRQ # for MTPAV (7,5, default=7) + hwports - number of supported hardware ports, default=8. + + Module supports only 1 card. This module has no enable option. + + Module snd-nm256 + ---------------- + + Module for NeoMagic NM256AV/ZX chips + + playback_bufsize - max playback frame size in kB (4-128kB) + capture_bufsize - max capture frame size in kB (4-128kB) + force_ac97 - 0 or 1 (disabled by default) + buffer_top - specify buffer top address + use_cache - 0 or 1 (disabled by default) + vaio_hack - alias buffer_top=0x25a800 + + Module supports autoprobe and multiple chips (max 8). + Note: on some notebooks the buffer address cannot be detected + automatically, or causes hang-up during initialization. + In such a case, specify the buffer top address explicity via + buffer_top option. + For example, + Sony F250: buffer_top=0x25a800 + Sony F270: buffer_top=0x272800 + The driver supports only ac97 codec. It's possible to force + to initialize/use ac97 although it's not detected. In such a + case, use force_ac97=1 option. + + The power-management is supported. + + Module snd-opl3sa2 + ------------------ + + Module for Yamaha OPL3-SA2/SA3 soundcards. + + port - control port # for OPL3-SA chip (0x370) + sb_port - SB port # for OPL3-SA chip (0x220,0x240) + wss_port - WSS port # for OPL3-SA chip (0x530,0xe80,0xf40,0x604) + midi_port - port # for MPU-401 UART (0x300,0x330), -1 = disable + fm_port - FM port # for OPL3-SA chip (0x388), -1 = disable + irq - IRQ # for OPL3-SA chip (5,7,9,10) + dma1 - first DMA # for Yamaha OPL3-SA chip (0,1,3) + dma2 - second DMA # for Yamaha OPL3-SA chip (0,1,3), -1 = disable + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + Module supports up to 8 cards and ISA PnP. This module does not support + autoprobe (if ISA PnP is not used) thus all ports must be specified!!! + + The power-management is supported. + + Module snd-opti92x-ad1848 + ------------------------- + + Module for soundcards based on OPTi 82c92x and Analog Devices AD1848 chips. + Module works with OAK Mozart cards as well. + + port - port # for WSS chip (0x530,0xe80,0xf40,0x604) + mpu_port - port # for MPU-401 UART (0x300,0x310,0x320,0x330) + fm_port - port # for OPL3 device (0x388) + irq - IRQ # for WSS chip (5,7,9,10,11) + mpu_irq - IRQ # for MPU-401 UART (5,7,9,10) + dma1 - first DMA # for WSS chip (0,1,3) + + This module supports only one card, autoprobe and PnP. + + Module snd-opti92x-cs4231 + ------------------------- + + Module for soundcards based on OPTi 82c92x and Crystal CS4231 chips. + + port - port # for WSS chip (0x530,0xe80,0xf40,0x604) + mpu_port - port # for MPU-401 UART (0x300,0x310,0x320,0x330) + fm_port - port # for OPL3 device (0x388) + irq - IRQ # for WSS chip (5,7,9,10,11) + mpu_irq - IRQ # for MPU-401 UART (5,7,9,10) + dma1 - first DMA # for WSS chip (0,1,3) + dma2 - second DMA # for WSS chip (0,1,3) + + This module supports only one card, autoprobe and PnP. + + Module snd-opti93x + ------------------ + + Module for soundcards based on OPTi 82c93x chips. + + port - port # for WSS chip (0x530,0xe80,0xf40,0x604) + mpu_port - port # for MPU-401 UART (0x300,0x310,0x320,0x330) + fm_port - port # for OPL3 device (0x388) + irq - IRQ # for WSS chip (5,7,9,10,11) + mpu_irq - IRQ # for MPU-401 UART (5,7,9,10) + dma1 - first DMA # for WSS chip (0,1,3) + dma2 - second DMA # for WSS chip (0,1,3) + + This module supports only one card, autoprobe and PnP. + + Module snd-powermac (on ppc only) + --------------------------------- + + Module for PowerMac, iMac and iBook on-board soundchips + + enable_beep - enable beep using PCM (enabled as default) + + Module supports autoprobe a chip. + Note: the driver may have problems regarding endianess. + + The power-management is supported. + + Module snd-rme32 + ---------------- + + Module for RME Digi32, Digi32 Pro and Digi32/8 (Sek'd Prodif32, + Prodif96 and Prodif Gold) soundcards. + + Module supports up to 8 cards. + + Module snd-rme96 + ---------------- + + Module for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST soundcards. + + Module supports up to 8 cards. + + Module snd-rme9652 + ------------------ + + Module for RME Digi9652 (Hammerfall, Hammerfall-Light) soundcards. + + precise_ptr - Enable precise pointer (doesn't work reliably). + (default = 0) + + Module supports up to 8 cards. + + Module snd-sa11xx-uda1341 (on arm only) + --------------------------------------- + + Module for Philips UDA1341TS on Compaq iPAQ H3600 soundcard. + + Module supports only one card. + Module has no enable and index options. + + Module snd-sb8 + -------------- + + Module for 8-bit SoundBlaster cards: SoundBlaster 1.0, + SoundBlaster 2.0, + SoundBlaster Pro + + port - port # for SB DSP chip (0x220,0x240,0x260) + irq - IRQ # for SB DSP chip (5,7,9,10) + dma8 - DMA # for SB DSP chip (1,3) + + Module supports up to 8 cards and autoprobe. + + Module snd-sb16 and snd-sbawe + ----------------------------- + + Module for 16-bit SoundBlaster cards: SoundBlaster 16 (PnP), + SoundBlaster AWE 32 (PnP), + SoundBlaster AWE 64 PnP + + port - port # for SB DSP 4.x chip (0x220,0x240,0x260) + mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disable + awe_port - base port # for EMU8000 synthesizer (0x620,0x640,0x660) + (snd-sbawe module only) + irq - IRQ # for SB DSP 4.x chip (5,7,9,10) + dma8 - 8-bit DMA # for SB DSP 4.x chip (0,1,3) + dma16 - 16-bit DMA # for SB DSP 4.x chip (5,6,7) + mic_agc - Mic Auto-Gain-Control - 0 = disable, 1 = enable (default) + csp - ASP/CSP chip support - 0 = disable (default), 1 = enable + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + Module supports up to 8 cards, autoprobe and ISA PnP. + + Note: To use Vibra16X cards in 16-bit half duplex mode, you must + disable 16bit DMA with dma16 = -1 module parameter. + Also, all Sound Blaster 16 type cards can operate in 16-bit + half duplex mode through 8-bit DMA channel by disabling their + 16-bit DMA channel. + + Module snd-sgalaxy + ------------------ + + Module for Aztech Sound Galaxy soundcard. + + sbport - Port # for SB16 interface (0x220,0x240) + wssport - Port # for WSS interface (0x530,0xe80,0xf40,0x604) + irq - IRQ # (7,9,10,11) + dma1 - DMA # + + Module supports up to 8 cards. + + Module snd-sun-amd7930 (on sparc only) + -------------------------------------- + + Module for AMD7930 sound chips found on Sparcs. + + Module supports up to 8 cards. + + Module snd-sun-cs4231 (on sparc only) + ------------------------------------- + + Module for CS4231 sound chips found on Sparcs. + + Module supports up to 8 cards. + + Module snd-wavefront + -------------------- + + Module for Turtle Beach Maui, Tropez and Tropez+ soundcards. + + cs4232_pcm_port - Port # for CS4232 PCM interface. + cs4232_pcm_irq - IRQ # for CS4232 PCM interface (5,7,9,11,12,15). + cs4232_mpu_port - Port # for CS4232 MPU-401 interface. + cs4232_mpu_irq - IRQ # for CS4232 MPU-401 interface (9,11,12,15). + use_cs4232_midi - Use CS4232 MPU-401 interface + (inaccessibly located inside your computer) + ics2115_port - Port # for ICS2115 + ics2115_irq - IRQ # for ICS2115 + fm_port - FM OPL-3 Port # + dma1 - DMA1 # for CS4232 PCM interface. + dma2 - DMA2 # for CS4232 PCM interface. + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + Module supports up to 8 cards and ISA PnP. + + Module snd-sonicvibes + --------------------- + + Module for S3 SonicVibes PCI soundcards. + * PINE Schubert 32 PCI + + reverb - Reverb Enable - 1 = enable, 0 = disable (default) + - SoundCard must have onboard SRAM for this. + mge - Mic Gain Enable - 1 = enable, 0 = disable (default) + + Module supports up to 8 cards and autoprobe. + + Module snd-serial-u16550 + ------------------------ + + Module for UART16550A serial MIDI ports. + + port - port # for UART16550A chip + irq - IRQ # for UART16550A chip, -1 = poll mode + speed - speed in bauds (9600,19200,38400,57600,115200) + 38400 = default + base - base for divisor in bauds (57600,115200,230400,460800) + 115200 = default + outs - number of MIDI ports in a serial port (1-4) + 1 = default + adaptor - Type of adaptor. + 0 = Soundcanvas, 1 = MS-124T, 2 = MS-124W S/A, + 3 = MS-124W M/B, 4 = Generic + + Module supports up to 8 cards. This module does not support autoprobe + thus the main port must be specified!!! Other options are optional. + + Module snd-trident + ------------------ + + Module for Trident 4DWave DX/NX soundcards. + * Best Union Miss Melody 4DWave PCI + * HIS 4DWave PCI + * Warpspeed ONSpeed 4DWave PCI + * AzTech PCI 64-Q3D + * Addonics SV 750 + * CHIC True Sound 4Dwave + * Shark Predator4D-PCI + * Jaton SonicWave 4D + + pcm_channels - max channels (voices) reserved for PCM + wavetable_size - max wavetable size in kB (4-?kb) + + Module supports up to 8 cards and autoprobe. + + The power-management is supported. + + Module snd-usb-audio + -------------------- + + Module for USB audio and USB MIDI devices. + + vid - Vendor ID for the device (optional) + pid - Product ID for the device (optional) + + This module supports up to 8 cards, autoprobe and hotplugging. + + Module snd-via82xx + ------------------ + + Module for AC'97 motherboards based on VIA 82C686A/686B, 8233 + (south) bridge. + + mpu_port - 0x300,0x310,0x320,0x330, otherwise obtain BIOS setup + ac97_clock - AC'97 codec clock base (default 48000Hz) + + Module supports autoprobe and multiple bus-master chips (max 8). + Note: on some SMP motherboards like MSI 694D the interrupts might + not be generated properly. In such a case, please try to + set the SMP (or MPS) version on BIOS to 1.1 instead of + default value 1.4. Then the interrupt number will be + assigned under 15. You might also upgrade your BIOS. + + Module snd-virmidi + ------------------ + + Module for virtual rawmidi devices. + This module creates virtual rawmidi devices which communicate + to the corresponding ALSA sequencer ports. + + midi_devs - MIDI devices # (1-8, default=4) + + Module supports up to 8 cards. + + Module snd-ymfpci + ----------------- + + Module for Yamaha PCI chips (YMF72x, YMF74x & YMF75x). + + mpu_port - 0x300,0x330,0x332,0x334, -1 (disable) by default + fm_port - 0x388,0x398,0x3a0,0x3a8, -1 (disable) by default + rear_switch - enable shared rear/line-in switch (bool) + + Module supports autoprobe and multiple chips (max 8). + + The power-management is supported. + + +Configuring Non-ISAPNP Cards +============================ + +When the kernel is configured with ISA-PnP support, the modules +supporting the isapnp cards will have module options "isapnp". +If this option is set, *only* the ISA-PnP devices will be probed. +For probing the non ISA-PnP cards, you have to pass "isapnp=0" option +together with the proper i/o and irq configuration. + +When the kernel is configured without ISA-PnP support, isapnp option +will be not built in. + + +modprobe/kmod support +===================== + +The modprobe program must know which modules are used for the +device major numbers. +Native ALSA devices have got default number 116. Thus a line like +'alias char-major-116 snd' must be added to /etc/modules.conf. If you have +compiled the ALSA driver with the OSS/Free emulation code, then you +will need to add lines as explained below: + +The ALSA driver uses soundcore multiplexer for 2.2+ kernels and OSS compatible +devices. You should add line like 'alias char-major-14 soundcore'. + +Example with OSS/Free emulation turned on: + +----- /etc/modules.conf + +# ALSA portion +alias char-major-116 snd +# OSS/Free portion +alias char-major-14 soundcore + +----- /etc/modules.conf + +After the main multiplexer is loaded, its code requests top-level soundcard +module. String 'snd-card-%i' is requested for native devices where %i is +soundcard number from zero to seven. String 'sound-slot-%i' is requested +for native devices where %i is slot number (for ALSA owner this means soundcard +number). + +----- /etc/modules.conf + +# ALSA portion +alias snd-card-0 snd-interwave +alias snd-card-1 snd-ens1371 +# OSS/Free portion +alias sound-slot-0 snd-card-0 +alias sound-slot-1 snd-card-1 + +----- /etc/modules.conf + +We are finished at this point with the configuration for ALSA native devices, +but you may also need autoloading for ALSA's add-on OSS/Free emulation +modules. At this time only one module does not depend on any others, thus +must be loaded separately - snd-pcm-oss. String 'sound-service-%i-%i' +is requested for OSS/Free service where first %i means slot number +(e.g. card number) and second %i means service number. + +----- /etc/modules.conf + +# OSS/Free portion - card #1 +alias sound-service-0-0 snd-mixer-oss +alias sound-service-0-1 snd-seq-oss +alias sound-service-0-3 snd-pcm-oss +alias sound-service-0-8 snd-seq-oss +alias sound-service-0-12 snd-pcm-oss +# OSS/Free portion - card #2 +alias sound-service-1-0 snd-mixer-oss +alias sound-service-1-3 snd-pcm-oss +alias sound-service-1-12 snd-pcm-oss + +----- /etc/modules.conf + +A complete example for Gravis UltraSound PnP soundcard: + +----- /etc/modules.conf + +# ISA PnP support (don't use IRQs 9,10,11,12,13) +options isapnp isapnp_reserve_irq=9,10,11,12,13 + +# ALSA native device support +alias char-major-116 snd +options snd major=116 cards_limit=1 +alias snd-card-0 snd-interwave +options snd-interwave index=0 id="GusPnP" + +# OSS/Free setup +alias char-major-14 soundcore +alias sound-slot-0 snd-card-0 +alias sound-service-0-0 snd-mixer-oss +alias sound-service-0-1 snd-seq-oss +alias sound-service-0-3 snd-pcm-oss +alias sound-service-0-8 snd-seq-oss +alias sound-service-0-12 snd-pcm-oss + +----- + +A complete example if you want to use more soundcards in one machine +(the configuration below is for Sound Blaster 16 and Gravis UltraSound Classic): + +----- /etc/modules.conf + +# ISA PnP support (don't use IRQs 9,10,11,12,13) +# it's only an example to reserve some IRQs for another hardware +options isapnp isapnp_reserve_irq=9,10,11,12,13 + +# ALSA native device support +alias char-major-116 snd +options snd major=116 cards_limit=2 +alias snd-card-0 snd-gusclassic +alias snd-card-1 snd-sb16 +options snd-gusclassic index=0 id="Gus" \ + port=0x220 irq=5 dma1=6 dma2=7 +options snd-sb16 index=1 id="SB16" + +# OSS/Free setup +alias char-major-14 soundcore +alias sound-slot-0 snd-card-0 +alias sound-service-0-0 snd-mixer-oss +alias sound-service-0-1 snd-seq-oss +alias sound-service-0-3 snd-pcm-oss +alias sound-service-0-8 snd-seq-oss +alias sound-service-0-12 snd-pcm-oss +alias sound-slot-1 snd-card-1 +alias sound-service-1-0 snd-mixer-oss +alias sound-service-1-3 snd-pcm-oss +alias sound-service-1-12 snd-pcm-oss + +----- + +A complete example, two Gravis UltraSound Classic soundcards are installed +in the system: + +----- /etc/modules.conf + +# ALSA native device support +alias char-major-116 snd +options snd major=116 cards_limit=2 +alias snd-card-0 snd-gusclassic +alias snd-card-1 snd-gusclassic +options snd-gusclassic index=0,1 id="Gus1","Gus2" \ + port=0x220,0x240 irq=5,7 dma1=1,5 dma2=3,6 + +# OSS/Free setup +alias char-major-14 soundcore +alias sound-slot-0 snd-card-0 +alias sound-service-0-0 snd-mixer-oss +alias sound-service-0-1 snd-seq-oss +alias sound-service-0-3 snd-pcm-oss +alias sound-service-0-8 snd-seq-oss +alias sound-service-0-12 snd-pcm-oss +alias sound-slot-1 snd-card-1 +alias sound-service-1-0 snd-mixer-oss +alias sound-service-1-3 snd-pcm-oss +alias sound-service-1-12 snd-pcm-oss + +----- + +If you want to autoclean your modules, you should put below line to your +/etc/crontab: + +*/10 * * * * root /sbin/modprobe -rs snd-card-0 snd-card-1; /sbin/rmmod -as + +You may also want to extend the soundcard list to follow your requirements. + + +ALSA PCM devices to OSS devices mapping +======================================= + +/dev/snd/pcmC0D0 -> /dev/audio0 (/dev/audio) -> minor 4 +/dev/snd/pcmC0D0 -> /dev/dsp0 (/dev/dsp) -> minor 3 +/dev/snd/pcmC0D1 -> /dev/adsp0 (/dev/adsp) -> minor 12 +/dev/snd/pcmC1D0 -> /dev/audio1 -> minor 4+16 = 20 +/dev/snd/pcmC1D0 -> /dev/dsp1 -> minor 3+16 = 19 +/dev/snd/pcmC1D1 -> /dev/adsp1 -> minor 12+16 = 28 +/dev/snd/pcmC2D0 -> /dev/audio2 -> minor 4+32 = 36 +/dev/snd/pcmC2D0 -> /dev/dsp2 -> minor 3+32 = 39 +/dev/snd/pcmC2D1 -> /dev/adsp2 -> minor 12+32 = 44 + +The first number from /dev/snd/pcmC{X}D{Y} expression means soundcard number +and second means device number. +Please note that the device mapping above may be varied via the module +options of snd-pcm-oss module. + + +DEVFS support +============= + +The ALSA driver fully supports the devfs extension. +You should add lines below to your devfsd.conf file: + +LOOKUP snd MODLOAD ACTION snd +REGISTER ^sound/.* PERMISSIONS root.audio 660 +REGISTER ^snd/.* PERMISSIONS root.audio 660 + +Warning: These lines assume that you have the audio group in your system. + Otherwise replace audio word with another group name (root for + example). + + +Proc interfaces (/proc/asound) +============================== + +/proc/asound/card#/pcm#[cp]/oss +------------------------------- + String "erase" - erase all additional informations about OSS applications + String " []" + + - name of application with (higher priority) or without path + - number of fragments or zero if auto + - size of fragment in bytes or zero if auto + - optional parameters + - disable the application tries to open a pcm device for + this channel but does not want to use it. + (Cause a bug or mmap needs) + It's good for Quake etc... + - direct don't use plugins + - block force block mode (rvplayer) + - non-block force non-block mode + + Example: echo "x11amp 128 16384" > /proc/asound/card0/pcm0p/oss + echo "squake 0 0 disable" > /proc/asound/card0/pcm0c/oss + echo "rvplayer 0 0 block" > /proc/asound/card0/pcm0p/oss + + +Links +===== + + ALSA project homepage + http://www.alsa-project.org + diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/CMIPCI.txt linux/Documentation/ALSA/CMIPCI.txt --- linux-2.4.21-rc1.orig/Documentation/ALSA/CMIPCI.txt 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/CMIPCI.txt 2003-02-20 02:24:41.000000000 -0700 @@ -0,0 +1,243 @@ + Brief Notes on C-Media 8738/8338 Driver + ======================================= + + Takashi Iwai + + +Front/Rear Multi-channel Playback +--------------------------------- + +CM8x38 chip can use ADC as the second DAC so that two different stereo +channels can be used for front/rear playbacks. Since there are two +DACs, both streams are handled independently unlike the 4/6ch multi- +channel playbacks in the section below. + +As default, ALSA driver assigns the first PCM device (i.e. hw:0,0 for +card#0) for front and 4/6ch playbacks, while the second PCM device +(hw:0,1) is assigned to the second DAC for rear playback. + +There are slight difference between two DACs. + +- The first DAC supports U8 and S16LE formats, while the second DAC + supports only S16LE. +- The seconde DAC supports only two channel stereo. + +Please note that the CM8x38 DAC doesn't support continuous playback +rate but only fixed rates: 5512, 8000, 11025, 16000, 22050, 32000, +44100 and 48000 Hz. + +The rear output can be heard only when "Four Channel Mode" switch is +disabled. Otherwise no signal will be routed to the rear speakers. +As default it's turned on. + +*** WARNING *** +When "Four Channel Mode" switch is off, the output from rear speakers +will be FULL VOLUME regardless of Master and PCM volumes. +This might damage your audio equipment. Please disconnect speakers +before your turn off this switch. +*** WARNING *** + +[ Well.. I once got the output with correct volume (i.e. same with the + front one) and was so excited. It was even with "Four Channel" bit + on and "double DAC" mode. Actually I could hear separate 4 channels + from front and rear speakers! But.. after reboot, all was gone. + It's a very pity that I didn't save the register dump at that + time.. Maybe there is an unknown register to achieve this... ] + +If your card has an extra output jack for the rear output, the rear +playback should be routed there as default. If not, there is a +control switch in the driver "Line-In As Rear", which you can change +via alsamixer or somewhat else. When this switch is on, line-in jack +is used as rear output. + +There are two more controls regarding to the rear output. +The "Exchange DAC" switch is used to exchange front and rear playback +routes, i.e. the 2nd DAC is output from front output. + + +4/6 Multi-Channel Playback +-------------------------- + +The recent CM8738 chips support for the 4/6 multi-channel playback +function. This is useful especially for AC3 decoding. + +When the multi-channel is supported, the driver name has a suffix +"-MC" such like "CMI8738-MC6". You can check this name from +/proc/asound/cards. + +When the 4/6-ch output is enabled, the second DAC accepts up to 6 (or +4) channels. While the dual DAC supports two different rates or +formats, the 4/6-ch playback supports only the same condition for all +channels. Since the multi-channel playback mode uses both DACs, you +cannot operate with full-duplex. + +The 4.0 and 5.1 modes are defined as the pcm "surround40" and "surround51" +in alsa-lib. For example, you can play a WAV file with 6 channels like + + % aplay -Dsurround51 sixchannels.wav + +For programmin the 4/6 channel playback, you need to specify the PCM +channels as you like and set the format S16LE. For example, for playback +with 4 channels, + + snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); + // or mmap if you like + snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_channels(pcm, hw, 4); + +and use the interleaved 4 channel data. + +There are some control switchs affecting to the speaker connections: + +"Line-In As Rear" - As mentioned above, the line-in jack is used + for the rear (3th and 4th channels) output. +"Line-In As Bass" - The line-in jack is used for the bass (5th + and 6th channels) output. +"Mic As Center/LFE" - The mic jack is used for the bass output. + If this switch is on, you cannot use a microphone as a capture + source, of course. + + +Digital I/O +----------- + +The CM8x38 provides the excellent SPDIF capability with very chip +price (yes, that's the reason I bought the card :) + +The SPDIF playback and capture are done via the third PCM device +(hw:0,2). Usually this is assigned to the PCM device "spdif". +The available rates are 44100 and 48000 Hz. +For playback with aplay, you can run like below: + + % aplay -Dhw:0,2 foo.wav + +or + + % aplay -Dspdif foo.wav + +So far, only S16LE format is supported. Still no 24bit. Sorry, not +enough info for this. + +The playback and capture over SPDIF use normal DAC and ADC, +respectively, so you cannot playback both analog and digital streams +simultaneously. + +To enable SPDIF output, you need to turn on "IEC958 Output Switch" +control via mixer or alsactl. Then you'll see the red light on from +the card so you know that's working obviously :) +The SPDIF input is always enabled, so you can hear SPDIF input data +from line-out with "IEC958 In Monitor" switch at any time (see +below). + +You can play via SPDIF even with the first device (hw:0,0), +but SPDIF is enabled only when the proper format (S16LE), sample rate +(441100 or 48000) and channels (2) are used. Otherwise it's turned +off. (Also don't forget to turn on "IEC958 Output Switch", too.) + + +Additionally there are relevant control switches: + +"IEC958 Mix Analog" - Mix analog PCM playback and FM-OPL/3 streams and + output through SPDIF. This switch appears only on old chip + models (CM8738 033 and 037). + Note: without this control you can output PCM to SPDIF. + This is "mixing" of streams, so e.g. it's not for AC3 output + (see the next section). + +"IEC958 In Select" - Select SPDIF input, the internal CD-in (false) + and the external input (true). + +"IEC958 Loop" - SPDIF input data is loop back into SPDIF + output (aka bypass) + +"IEC958 Copyright" - Set the copyright bit. + +"IEC958 5V" - Select 0.5V (coax) or 5V (optical) interface. + On some cards this doesn't work and you need to change the + configuration with hardware dip-switch. + +"IEC958 In Monitor" - SPDIF input is routed to DAC. + +"IEC958 In Phase Inverse" - Set SPDIF input format as inverse. + [FIXME: this doesn't work on all chips..] + +"IEC958 In Valid" - Set input validity flag detection. + +Note: When "PCM Playback Switch" is on, you'll hear the digital output +stream through analog line-out. + + +The AC3 (RAW DIGITAL) OUTPUT +---------------------------- + +The driver supports raw digital (typically AC3) i/o over SPDIF. This +can be toggled via IEC958 playback control, but usually you need to +access it via alsa-lib. See alsa-lib documents for more details. + +On the raw digital mode, the "PCM Playback Switch" is automatically +turned off so that non-audio data is heard from the analog line-out. +Similarly the following switches are off: "IEC958 Mix Analog" and +"IEC958 Loop". The switches are resumed after closing the SPDIF PCM +device automatically to the previous state. + +On the model 033, AC3 is implemented by the software conversion in +the driver. This prevents the mmap support. If you need mmap +support, pass the "soft_ac3=0" module option. This doesn't matter +on the newer models. + + +ANALOG MIXER INTERFACE +---------------------- + +The mixer interface on CM8x38 is similar to SB16. +There are Master, PCM, Synth, CD, Line, Mic and PC Speaker playback +volumes. Synth, CD, Line and Mic have playback and capture switches, +too, as well as SB16. + +In addition to the standard SB mixer, CM8x38 provides more functions. +- PCM playback switch +- PCM capture switch (to capture the data sent to DAC) +- Mic Boost switch +- Mic capture volume +- Aux playback volume/switch and capture switch +- 3D control switch + + +MIDI CONTROLLER +--------------- + +The MPU401-UART interface is enabled as default only for the first +(CMIPCI) card. You need to set module option "midi_port" properly +for the 2nd (CMIPCI) card. + +There is _no_ hardware wavetable function on this chip (except for +OPL3 synth below). +What's said as MIDI synth on Windows is a software synthesizer +emulation. On Linux use TiMidity or other softsynth program for +playing MIDI music. + + +FM OPL/3 Synth +-------------- + +The FM OPL/3 is also enabled as default only for the first card. +Set "fm_port" module option for more cards. + +The output quality of FM OPL/3 is, however, very weird. +I don't know why.. + + +Joystick and Modem +------------------ + +The joystick and modem should be available by enabling the control +switch "Joystick" and "Modem" respectively. But I myself have never +tested them yet. + + +Debugging Information +--------------------- + +The registers are shown in /proc/asound/cardX/cmipci. If you have any +problem (especially unexpected behavior of mixer), please attach the +output of this proc file together with the bug report. diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/ControlNames.txt linux/Documentation/ALSA/ControlNames.txt --- linux-2.4.21-rc1.orig/Documentation/ALSA/ControlNames.txt 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/ControlNames.txt 2003-02-28 09:50:19.000000000 -0700 @@ -0,0 +1,84 @@ +This document describes standard names of mixer controls. + +Syntax: SOURCE [DIRECTION] FUNCTION + +DIRECTION: + (both directions) + Playback + Capture + Bypass Playback + Bypass Capture + +FUNCTION: + Switch (on/off switch) + Volume + Route (route control, hardware specific) + +SOURCE: + Master + Master Mono + Hardware Master + Headphone + PC Speaker + Phone + Phone Input + Phone Output + Synth + FM + Mic + Line + CD + Video + Zoom Video + Aux + PCM + PCM Front + PCM Rear + PCM Pan + Loopback + Analog Loopback (D/A -> A/D loopback) + Digital Loopback (playback -> capture loopback - without analog path) + Mono + Mono Output + Multi + ADC + Wave + Music + I2S + IEC958 + +Exceptions: + [Digital] Capture Source + [Digital] Capture Switch (aka input gain switch) + [Digital] Capture Volume (aka input gain volume) + [Digital] Playback Switch (aka output gain switch) + [Digital] Playback Volume (aka output gain volume) + Tone Control - Switch + Tone Control - Bass + Tone Control - Treble + 3D Control - Switch + 3D Control - Center + 3D Control - Depth + 3D Control - Wide + 3D Control - Space + 3D Control - Level + Mic Boost [(?dB)] + +PCM interface: + + Sample Clock Source { "Word", "Internal", "AutoSync" } + Clock Sync Status { "Lock", "Sync", "No Lock" } + External Rate /* external capture rate */ + Capture Rate /* capture rate taken from external source */ + +IEC958 (S/PDIF) interface: + + IEC958 [...] [Playback|Capture] Switch /* turn on/off the IEC958 interface */ + IEC958 [...] [Playback|Capture] Volume /* digital volume control */ + IEC958 [...] [Playback|Capture] Default /* default or global value - read/write */ + IEC958 [...] [Playback|Capture] Mask /* consumer and professional mask */ + IEC958 [...] [Playback|Capture] Con Mask /* consumer mask */ + IEC958 [...] [Playback|Capture] Pro Mask /* professional mask */ + IEC958 [...] [Playback|Capture] PCM Stream /* the settings assigned to a PCM stream */ + IEC958 Q-subcode [Playback|Capture] Default /* Q-subcode bits */ + IEC958 Preamble [Playback|Capture] Default /* burst preamble words (4*16bits) */ diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/DocBook/alsa-driver-api.tmpl linux/Documentation/ALSA/DocBook/alsa-driver-api.tmpl --- linux-2.4.21-rc1.orig/Documentation/ALSA/DocBook/alsa-driver-api.tmpl 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/DocBook/alsa-driver-api.tmpl 2003-02-28 09:03:58.000000000 -0700 @@ -0,0 +1,101 @@ + + + + + + + + + + The ALSA Driver API + + + + This document is free; 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 document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + + + Management of Cards and Devices + Card Managment +!Esound/core/init.c + + Device Components +!Esound/core/device.c + + KMOD and Device File Entries +!Esound/core/sound.c + + Memory Management Helpers +!Esound/core/memory.c +!Iinclude/sound/sndmagic.h +!Esound/core/memalloc.c +!Esound/core/sgbuf.c + + + PCM API + PCM Core +!Esound/core/pcm.c +!Esound/core/pcm_lib.c +!Esound/core/pcm_native.c + + PCM Format Helpers +!Esound/core/pcm_misc.c + + PCM Memory Managment +!Esound/core/pcm_memory.c + + + Control/Mixer API + General Control Interface +!Esound/core/control.c + + AC97 Codec API +!Esound/pci/ac97/ac97_codec.c + + + MIDI API + Raw MIDI API +!Esound/core/rawmidi.c + + MPU401-UART API +!Esound/drivers/mpu401/mpu401_uart.c + + + Proc Info API + Proc Info Interface +!Esound/core/info.c + + + Miscellaneous Functions + Hardware-Dependent Devices API +!Esound/core/hwdep.c + + ISA DMA Helpers +!Esound/core/isadma.c + + Other Helper Macros +!Iinclude/sound/core.h + + + + diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/DocBook/writing-an-alsa-driver.tmpl linux/Documentation/ALSA/DocBook/writing-an-alsa-driver.tmpl --- linux-2.4.21-rc1.orig/Documentation/ALSA/DocBook/writing-an-alsa-driver.tmpl 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/DocBook/writing-an-alsa-driver.tmpl 2003-03-19 09:14:29.000000000 -0700 @@ -0,0 +1,5232 @@ + + + + + + + + + + Writing an ALSA Driver + + Takashi + Iwai + +
+ tiwai@suse.de +
+
+
+ + Dec. 27, 2002 + 0.2 (reborn at Christmas) + + + + This document describes how to write an ALSA (Advanced Linux + Sound Architecture) driver. + + + + + + Copyright (c) 2002 Takashi Iwai tiwai@suse.de + + + + This document is free; 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 document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + +
+ + + + + + Preface + + This document describes how to write an + + ALSA (Advanced Linux Sound Architecture) + driver. The document focuses mainly on the PCI soundcard. + In the case of other device types, the API might + be different, too. However, at least the ALSA kernel API is + consistent, and therefore it would be still a bit help for + writing them. + + + + The target of this document is ones who already have enough + skill of C language and have the basic knowledge of linux + kernel programming. This document doesn't explain the general + topics of linux kernel codes and doesn't cover the detail of + implementation of each low-level driver. It describes only how is + the standard way to write a PCI sound driver on ALSA. + + + + If you are already familiar with the older ALSA ver.0.5.x, you + can check the drivers such as es1938.c or + maestro3.c which have also almost the same + code-base in the ALSA 0.5.x tree, so you can compare the differences. + + + + This document is still a draft version. Any feedbacks and + corrections, please!! + + + + + + + + + File Tree Structure + +
+ General + + The ALSA drivers are provided in the two ways. + + + + One is the the trees provided as a tarball or via cvs from the + ALSA's ftp site, and another is the 2.5 (or later) Linux kernel + tree. To synchronize both, the ALSA driver tree is split to + two different trees: alsa-kernel and alsa-driver. The former + contains purely the source codes for the Linux 2.5 (or later) + tree. This tree is designed only for compilation on 2.5 or + later environment. The latter, alsa-driver, contains many subtle + files for compiling the ALSA driver on the outside of Linux + kernel like configure script, the wrapper functions for older, + 2.2 and 2.4 kernels, to adapt the latest kernel API, + and additional drivers which are still in development or in + tests. The drivers in alsa-driver tree will be moved to + alsa-kernel (eventually 2.5 kernel tree) once when they are + finished and confirmed to work fine. + + + + The file tree structure of ALSA driver is depicted below. Both + alsa-kernel and alsa-driver have almost the same file + structure, except for core directory. It's + named as acore in alsa-driver tree. + + + ALSA File Tree Structure + + sound + /core + /oss + /seq + /oss + /instr + /ioctl32 + /include + /drivers + /mpu401 + /opl3 + /i2c + /l3 + /synth + /emux + /pci + /(cards) + /isa + /(cards) + /arm + /ppc + /sparc + /usb + /pcmcia /(cards) + /oss + + + +
+ +
+ core directory + + This directory contains the middle layer, that is, the heart + of ALSA drivers. In this directory, the native ALSA modules are + stored. The sub-directories contain different modules and are + dependent upon the kernel config. + + +
+ core/oss + + + The codes for PCM and mixer OSS emulation modules are stored + in this directory. The rawmidi OSS emulation is included in + the ALSA rawmidi code since it's quite small. The sequencer + code is stored in core/seq/oss directory (see + + below). + +
+ +
+ core/ioctl32 + + + This directory contains the 32bit-ioctl wrappers for 64bit + architectures such like x86-64, ppc64 and sparc64. For 32bit + and alpha architectures, these are not compiled. + +
+ +
+ core/seq + + This and its sub-directories are for the ALSA + sequencer. This directory contains the sequencer core and + primary sequencer modules such like snd-seq-midi, + snd-seq-virmidi, etc. They are compiled only when + CONFIG_SND_SEQUENCER is set in the kernel + config. + +
+ +
+ core/seq/oss + + This contains the OSS sequencer emulation codes. + +
+ +
+ core/seq/instr + + This directory contains the modules for the sequencer + instrument layer. + +
+
+ +
+ include directory + + This is the place for the public header files of ALSA drivers, + which are to be exported to the user-space, or included by + several files at different directories. Basically, the private + header files should not be placed in this directory, but you may + still find files there, due to historical reason :) + +
+ +
+ drivers directory + + This directory contains the non-architecture-specific + codes. For example, the dummy pcm driver and the serial MIDI + driver are found in this directory. In the sub-directories, + there are the codes for components which are independent from + bus and cpu architectures. + + +
+ drivers/mpu401 + + The MPU401 and MPU401-UART modules are stored here. + +
+ +
+ drivers/opl3 + + The OPL3 FM-synth stuff is found here. + +
+
+ +
+ i2c directory + + This contains the ALSA i2c components. + + + + Although there is a standard i2c layer on Linux, ALSA uses its + own i2c codes for some cards, because the soundcard needs only a + simple operation and the standard API is too complicated for + such a purpose. + + +
+ i2c/l3 + + This is a sub-directory for ARM L3 i2c. + +
+
+ +
+ synth directory + + This contains the synth middle-level modules. + + + + So far, there is only Emu8000/Emu10k1 synth driver under + synth/emux sub-directory. + +
+ +
+ pci directory + + This and its sub-directories hold the top-level card modules + for PCI soundcards. + + + + The drivers compiled from a single file is stored directly on + pci directory, while the drivers with several source files are + stored on its own sub-directory (e.g. emu10k1, ice1712). + +
+ +
+ isa directory + + This and its sub-directories hold the top-level card modules + for ISA soundcards. + +
+ +
+ arm, ppc, and sparc directories + + These are for the top-level card modules which are + architecture specific. + +
+ +
+ usb directory + + This contains the USB-audio driver. On the latest version, the + USB MIDI driver is integrated together with usb-audio driver. + +
+ +
+ pcmcia directory + + The PCMCIA, especially PCCard drivers will go here. CardBus + drivers will be on pci directory, because its API is identical + with the standard PCI cards. + + + + At this moment, only VX-pocket driver exists. + +
+ +
+ oss directory + + The OSS/Lite source files are stored here on Linux 2.5 (or + later) tree. (In the ALSA driver tarball, it's empty, of course :) + +
+
+ + + + + + + Basic Flow for PCI Drivers + +
+ Outline + + The minimum flow of PCI soundcard is like the following: + + + define the PCI ID table (see the section + PCI Entries + ). + create probe() callback. + create remove() callback. + create pci_driver table which contains the three pointers above. + create init() function just calling pci_module_init() to register the pci_driver table defined above. + create exit() function to call pci_unregister_driver() function. + + +
+ +
+ Full Code Example + + The code example is shown below. Some parts are kept + unimplemented at this moment but will be filled in the + succeeding sections. The numbers in comment lines of + snd_mychip_probe() function are the + markers. + + + Basic Flow for PCI Drivers Example + + + #include + #include + #include + #include + #define SNDRV_GET_ID + #include + + // module parameters (see "Module Parameters") + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; + static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + + // definition of the chip-specific record + typedef struct snd_mychip mychip_t; + struct snd_mychip { + snd_card_t *card; + // rest of implementation will be in the section + // "PCI Resource Managements" + }; + + // this should be go into + // (see "Management of Cards and Components") + #define mychip_t_magic 0xa15a4501 + + // chip-specific destructor + // (see "PCI Resource Managements") + static int snd_mychip_free(mychip_t *chip) + { + // will be implemented later... + } + + // component-destructor + // (see "Management of Cards and Components") + static int snd_mychip_dev_free(snd_device_t *device) + { + mychip_t *chip = snd_magic_cast(mychip_t, + device->device_data, return -ENXIO); + return snd_mychip_free(chip); + } + + // chip-specific constructor + // (see "Management of Cards and Components") + static int __devinit snd_mychip_create(snd_card_t *card, + struct pci_device *pci, + mychip_t **rchip) + { + mychip_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + // check PCI availability here + // (see "PCI Resource Managements") + + // allocate a chip-specific data with magic-alloc + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + + // rest of initialization here; will be implemented + // later, see "PCI Resource Managements" + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_mychip_free(chip); + return err; + } + *rchip = chip; + return 0; + } + + // constructor -- see "Constructor" sub-section + static int __devinit snd_mychip_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + static int dev; + snd_card_t *card; + mychip_t *chip; + int err; + + // (1) + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + // (2) + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + // (3) + if ((err = snd_mychip_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + // (4) + // implemented later + + // (5) + strcpy(card->driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); + + // (6) + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + // (7) + pci_set_drvdata(pci, chip); + dev++; + return 0; + } + + // destructor -- see "Destructor" sub-section + static void __devexit snd_mychip_remove(struct pci_dev *pci) + { + mychip_t *chip = snd_magic_cast(mychip_t, + pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); + } +]]> + + + +
+ +
+ Constructor + + The real constructor of PCI drivers is probe callback. The + probe callback and other component-constructors which are called + from probe callback should be defined with + __devinit prefix. You + cannot use __init prefix for them, + because any PCI device could be a hotplug device. + + + + In the probe callback, the following scheme is often used. + + +
+ 1) Check and increment the device index. + + + += SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } +]]> + + + + where enable[dev] is the module option. + + + + At each time probe callback is called, check the + availability of the device. If not available, simply increment + the device index and returns. dev will be incremented also + later (step + 7). + +
+ +
+ 2) Create a card instance + + + + + + + + + + The detail will be explained in the section + + Management of Cards and Components. + +
+ +
+ 3) Create a main component + + In this part, the PCI resources are allocated. + + + + + + + + The detail will be explained in the section PCI Resource + Managements. + +
+ +
+ 4) Create other components, such as mixer, MIDI, etc. + + Here you define the basic components such as + PCM, + mixer (e.g. AC97), + MIDI (e.g. MPU-401), + and other interfaces. + Also, if you want a proc + file, define it here, too. + +
+ +
+ 5) Set the driver ID and name strings. + + + +driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); +]]> + + + + The driver field holds the minimal ID string of the + chip. This is referred by alsa-lib's configurator, so keep it + simple but unique. + Even the same driver can have different driver IDs to + distinguish the functionality of each chip type. + + + + The shortname field is a string shown as more verbose + name. The longname field contains the information which is + shown in /proc/asound/cards. + +
+ +
+ 6) Register the card instance. + + + + + + + + + + Will be explained in the section Management + of Cards and Components, too. + +
+ +
+ 7) Set the PCI driver data and return zero. + + + + + + + + In the above, the chip record is stored. This pointer is + referred in the remove callback and power-management + callbacks, too. + If the card doesn't support the suspend/resume, you can store + the card pointer instead of the chip pointer, so that + snd_card_free can be called directly + without cast in the remove callback. But anyway, be sure + which pointer is used. + +
+
+ +
+ Destructor + + The destructor, remove callback, simply releases the card + instance. Then the ALSA middle layer will release all the + attached components automatically. + + + + It would be typically like the following: + + + +card); + pci_set_drvdata(pci, NULL); + } +]]> + + + + The above code assumes that the chip is allocated + with snd_magic stuff and + has the field to hold the card pointer (see the next + section). + +
+ +
+ Header Files + + For the above example, at least the following include files + are necessary. + + + + + #include + #include + #include + #include + #define SNDRV_GET_ID + #include +]]> + + + + where the last twos are necessary only when module options are + defined in the source file. If the codes are split to several + files, the file without module options don't need them. + + + + In addition to them, you'll need + <linux/interrupt.h> for the interrupt + handling, and <asm/io.h> for the i/o + access. If you use mdelay() or + udelay() functions, you'll need to include + <linux/delay.h>, too. + + + + The ALSA interfaces like PCM or control API are define in other + header files as <sound/xxx.h>. + They have to be included after + <sound/core.h>. + + +
+
+ + + + + + + Management of Cards and Components + +
+ Card Instance + + For each soundcard, a card record must be allocated. + + + + A card record is the headquarters of the soundcard. It manages + the list of whole devices (components) on the soundcard, such as + PCM, mixers, MIDI, synthesizer, and so on. Also, the card + record holds the ID and the name strings of the card, manages + the root of proc files, and controls the power-management states + and hotplug disconnections. The component list on the card + record is used to manage the proper releases of resources at + destruction. + + + + As mentioned above, to create a card instance, call + snd_card_new(). + + + + + + + + + + The function takes four arguments, the card-index number, the + id string, the module pointer (usually + THIS_MODULE), + and the size of extra-data space. The last argument is used to + allocate card->private_data for the + chip-specific data. Note that this data + is allocated by + snd_card_new(). + +
+ +
+ Components + + After the card is created, you can attach the components + (devices) to the card instance. On ALSA driver, a component is + represented as a snd_device_t object. + A component can be a PCM instance, a control interface, a raw + MIDI interface, etc. Each of such instances has one component + entry. + + + + A component can be created via + snd_device_new() function. + + + + + + + + + + This takes the card pointer, the device-level + (SNDRV_DEV_XXX), the data pointer, and the + callback pointers (&ops). The + device-level defines the type of components and the order of + registration and de-registration. For most of components, the + device-level is already defined. For a user-defined component, + you can use SNDRV_DEV_LOWLEVEL. + + + + This function itself doesn't allocate the data space. The data + must be allocated manually beforehand, and its pointer is passed + as the argument. This pointer is used as the identifier + (chip in the above example) for the + instance. + + + + Each ALSA pre-defined component such as ac97 or pcm calls + snd_device_new() inside its + constructor. The destructor for each component is defined in the + callback pointers. Hence, you don't need to take care of + calling a destructor for such a component. + + + + If you would like to create your own component, you need to + set the destructor function to dev_free callback in + ops, so that it can be released + automatically via snd_card_free(). The + example will be shown later as an implementation of a + chip-specific data. + +
+ +
+ Chip-Specific Data + + The chip-specific information, e.g. the i/o port address, its + resource pointer, or the irq number, is stored in the + chip-specific record. + Usually, the chip-specific record is typedef'ed as + xxx_t like the following: + + + + + + + + + + You might have objections against such a typedef, but this + typedef is necessary if you use a magic-cast + (explained later). + + + + In general, there are two ways to allocate the chip record. + + +
+ 1. Allocating via <function>snd_card_new()</function>. + + As mentioned above, you can pass the extra-data-length to the 4th argument of snd_card_new(), i.e. + + + + + + + + whether mychip_t is the type of the chip record. + + + + In return, the allocated record can be accessed as + + + +private_data; +]]> + + + + With this method, you don't have to allocate twice. But you + cannot use magic-cast for this record pointer, + instead. + +
+ +
+ 2. Allocating an extra device. + + + After allocating a card instance via + snd_card_new() (with + NULL on the 4th arg), call + snd_magic_kcalloc(). + + + + + + + + Once when the record is allocated via snd_magic stuff, you + can use magic-cast for the void pointer. + + + + The chip record should have the field to hold the card + pointer at least, + + + + + + + + + + Then, set the card pointer in the returned chip instance. + + + +card = card; +]]> + + + + + + Also, you need to define a magic-value for mychip_t. + + + + + + + (the detail will be described in the + + next subsection). + + + + Next, initialize the fields, and register this chip + record as a low-level device with a specified + ops, + + + + + + + + snd_mychip_dev_free() is the + device-destructor function, which will call the real + destructor. + + + + + +device_data, + return -ENXIO); + return snd_mychip_free(chip); + } +]]> + + + + where snd_mychip_free() is the real destructor. + +
+ +
+ Not a magic but a logic + + Now, you might have a question: What is the advantage of the + second method? Obviously, it looks far more complicated. + + As I wrote many times, the second method allows a + magic-cast for mychip_t. If you + have a void pointer (such as + pcm->private_data), the pointer type + is unknown at the compile time, and you cannot know even if a + wrong pointer type is passed. The compiler would accept + it. The magic-cast checks the pointer type at the runtime (and + whether it's a null pointer, too). Hence, the cast will be + much safer and good for debugging. + + + + As you have already seen, allocation with a magic-header can + be done via snd_magic_kmalloc() or + snd_magic_kcalloc(). + + + + + + + + The difference of these two functions is whether the area is + zero-cleared (kcalloc) or not + (kmalloc). + + + + The first argument of the allocator is the type of the + record. The magic-constant has to be defined for this type + beforehand. In this case, we'll need to define + mychip_t_magic, for example, as already + seen, + + + + + + + + The value is arbitrary but should be unique. + This is usually defined in + <include/sndmagic.h> or + <include/amagic.h> for alsa-driver tree, + but you may define it locally in the code at the early + development stage, since changing + sndmagic.h will lead to the recompilation + of the whole driver codes. + + + + The second argument is the extra-data length. It is usually + zero. The third argument is the flags to be passed to kernel + memory allocator, GFP_XXX. Normally, + GFP_KERNEL is passed. + + + + For casting a pointer, use + snd_magic_cast() macro: + + + + + + + + where source_pointer is the pointer to + be casted (e.g. pcm->private_data), and + action is the action to do if the cast + fails (e.g. return -EINVAL). + + + + For releasing the magic-allocated data, you need to call + snd_magic_kfree() function instead of + kfree(). + + + + + + + + + + If you call kfree() for the + magic-allocated value, it will lead to memory leaks. + When the ALSA drivers are compiled with + CONFIG_SND_DEBUG_MEMORY kernel config (or + configured with ), the + non-matching free will be checked and you'll see warning + messages. + + + + If you are 100% sure that your code is bug-free, you can + compile the driver without + CONFIG_SND_DEBUG_MEMORY kernel config, + so that the magic-allocator and the magic-cast will be + replaced to the normal kmalloc and cast. + +
+
+ +
+ Registration and Release + + After all components are assigned, register the card instance + by calling snd_card_register(). The access + to the device files are enabled at this point. That is, before + snd_card_register() is called, the + components are safely inaccessible from external side. If this + call fails, exit the probe function after releasing the card via + snd_card_free(). + + + + For releasing the card instance, you can call simply + snd_card_free(). As already mentioned, all + components are released automatically by this call. + + + + As further notes, the destructors (both + snd_mychip_dev_free and + snd_mychip_free) cannot be defined with + __devexit prefix, because they may be + called from the constructor, too, at the false path. + + + + For a device which allows hotplugging, you can use + snd_card_free_in_thread. This one will + postpone the destruction and wait in a kernel-thread until all + devices are closed. + + +
+ +
+ + + + + + + PCI Resource Managements + +
+ Full Code Example + + In this section, we'll finish the chip-specific constructor, + destructor and PCI entries. The example code is shown first, + below. + + + PCI Resource Managements Example + +res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + // release the irq + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + // release the data + snd_magic_kfree(chip); + return 0; + } + + // chip-specific constructor + static int __devinit snd_mychip_create(snd_card_t *card, + struct pci_dev *pci, + mychip_t **rchip) + { + mychip_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + // check PCI availability (28bit DMA) + if ((err = pci_enable_device(pci)) < 0) + return err; + if (!pci_dma_supported(pci, 0x0fffffff)) { + printk(KERN_ERR "error to set 28bit mask DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + // initialize the stuff + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + // (1) PCI resource allocation + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 8, + "My Chip")) == NULL) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot allocate the port\n"); + return -EBUSY; + } + if (request_irq(pci->irq, snd_mychip_interrupt, + SA_INTERRUPT|SA_SHIRQ, "My Chip", + (void *)chip)) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + + // (2) initialization of the chip hardware + // (not implemented in this document) + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_mychip_free(chip); + return err; + } + *rchip = chip; + return 0; + } + + // PCI IDs + static struct pci_device_id snd_mychip_ids[] __devinitdata = { + { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + .... + { 0, } + }; + MODULE_DEVICE_TABLE(pci, snd_mychip_ids); + + // pci_driver definition + static struct pci_driver driver = { + .name = "My Own Chip", + .id_table = snd_mychip_ids, + .probe = snd_mychip_probe, + .remove = __devexit_p(snd_mychip_remove), + }; + + // initialization of the module + static int __init alsa_card_mychip_init(void) + { + int err; + + if ((err = pci_module_init(&driver)) < 0) { + #ifdef MODULE + printk(KERN_ERR "My chip soundcard not found " + "or device busy\n"); + #endif + return err; + } + return 0; + } + + // clean up the module + static void __exit alsa_card_mychip_exit(void) + { + pci_unregister_driver(&driver); + } + + module_init(alsa_card_mychip_init) + module_exit(alsa_card_mychip_exit) + + EXPORT_NO_SYMBOLS; /* for old kernels only */ +]]> + + + +
+ +
+ Some Hafta's + + The allocation of PCI resources is done in the + probe() function, and usually an extra + xxx_create() function is written for this + purpose. + + + + In the case of PCI devices, you have to call at first + pci_enable_device() function before + allocating resources. Also, you need to set the proper PCI DMA + mask to limit the accessed i/o range. In some cases, you might + need to call pci_set_master() function, + too. + + + + Suppose the 28bit mask, and the code to be added would be like: + + + + + + + +
+ +
+ Resource Allocation + + The allocation of ports and irqs are done via standard kernel + functions. Unlike ALSA ver.0.5.x., there are no helpers for + that. And these resources must be released in the destructor + function (see below). Also, on ALSA 0.9.x, you don't need + allocate (pseudo-)DMA for PCI like 0.5.x. + + + + Now assume that this PCI device has an I/O port with 8 bytes + and an interrupt. Then mychip_t will have the + following fields: + + + + + + + + + + For an i/o port (and also a memory region), you need to have + the resource pointer for the standard resource management. For + an irq, you have to keep only the irq number (integer). But you + need to initialize this number as -1 before actual allocation, + since irq 0 is valid. The port address and its resource pointer + can be initialized as null by + snd_magic_kcalloc() automatically, so you + don't have to take care of it. + + + + The allocation of an i/o port is done like this: + + + +port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 8, + "My Chip")) == NULL) { + printk(KERN_ERR "cannot allocate the port 0x%lx\n", + chip->port); + snd_mychip_free(chip); + return -EBUSY; + } +]]> + + + + + + It will reserve the i/o port region of 8 bytes of the given + PCI device. The returned value, chip->res_port, is allocated + via kmalloc() by + request_region(). The pointer must be + released via kfree(), but there is some + problem regarding this. This issue will be explained more below. + + + + The allocation of an interrupt source is done like this: + + + +irq, snd_mychip_interrupt, + SA_INTERRUPT|SA_SHIRQ, "My Chip", + (void *)chip)) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot grab irq %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; +]]> + + + + where snd_mychip_interrupt() is the + interrupt handler defined later. + Note that chip->irq should be defined + only when request_irq() succeeded. + + + + On the PCI bus, the interrupts can be shared. Thus, + SA_SHIRQ is given as the interrupt flag of + request_irq(). + + + + The last argument of request_irq() is the + data pointer passed to the interrupt handler. Usually, the + chip-specific record is used for that, but you can use what you + like, too. + + + + I won't define the detail of the interrupt handler at this + point, but at least its appearance can be explained now. The + interrupt handler looks usually like the following: + + + + + + + + Again the magic-cast is used here to get the correct pointer + from the second argument. + + + + Now let's write the corresponding destructor for the resources + above. The role of destructor is simple: disable the hardware + (if already activated) and release the resources. So far, we + have no hardware part, so the disabling is not written here. + + + + For releasing the resources, check-and-release + method is a safer way. For the i/o port, do like this: + + + +res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } +]]> + + + + + + As you can see, the i/o resource pointer is also to be freed + via kfree_nocheck() after + release_resource() is called. You + cannot use kfree() here, because on ALSA, + kfree() may be a wrapper to its own + allocator with the memory debugging. Since the resource pointer + is allocated externally outside the ALSA, it must be released + via the native + kfree(). + kfree_nocheck() is used for that; it calls + the native kfree() without wrapper. + + + + For releasing the interrupt, do like this: + + + +irq >= 0) + free_irq(chip->irq, (void *)chip); +]]> + + + + And finally, release the chip-specific record. + + + + + + + + + + The chip instance is freed via + snd_magic_kfree(). Please use this function + for the object allocated by + snd_magic_kmalloc(). If you free it with + kfree(), it won't work properly and will + result in the memory leak. Also, again, remember that you cannot + set __devexit prefix for this destructor. + + + + We didn't implement the hardware-disabling part in the above. + If you need to do this, please note that the destructor may be + called even before the initialization of the chip is completed. + It would be better to have a flag to skip the hardware-disabling + if the hardware was not initialized yet. + + + + When the chip-data is assigned to the card using + snd_device_new() with + SNDRV_DEV_LOWLELVEL , its destructor is + called at the last. that is, it is assured that all other + components like PCMs and controls have been already released. + You don't have to call stopping PCMs, etc. explicitly, but just + stop the hardware in the low-level. + + + + The management of a memory-mapped region is almost as same as + the management of an i/o port. You'll need three fields like + the following: + + + + + + + + and the allocation would be (assuming its size is 512 bytes): + + + +iobase_phys = pci_resource_start(pci, 0); + chip->iobase_virt = (unsigned long) + ioremap_nocache(chip->iobase_phys, 512); + if ((chip->res_port = request_mem_region(chip->iobase_phys, 512, + "My Chip")) == NULL) { + printk(KERN_ERR "cannot allocate the memory region\n"); + snd_mychip_free(chip); + return -EBUSY; + } +]]> + + + + and the corresponding destructor would be: + + + +iobase_virt) + iounmap((void *)chip->iobase_virt); + if (chip->res_iobase) { + release_resource(chip->res_iobase); + kfree_nocheck(chip->res_iobase); + } + .... + } +]]> + + + + +
+ +
+ PCI Entries + + So far, so good. Let's finish the rest of missing PCI + stuffs. At first, we need a + pci_device_id table for this + chipset. It's a table of PCI vendor/device ID number, and some + masks. + + + + For example, + + + + + + + + + + The first and second fields of + pci_device_id struct are the vendor and + device IDs. If you have nothing special to filter the matching + devices, you can use the rest of fields like above. The last + field of pci_device_id struct is a + private data for this entry. You can specify any value here, for + example, to tell the type of different operations per each + device IDs. Such an example is found in intel8x0 driver. + + + + The last entry of this list is the terminator. You must + specify this all-zero entry. + + + + Then, prepare the pci_driver record: + + + + + + + + + + The probe and + remove functions are what we already + defined in + the previous sections. The remove should + be defined with + __devexit_p() macro, so that it's not + defined for built-in (and non-hot-pluggable) case. The + name + field is the name string of this device. Note that you must not + use a slash / in this string. + + + + And at last, the module entries: + + + + + + + + + + Note that these module entries are tagged with + __init and + __exit prefixes, not + __devinit nor + __devexit. + + + + Oh, one thing was forgotten. If you have no exported symbols, + you need to declare it on 2.2 or 2.4 kernels (on 2.5 kernels + it's not necessary, though). + + + + + + + + That's all! + +
+
+ + + + + + + PCM Interface + +
+ General + + The PCM middle layer of ALSA is quite powerful and it is only + necessary for each driver to implement the low-level functions + to access its hardware. + + + + For accessing to the PCM layer, you need to include + <sound/pcm.h> above all. In addition, + <sound/pcm_params.h> might be needed + if you access to some functions related with hw_param. + + + + Each card device can have up to four pcm instances. A pcm + instance corresponds to a pcm device file. The limitation of + number of instances comes only from the available bit size of + the linux's device number. Once when 64bit device number is + used, we'll have more available pcm instances. + + + + A pcm instance consists of pcm playback and capture streams, + and each pcm stream consists of one or more pcm substreams. Some + soundcard supports the multiple-playback function. For example, + emu10k1 has a PCM playback of 32 stereo substreams. In this case, at + each open, a free substream is (usually) automatically chosen + and opened. Meanwhile, when only one substream exists and it was + already opened, the succeeding open will result in the blocking + or the error with EAGAIN according to the + file open mode. But you don't have to know the detail in your + driver. The PCM middle layer will take all such jobs. + +
+ +
+ Full Code Example + + The example code below does not include any hardware access + routines but shows only the skeleton, how to build up the PCM + interfaces. + + + PCM Example Code + + + .... + + #define chip_t mychip_t + .... + + /* hardware definition */ + static snd_pcm_hardware_t snd_mychip_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + }; + + /* open callback */ + static int snd_mychip_pcm_open(snd_pcm_substream_t *subs) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->hw = snd_mychip_playback_hw; + // more hardware-initialization will be done here + return 0; + } + + /* close callback */ + static int snd_mychip_pcm_close(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + // the hardware-specific codes will be here + return 0; + + } + + /* hw_params callback */ + static int snd_mychip_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) + { + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + } + + /* hw_free callback */ + static int snd_mychip_pcm_hw_free(snd_pcm_substream_t *substream) + { + return snd_pcm_lib_free_pages(substream); + } + + /* prepare callback */ + static int snd_mychip_pcm_prepare(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + // set up the hardware with the current configuration + // for example... + mychip_set_sample_format(chip, runtime->format); + mychip_set_sample_rate(chip, runtime->rate); + mychip_set_channels(chip, runtime->channels); + mychip_set_dma_setup(chip, runtime->dma_area, + chip->buffer_size, + chip->period_size); + return 0; + } + + /* trigger callback */ + static int snd_mychip_pcm_trigger(snd_pcm_substream_t *substream, + int cmd) + { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + // do something to start the PCM engine + break; + case SNDRV_PCM_TRIGGER_STOP: + // do something to stop the PCM engine + break; + default: + return -EINVAL; + } + } + + /* pointer callback */ + static snd_pcm_uframes_t + snd_mychip_pcm_pointer(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + unsigned int current_ptr; + + // get the current hardware pointer + current_ptr = mychip_get_hw_pointer(chip); + return current_ptr; + } + + /* operators */ + static snd_pcm_ops_t snd_mychip_playback_ops = { + .open = snd_mychip_playback_open, + .close = snd_mychip_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + }; + + /* + * definitions of capture are omitted here... + */ + + /* create a pcm device */ + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, + &pcm)) < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_mychip_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_mychip_capture_ops); + /* pre-allocation of buffers */ + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, + 64*1024, 64*1024); + return 0; + } +]]> + + + +
+ +
+ Constructor + + A pcm instance is allocated snd_pcm_new() + function. It would be better to create a constructor for pcm, + namely, + + + +card, "My Chip", 0, 1, 1, + &pcm)) < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + .... + return 0; + } +]]> + + + + + + The snd_pcm_new() function takes the four + arguments. The first argument is the card pointer to which this + pcm is assigned, and the second is the ID string. + + + + The third argument (index, 0 in the + above) is the index of this new pcm. It begins from zero. When + you will create more than one pcm instances, specify the + different numbers in this argument. For example, + index = 1 for the second PCM device. + + + + The fourth and fifth arguments are the number of substreams + for playback and capture, respectively. Here both 1 are given in + the above example. When no playback or no capture is available, + pass 0 to the corresponding argument. + + + + If a chip supports multiple playbacks or captures, you can + specify more numbers, but they must be handled properly in + open/close, etc. callbacks. When you need to know which + substream you are referring to, then it can be obtained from + snd_pcm_substream_t data passed to each callback + as follows: + + + +number; +]]> + + + + + + After the pcm is created, you need to set operators for each + pcm stream. + + + + + + + + + + The operators are defined typically like this: + + + + + + + + Each of callbacks is explained in the subsection + + Operators. + + + + After setting the operators, most likely you'd like to + pre-allocate the buffer. For the pre-allocation, simply call + the following: + + + +pci, pcm, + 64*1024, 64*1024); +]]> + + + + It will allocate up to 64kB buffer as default. The details of + buffer management will be described in the later section Buffer and Memory + Management. + + + + Additionally, you can set some extra information for this pcm + in pcm->info_flags. + The available values are defined as + SNDRV_PCM_INFO_XXX in + <sound/asound.h>, which is used for + the hardware definition (described later). When your soundchip + supports only half-duplex, specify like this: + + + +info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; +]]> + + + +
+ +
+ ... And the Destructor? + + The destructor for a pcm instance is not always + necessary. Since the pcm device will be released by the middle + layer code automatically, you don't have to call destructor + explicitly. + + + + The destructor would be necessary when you created some + special records internally and need to release them. In such a + case, set the destructor function to + pcm->private_free: + + + PCM Instance with a Destructor + +private_data, return); + // free your own data + kfree(chip->my_private_pcm_data); + // do what you like else... + } + + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + .... + // allocate your own data + chip->my_private_pcm_data = kmalloc(...); + // set the destructor + pcm->private_data = chip; + pcm->private_free = mychip_pcm_free; + .... + } +]]> + + + +
+ +
+ Operators + + OK, now let me explain the detail of each pcm callback + (ops). In general, every callback must + return 0 if successful, or a negative number with the error + number such as -EINVAL at any + error. + + + + The callback function takes at least the argument with + snd_pcm_substream_t pointer. For retrieving the + chip record from the given substream instance, you can use the + following macro. + + + + + + + + + + It's expanded with a magic-cast, so the cast-error is + automatically checked. You should define chip_t at + the beginning of the code, since this will be referred in many + places of pcm and control interfaces. + + +
+ open callback + + + + + + + + This is called when a pcm substream is opened. + + + + At least, here you have to initialize the runtime hardware + record. Typically, this is done by like this: + + + +runtime; + + runtime->hw = snd_mychip_playback_hw; + return 0; + } +]]> + + + + where snd_mychip_playback_hw is the + pre-defined hardware record. + + + + + + + + + + The similar struct exists on ALSA 0.5.x driver, so you can + guess the values if you already wrote a driver. + + + + The info field contains the type and + capabilities of this pcm. The bit flags are defined in + <sound/asound.h> as + SNDRV_PCM_INFO_XXX. Here, at least, you + have to specify whether the mmap is supported and which + interleaved format is supported. + When the mmap is supported, add + SNDRV_PCM_INFO_MMAP flag here. When the + hardware supports the interleaved or the non-interleaved + format, SNDRV_PCM_INFO_INTERLEAVED or + SNDRV_PCM_INFO_NONINTERLEAVED flag must + be set, respectively. If both are supported, you can set both, + too. + + + + In the above example, MMAP_VALID and + BLOCK_TRANSFER are specified for OSS mmap + mode. Usually both are set. Of course, + MMAP_VALID is set only if the mmap is + really supported. + + + + The other possible flags are + SNDRV_PCM_INFO_PAUSE and + SNDRV_PCM_INFO_RESUME. The + PAUSE bit means that the pcm supports the + pause operation, while the + RESUME bit means that the pcm supports + the suspend/resume operation. If these flags + are set, the trigger callback below + must handle the corresponding commands. + + + + formats field contains the bit-flags + of supported formats (SNDRV_PCM_FMTBIT_XXX). + If the hardware supports more than one format, give all or'ed + bits. In the example above, the signed 16bit little-endian + format is specified. + + + + rates field contains the bit-flags of + supported rates (SNDRV_PCM_RATE_XXX). + When the chip supports continuous rates, pass + CONTINUOUS bit additionally. + The pre-defined rate bits are only for typical rates. If your + chip supports unconventional rates, you need to add + KNOT bit and set up the + constraint manually (explained later). + + + + There have been many changes of terminology between + ALSA 0.5.x and 0.9.x. + On the ALSA 0.9.x world, period means what is + known as fragment in the OSS. It's the least + size of (a part of) the buffer to generate an interrupt. + + + + Now, taking the new terminology into account, the other fields + are self-explanatory (I hope). Please note that here, both + min/max buffer and period sizes are specified in bytes. + + + + Some drivers allocate the private instance for each pcm + substream. It can be stored in + substream->runtime->private_data. + Since it's a void pointer, you + should use magic-kmalloc and magic-cast for such an object. + + + +runtime->private_data = data; + .... + } +]]> + + + + + + The allocated object must be released in the close callback below. + +
+ +
+ close callback + + + + + + + + Obviously, this is called when a pcm substream is closed. + + + + Any private instance for a pcm substream allocated in the + open callback will be released here. + + + +runtime->private_data); + .... + } +]]> + + + +
+ +
+ ioctl callback + + This is used for any special action to pcm ioctls. But + usually you can pass a generic ioctl callback, + snd_pcm_lib_ioctl. + +
+ +
+ hw_params callback + + + + + + + + This and hw_free callbacks exist + only on ALSA 0.9.x. + + + + This is called when the hardware parameter + (hw_params) is set + up by the application, + that is, once when the buffer size, the period size, the + format, etc. are defined for the pcm substream. + + + + Many hardware set-up should be done in this callback, + including the allocation of buffers. + + + + Parameters to be initialized are retrieved by + params_xxx() macros. For allocating a + buffer, you can call a helper function, + + + + + + + + snd_pcm_lib_malloc_pages() is available + only when the DMA buffers have been pre-allocated. + See the section + Buffer Types for more details. + + + + Note that this and prepare callbacks + may be called multiple times per initialization. + For example, the OSS emulation may + call these callbacks at each change via its ioctl. + + + + Thus, you need to take care not to allocate the same buffers + many times, which will lead to memory leak! Calling the + helper function above many times is OK. It will release the + previous buffer automatically when it was already allocated. + + + + Another note is that this callback is non-atomic + (schedulable). This is important, because the + prepare callback + is atomic (non-schedulable). That is, mutex or any + schedule-related functions are available only in + hw_params callback. + Please see the subsection + + Atomicity for details. + +
+ +
+ hw_free callback + + + + + + + + + + This is called to release the resources allocated via + hw_params. For example, releasing the + buffer via + snd_pcm_lib_malloc_pages() is done by + calling the following: + + + + + + + + + + This callback may be called multiple times, too. + Keep track whether the resource was already released. + +
+ +
+ prepare callback + + + + + + + + + + This callback is called when the pcm is + prepared. You can set the format type, sample + rate, etc. here. The difference from + hw_params is that the + prepare callback will be called at each + time + snd_pcm_prepare() is called, i.e. when + recovered after underruns, etc. + + + + As mentioned above, this callback is atomic. + + + + In this and the following callbacks, you can refer to the + values via the runtime record, + substream->runtime. + For example, to get the current + rate, format or channels, access to + runtime->rate, + runtime->format or + runtime->channels, respectively. + The physical address of the allocated buffer is set to + runtime->dma_area. The buffer and period sizes are + in runtime->buffer_size and runtime->period_size, + respectively. + + + + Note that the period and the buffer sizes are stored in + frames. In the ALSA world, 1 frame = channels + * samples-size. For conversion between frames and bytes, you + can use the helper functions, + frames_to_bytes() and + bytes_to_frames(). + + + +period_size); +]]> + + + + + + Be careful that this callback will be called many times at + each set up, too. + +
+ +
+ trigger callback + + + + + + + + This is called when the pcm is started, stopped or paused. + + + + Which action is specified in the second argument, + SNDRV_PCM_TRIGGER_XXX in + <sound/pcm.h>. At least, + START and STOP + commands must be defined in this callback. + + + + + + + + + + When the pcm supports the pause operation (given in info + field of the hardware table), PAUSE_PUSE + and PAUSE_RELEASE commands must be + handled here, too. The former is the command to pause the pcm, + and the latter to restart the pcm again. + + + + When the pcm supports the suspend/resume operation, + SUSPEND and RESUME + commands must be handled, too. Obviously it does suspend and + resume of the pcm substream. Usually, the + SUSPEND is identical with + STOP command and the + RESUME is identical with + START command. + + + + This callback is also atomic. + +
+ +
+ pointer callback + + + + + + + + This callback is called when the PCM middle layer inquires + the current hardware position on the buffer. The position must + be returned in frames (which was in bytes on ALSA 0.5.x), + ranged from 0 to buffer_size - 1. + + + + This is called usually from the buffer-update routine in the + pcm middle layer, which is invoked when + snd_pcm_period_elapsed() is called in the + interrupt routine. Then the pcm middle layer updates the + position and calculates the available space, and wakes up the + sleeping poll threads, etc. + + + + This callback is also atomic. + +
+ +
+ copy and silence callbacks + + These callbacks are not mandatory, and can be omitted in + most cases. These callbacks are used when the hardware buffer + cannot be on the normal memory space. Some chips have their + own buffer on the hardware which is not mappable. In such a + case, you have to transfer the data manually from the memory + buffer to the hardware buffer. Or, if the buffer is + non-contiguous on both physical and virtual memory spaces, + these callbacks must be defined, too. + + + + If these two callbacks are defined, copy and set-silence + operations are done by them. The detailed will be described in + the later section Buffer and Memory + Management. + +
+ +
+ ack callback + + This callback is also not mandatory. This callback is called + when the appl_ptr is updated in read or write operations. + Some drivers like emu10k1-fx and cs46xx need to track the + current appl_ptr for the internal buffer, and this callback + is useful only for such a purpose. + +
+ +
+ page callback + + + This callback is also not mandatory. This callback is used + mainly for the non-contiguous buffer. The mmap calls this + callback to get the page address. Some examples will be + explained in the later section Buffer and Memory + Management, too. + +
+
+ +
+ Interrupt Handler + + The rest of pcm stuff is the PCM interrupt handler. The + role of PCM interrupt handler in the sound driver is to update + the buffer position and to tell the PCM middle layer when the + buffer position goes across the prescribed period size. To + inform this, call snd_pcm_period_elapsed() + function. + + + + There are several types of sound chips to generate the interrupts. + + +
+ Interrupts at the period (fragment) boundary + + This is the most frequently found type: the hardware + generates an interrupt at each period boundary. + In this case, you can call + snd_pcm_period_elapsed() at each + interrupt. + + + + snd_pcm_period_elapsed() takes the + substream pointer as its argument. Thus, you need to keep the + substream pointer accessible from the chip instance. For + example, define substream field in the chip record to hold the + current running substream pointer, and set the pointer value + at open callback (and reset at close callback). + + + + If you aquire a spinlock in the interrupt handler, and the + lock is used in other pcm callbacks, too, then you have to + release the lock before calling + snd_pcm_period_elapsed(), because + snd_pcm_period_elapsed() calls other pcm + callbacks inside. + + + + A typical coding would be like: + + + Interrupt Handler Case #1 + +lock); + .... + if (pcm_irq_invoked(chip)) { + // call updater, unlock before it + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(chip->substream); + spin_lock(&chip->lock); + } + .... + spin_unlock(&chip->lock); + } +]]> + + + +
+ +
+ High-frequent timer interrupts + + This is the case when the hardware doesn't generate interrupts + at the period boundary but do timer-interrupts at the fixed + timer rate (e.g. es1968 or ymfpci drivers). + In this case, you need to check the current hardware + position and accumulates the processed sample length at each + interrupt. When the accumulated size overcomes the period + size, call + snd_pcm_period_elapsed() and reset the + accumulator. + + + + A typical coding would be like the following. + + + Interrupt Handler Case #2 + +lock); + .... + if (pcm_irq_invoked(chip)) { + unsigned int last_ptr, size; + // get the current hardware pointer (in frames) + last_ptr = get_hw_ptr(chip); + // calculate the processed frames since the + // last update + if (last_ptr < chip->last_ptr) + size = runtime->buffer_size + last_ptr + - chip->last_ptr; + else + size = last_ptr - chip->last_ptr; + // remember the last updated point + chip->last_ptr = last_ptr; + // accumulate the size + chip->size += size; + // over the period boundary? + if (chip->size >= runtime->period_size) { + // reset the accumulator + chip->size %= runtime->period_size; + // call updater + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->lock); + } + } + .... + spin_unlock(&chip->lock); + } +]]> + + + +
+ +
+ On calling <function>snd_pcm_period_elapsed()</function> + + In both cases, even if more than one period are elapsed, you + don't have to call + snd_pcm_period_elapsed() many times. Call + only once. And the pcm layer will check the current hardware + pointer and update to the latest status. + +
+
+ +
+ Atomicity + + One of the most important (and thus difficult to debug) problem + on the kernel programming is the race condition. + On linux kernel, usually it's solved via spin-locks or + semaphores. In general, if the race condition may + happen in the interrupt handler, it's handled as atomic, and you + have to use spinlock for protecting the critical session. If it + never happens in the interrupt and it may take relatively long + time, you should use semaphore. + + + + As already seen, some pcm callbacks are atomic and some are + not. For example, hw_params callback is + non-atomic, while prepare callback is + atomic. This means, the latter is called already in a spinlock + held by the PCM middle layer. Please take this atomicity into + account when you use a spinlock or a semaphore in the callbacks. + + + + In the atomic callbacks, you cannot use functions which may call + schedule or go to + sleep. The semaphore and mutex do sleep, + and hence they cannot be used inside the atomic callbacks + (e.g. prepare callback). + For taking a certain delay in such a callback, please use + udelay() or mdelay(). + + + + This atomicity problem appears also in the initialization of the + hardware when the power-management is supported. The functions + for suspending and resuming the chip must be atomic, i.e. no + mutex nor sleep can be used in them. + + +
+
+ Constraints + + If your chip supports unconventional sample rates, or only the + limited samples, you need to set a constraint for the + condition. + + + + For example, in order to restrict the sample rates in the some + supported values, use + snd_pcm_hw_constraint_list(). + You need to call this function in the open callback. + + + Example of Hardware Constraints + +runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + if (err < 0) + return err; + .... + } +]]> + + + + + + There are many different constraints. You can even define your + own constraint rules. I won't explain the details here, rather I + would like to say, Luke, use the source. + +
+ +
+ + + + + + + Control Interface + +
+ General + + The control interface is used widely for many switches, + sliders, etc. which are accessed from the user-space. Its most + important use is the mixer interface. In other words, on ALSA + 0.9.x, all the mixer stuff is implemented on the control kernel + API (while there was an independent mixer kernel API on 0.5.x). + + + + ALSA has a well-defined AC97 control module. If your chip + supports only the AC97 and nothing else, you can skip this + section. + + + + The control API is defined in + <sound/control.h>. + Include this file if you add your own controls. + +
+ +
+ Definition of Controls + + For creating a new control, you need to define the three + callbacks: info, + get and + put. Then, define a + snd_kcontrol_new_t record, such as: + + + Definition of a Control + + + + + + + + Most likely the control is created via + snd_ctl_new1(), and in such a case, you can + add __devinitdata prefix to the + definition like above. + + + + The iface field specifies the type of + the control, + SNDRV_CTL_ELEM_IFACE_XXX. There are + MIXER, PCM, + CARD, etc. + + + + The name is the name identifier + string. On ALSA 0.9.x, the control name is very important, + because its role is classified from its name. There are + pre-defined standard control names. The details are described in + the subsection + + Control Names. + + + + The index field holds the index number + of this control. If there are several different controls with + the same name, they can be distinguished by the index + number. This is the case when + several codecs exist on the card. If the index is zero, you can + omit the definition above. + + + + The access field contains the access + type of this control. Give the combination of bit masks, + SNDRV_CTL_ELEM_ACCESS_XXX, there. + The detailed will be explained in the subsection + + Access Flags. + + + + The private_values field contains + an arbitrary long integer value for this record. When using + generic info, + get and + put callbacks, you can pass a value + through this field. If several small numbers are necessary, you can + combine them in bitwise. Or, it's possible to give a pointer + (casted to unsigned long) of some record to this field, too. + + + + The other three are + + callback functions. + +
+ +
+ Control Names + + There are some standards for defining the control names. A + control is usually defined from the three parts as + SOURCE DIRECTION FUNCTION. + + + + The first, SOURCE, specifies the source + of the control, and is a string such as Master, + PCM, CD or + Line. There are many pre-defined sources. + + + + The second, DIRECTION, is one of the + following strings according to the direction of the control: + Playback, Capture, Bypass + Playback and Bypass Capture. Or, it can + be omitted, meaning both playback and capture directions. + + + + The third, FUNCTION, is one of the + following strings according to the function of the control: + Switch, Volume and + Route. + + + + The example of control names are, thus, Master Capture + Switch or PCM Playback Volume. + + + + There are some exceptions: + + +
+ Global capture and playback + + Capture Source, Capture Switch + and Capture Volume are used for the global + capture (input) source, switch and volume. Similarly, + Playback Switch and Playback + Volume are used for the global output gain switch and + volume. + +
+ +
+ Tone-controls + + tone-control switch and volumes are specified like + Tone Control - XXX, e.g. Tone Control - + Switch, Tone Control - Bass, + Tone Control - Center. + +
+ +
+ 3D controls + + 3D-control switches and volumes are specified like 3D + Control - XXX, e.g. 3D Control - + Switch, 3D Control - Center, 3D + Control - Space. + +
+ +
+ Mic boost + + Mic-boost switch is set as Mic Boost or + Mic Boost (6dB). + + + + More precise information can be found in + alsa-kernel/Documentation/ControlNames.txt. + +
+
+ +
+ Access Flags + + + The access flag is the bit-flags which specifies the access type + of the given control. The default access type is + SNDRV_CTL_ELEM_ACCESS_READWRITE, + which means both read and write are allowed to this control. + When the access flag is omitted (i.e. = 0), it is + regarded as READWRITE access as default. + + + + When the control is read-only, pass + SNDRV_CTL_ELEM_ACCESS_READ instead. + In this case, you don't have to define + put callback. + Similarly, when the control is write-only (although it's a rare + case), you can use WRITE flag instead, and + you don't need get callback. + + + + If the control value changes frequently (e.g. the VU meter), + VOLATILE flag should be given. This means + that the control may be changed without + + notification. Applications should poll such + a control constantly. + + + + When the control is inactive, set + INACTIVE flag, too. + There are LOCK and + OWNER flags for changing the write + permissions. + + +
+ +
+ Callbacks + +
+ info callback + + The info callback is used to get + the detailed information of this control. This must store the + values of the given snd_ctl_elem_info_t + object. For example, for a boolean control with a single + element will be: + + + Example of info callback + +type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; + } +]]> + + + + + + The type field specifies the type + of the control. There are BOOLEAN, + INTEGER, ENUMERATED, + BYTES, IEC958 and + INTEGER64. The + count field specifies the + number of elements in this control. For example, a stereo + volume would have count = 2. The + value field is a union, and + the values stored are depending on the type. The boolean and + integer are identical. + + + + The enumerated type is a bit different from others. You'll + need to set the string for the currently given item index. + + + +value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); +]]> + + + +
+ +
+ get callback + + + This callback is used to read the current value of the + control and to return to the user-space. + + + + For example, + + + Example of get callback + +value.integer.value[0] = get_some_value(chip); + return 0; + } +]]> + + + + + + Here, the chip instance is retrieved via + snd_kcontrol_chip() macro. This macro + converts from kcontrol->private_data to the type defined by + chip_t. The + kcontrol->private_data field is + given as the argument of snd_ctl_new() + (see the later subsection + Constructor). + + + + The value field is depending on + the type of control as well as on info callback. For example, + the sb driver uses this field to store the register offset, + the bit-shift and the bit-mask. The + private_value is set like + + + + + + and is retrieved in callbacks like + + +private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + .... + } +]]> + + + + + + In get callback, you have to fill all the elements if the + control has more than one elements, + i.e. count > 1. + In the example above, we filled only one element + (value.integer.value[0]) since it's + assumed as count = 1. + +
+ +
+ put callback + + + This callback is used to write a value from the user-space. + + + + For example, + + + Example of put callback + +current_value != + ucontrol->value.integer.value[0]) { + change_current_value(chip, + ucontrol->value.integer.value[0]); + changed = 1; + } + return changed; + } +]]> + + + + As seen above, you have to return 1 if the value is + changed. If the value is not changed, return 0 instead. + If any fatal error happens, return a negative error code as + usual. + + + + Like get callback, + when the control has more than one elements, + all elemehts must be evaluated in this callback, too. + +
+ +
+ Callbacks are not atomic + + All these three callbacks are basically not atomic. + +
+
+ +
+ Constructor + + When everything is ready, finally we can create a new + control. For creating a control, there are two functions to be + called, snd_ctl_new1() and + snd_ctl_add(). + + + + In the simplest way, you can do like this: + + + + + + + + where my_control is the + snd_kcontrol_new_t object defined above, and chip + is the object pointer to be passed to + kcontrol->private_data + which can be referred in callbacks. + + + + snd_ctl_new1() allocates a new + snd_kcontrol_t instance (that's why the definition + of my_control can be with + __devinitdata + prefix), and snd_ctl_add assigns the given + control component to the card. + +
+ +
+ Change Notification + + If you need to change and update a control in the interrupt + routine, you can call snd_ctl_notify(). For + example, + + + + + + + + This function takes the card pointer, the event-mask, and the + control id pointer for the notification. The event-mask + specifies the types of notification, for example, in the above + example, the change of control values is notified. + The id pointer is the pointer of snd_ctl_elem_id_t + to be notified. + You can find some examples in es1938.c or + es1968.c for hardware volume interrupts. + +
+ +
+ + + + + + + API for AC97 Codec + +
+ General + + The ALSA AC97 codec layer is a well-defined one, and you don't + have to write many codes to control it. Only low-level control + routines are necessary. The AC97 codec API is defined in + <sound/ac97_codec.h>. + +
+ +
+ Full Code Example + + + Example of AC97 Interface + +private_data, return 0); + .... + // read a register value here from the codec + return the_register_value; + } + + static void snd_mychip_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) + { + mychip_t *chip = snd_magic_cast(mychip_t, + ac97->private_data, return 0); + .... + // write the given register value to the codec + } + + static int snd_mychip_ac97(mychip_t *chip) + { + ac97_t ac97; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_mychip_ac97_write; + ac97.read = snd_mychip_ac97_read; + ac97.private_data = chip; + return snd_ac97_mixer(card, &ac97, &chip->ac97); + } + +]]> + + + +
+ +
+ Constructor + + For creating an ac97 instance, call + snd_ac97_mixer() with an ac97_t + record, in which the callbacks and the private_data is set. + + + +ac97); +]]> + + + + where chip->ac97 is the pointer of a newly created + ac97_t instance. + This instance is not necessarily stored in the chip + record. When you need to change the register values from the + driver, or need the suspend/resume of ac97 codecs, keep this + pointer to pass to the corresponding functions. + +
+ +
+ Callbacks + + The standard callbacks are read and + write. Obviously they + correspond to the functions for read and write accesses to the + hardware low-level codes. + + + + The read callback returns the + register value specified in the argument. + + + +private_data, return 0); + .... + return the_register_value; + } +]]> + + + + Here, the chip can be cast from ac97->private_data. + + + + Meanwhile, the write callback is + used to set the register value. + + + + + + + + + + These callbacks are non-atomic like the callbacks of control API + unless they are called during suspend/resume phase. + + + + There are also other callbacks: + reset, + wait and + init. + + + + The reset callback is used to reset + the codec. If the chip requires a special way of reset, you can + define this callback. + This callback must be atomic when it's called in the suspend + mode. + + + + The wait callback is used for a + certain wait at the standard initialization of the codec. If the + chip requires the extra wait-time, define this callback. + This callback is always non-atomic, because it's never called + in the resume mode. + + + + The init callback is used for + additional initialization of the codec. This callback is called + after the reset, and should be atomic in the resume mode. + +
+ +
+ Updating Registers in The Driver + + If you need to access to the codec from the driver, you can + call the following functions: + snd_ac97_write(), + snd_ac97_read(), + snd_ac97_update() and + snd_ac97_update_bits(). + + + + Both snd_ac97_write() and + snd_ac97_update() functions are used to + set a value to the given register + (AC97_XXX). The different between them is + that snd_ac97_update() doesn't write a + value if the given value has been already set, while + snd_ac97_write() always rewrites the + value. + + + + + + + + + + snd_ac97_read() is used to read the value + of the given register. For example, + + + + + + + + + + snd_ac97_update_bits() is used to update + some bits of the given register. + + + + + + + + + + Also, there is a function to change the sample rate (of a + certain register such as + AC97_PCM_FRONT_DAC_RATE) when VRA is + supported by the codec: + snd_ac97_set_rate(). + + + + + + + + + + The following registers are available for setting the rate: + AC97_PCM_MIC_ADC_RATE, + AC97_PCM_FRONT_DAC_RATE, + AC97_PCM_LR_ADC_RATE, + AC97_SPDIF. When the + AC97_SPDIF is specified, the register is + not really changed but the corresponding IEC958 status bits will + be updated. + +
+ +
+ Clock Adjustment + + On some chip, the clock of the codec isn't 48000 but using a + PCI clock (to save a quartz!). In this case, change the field + ac97->clock to the corresponding + value. For example, intel8x0 + and es1968 drivers have the auto-measurement function of the + clock. + +
+ +
+ Proc Files + + The ALSA AC97 interface will create a proc file such as + /proc/asound/card0/ac97#0 and + ac97#0regs. You can refer to these files to + see the current status and registers of the codec. + +
+ +
+ Multiple Codecs + + When there are several codecs on the same card, you need to + call snd_ac97_new() multiple times with + ac97.num=1 or greater. The num field + specifies the codec + number. + + + + If you have set up multiple codecs, you need to either write + different callbacks for each codec or check + ac97->num in the + callback routines. + +
+ +
+ + + + + + + MIDI (MPU401-UART) Interface + +
+ General + + Many soundcards have built-in MIDI (MPU401-UART) + interfaces. When the soundcard supports the standard MPU401-UART + interface, most likely you can use the ALSA MPU401-UART API. The + MPU401-UART API is defined in + <sound/mpu401.h>. + + + + Some soundchips have similar but a little bit different + implementation of mpu401 stuff. For example, emu10k1 has its own + mpu401 routines. + + + + In this document, I won't explain the rawmidi interface API, + which is the basis of MPU401-UART implementation. + + + + For details, please check the source, + core/rawmidi.c, and examples such as + drivers/mpu401/mpu401_uart.c or + usb/usbmidi.c. + +
+ +
+ Constructor + + For creating a rawmidi object, call + snd_mpu401_uart_new(). + + + + + + + + + + The first argument is the card pointer, and the second is the + index of this component. You can create up to 8 rawmidi + devices. + + + + The third argument is the type of the hardware, + MPU401_HW_XXX. If it's not a special one, + you can use MPU401_HW_MPU401. + + + + The 4th argument is the i/o port address. Many + backward-compatible MPU401 has an i/o port such as 0x330. Or, it + might be a part of its own PCI i/o region. It depends on the + chip design. + + + + When the i/o port address above is a part of the PCI i/o + region, the MPU401 i/o port might have been already allocated + (reserved) by the driver itself. In such a case, pass non-zero + to the 5th argument + (integrated). Otherwise, pass 0 to it, + and + the mpu401-uart layer will allocate the i/o ports by itself. + + + + Usually, the port address corresponds to the command port and + port + 1 corresponds to the data port. If not, you may change + the cport field of + mpu401_t manually + afterward. However, mpu401_t pointer is not + returned explicitly by + snd_mpu401_uart_new(). You need to cast + rmidi->private_data to + mpu401_t explicitly, + + + +private_data, ); +]]> + + + + and reset the cport as you like: + + + +cport = my_own_control_port; +]]> + + + + + + The 6th argument specifies the irq number for UART. If the irq + is already allocated, pass 0 to the 7th argument + (irq_flags). Otherwise, pass the flags + for irq allocation + (SA_XXX bits) to it, and the irq will be + reserved by the mpu401-uart layer. If the card doesn't generates + UART interrupts, pass -1 as the irq number. Then a timer + interrupt will be invoked for polling. + +
+ +
+ Interrupt Handler + + When the interrupt is allocated in + snd_mpu401_uart_new(), the private + interrupt handler is used, hence you don't have to do nothing + else than creating the mpu401 stuff. Otherwise, you have to call + snd_mpu401_uart_interrupt() explicitly when + a UART interrupt is invoked and checked in your own interrupt + handler. + + + + In this case, you need to pass the private_data of the + returned rawmidi object from + snd_mpu401_uart_new() as the second + argument of snd_mpu401_uart_interrupt(). + + + +private_data, regs); +]]> + + + +
+ +
+ + + + + + + Miscellaneous Devices + +
+ FM OPL3 + + The FM OPL3 is still used on many chips (mainly for backward + compatibility). ALSA has a nice OPL3 FM control layer, too. The + OPL3 API is defined in + <sound/opl3.h>. + + + + FM registers can be directly accessed through direct-FM API, + defined in <sound/asound_fm.h>. In + ALSA native mode, FM registers are accessed through + Hardware-Dependant Device direct-FM extension API, whereas in + OSS compatible mode, FM registers can be accessed with OSS + direct-FM compatible API on /dev/dmfmX device. + + + + For creating the OPL3 component, you have two functions to + call. The first one is a constructor for opl3_t + instance. + + + + + + + + + + The first argument is the card pointer, the second one is the + left port address, and the third is the right port address. In + most cases, the right port is placed at the left port + 2. + + + + The fourth argument is the hardware type. + + + + When the left and right ports have been already allocated by + the card driver, pass non-zero to the fifth argument + (integrated). Otherwise, opl3 module will + allocate the specified ports by itself. + + + + If this function returns successfully with 0, then create a + hwdep device for this opl3. + + + + + + + + + + The first argument is the opl3_t instance you + created, and the second is the index number, usually 0. + + + + The third argument is the index-offset for the sequencer + client assigned to the OPL3 port. When there is an MPU401-UART, + give 1 for here (UART always takes 0). + +
+ +
+ Hardware-Dependent Devices + + Some chips need the access from the user-space for special + controls or for loading the micro code. In such a case, you can + create a hwdep (hardware-dependent) device. The hwdep API is + defined in <sound/hwdep.h>. You can + find examples in opl3 driver or + isa/sb/sb16_csp.c. + + + + Creation of the hwdep instance is done via + snd_hwdep_new(). + + + + + + + + where the third argument is the index number. + + + + You can then pass any pointer value to the + private_data. Again, it should be a + magic-allocated record, so that the cast can be checked more + safely. If you assign a private data, you should define the + destructor, too. The destructor function is set to + private_free field. + + + +private_data = p; + hw->private_free = mydata_free; +]]> + + + + and the implementation of destructor would be: + + + +private_data, return); + snd_magic_kfree(p); + } +]]> + + + + + + The arbitrary file operations can be defined for this + instance. The file operators are defined in + ops table. For example, assume that + this chip needs an ioctl. + + + +ops.open = mydata_open; + hw->ops.ioctl = mydata_ioctl; + hw->ops.release = mydata_release; +]]> + + + + And implement the callback functions as you like. + +
+ +
+ IEC958 (S/PDIF) + + Usually the controls for IEC958 devices are implemented via + control interface. There is a macro to compose a name string for + IEC958 controls, SNDRV_CTL_NAME_IEC958() + defined in <include/asound.h>. + + + + There are some standard controls for IEC958 status bits. These + controls use the type SNDRV_CTL_ELEM_TYPE_IEC958, + and the size of element is fixed as 4 bytes array + (value.iec958.status[x]). For info + callback, you don't specify + the value field for this type (the count field must be set, + though). + + + + IEC958 Playback Con Mask is used to return the + bit-mask for the IEC958 status bits of consumer mode. Similarly, + IEC958 Playback Pro Mask returns the bitmask for + professional mode. They are read-only controls, and are defined + as MIXER controls (iface = + SNDRV_CTL_ELEM_IFACE_MIXER). + + + + Meanwhile, IEC958 Playback Default control is + defined for getting and setting the current default IEC958 + bits. Note that this one is usually defined as a PCM control + (iface = SNDRV_CTL_ELEM_IFACE_PCM), + although in some places it's defined as a MIXER control. + + + + In addition, you can define the control switches to + enable/disable or to set the raw bit mode. The implementation + will depend on the chip, but the control should be named as + IEC958 xxx, preferably using + SNDRV_CTL_NAME_IEC958() macro. + + + + You can find several cases, for example, + pci/emu10k1, + pci/ice1712, or + pci/cmipci.c. + +
+ +
+ + + + + + + Buffer and Memory Management + +
+ Buffer Types + + ALSA provides several different buffer allocation functions + depending on the bus and the architecture. All these have a + consistent API. The allocation of physically-contiguous pages is + done via + snd_malloc_xxx_pages() function, where xxx + is the bus type. + + + + The allocation of pages with fallback is + snd_malloc_xxx_pages_fallback(). This + function tries to allocate the specified pages but if the pages + are not available, it tries to reduce the page sizes until the + enough space is found. + + + + For releasing the space, call + snd_free_xxx_pages() function. + + + + Usually, ALSA drivers try to allocate and reserve + a large contiguous physical space + at the time the module is loaded for the later use. + This is called pre-allocation. + As already written, you can call the following function at the + construction of pcm instance (in the case of PCI bus). + + + + + + + + where size is the byte size to be + pre-allocated and the max is the maximal + size to be changed via prealloc proc file. + The allocator will try to get as the large area as possible + within the given size. + There are different versions of pre-allocator for different + buses. + + + + Once when the buffer is pre-allocated, you can use the + allocator in the hw_params callback + + + + + + + + Note that you have to pre-allocate to use this function + (i.e. you cannot use this function for + + a scatter-gather buffer). + +
+ +
+ External Hardware Buffers + + Some chips have their own hardware buffers and the DMA + transfer from the host memory is not available. In such a case, + you need to either 1) copy/set the audio data directly to the + external hardware buffer, or 2) make an intermediate buffer and + copy/set the data from it to the external hardware buffer in + interrupts (or in tasklets, preferably). + + + + The first case works fine if the external hardware buffer is enough + large. This method doesn't need any extra buffers and thus is + more effective. You need to define the + copy and + silence callbacks for + the data transfer. However, there is a drawback: it cannot + be mmapped. The examples are GUS's GF1 PCM or emu8000's + wavetable PCM. + + + + The second case allows the mmap of the buffer, although you have + to handle an interrupt or a tasklet for transferring the data + from the intermediate buffer to the hardware buffer. You can find an + example in vxpocket driver. + + + + Another case is that the chip uses a PCI memory-map + region for the buffer instead of the host memory. In this case, + mmap is available only on certain architectures like intel. In + non-mmap mode, the data cannot be transferred as the normal + way. Thus you need to define copy and + silence callbacks as well + as in the cases above. The examples are found in + rme32.c and rme96.c. + + + + The implementation of copy and + silence callbacks depends upon + whether the hardware supports interleaved or non-interleaved + samples. The copy callback is + defined like below, a bit + differently depending whether the direction is playback or + capture: + + + + + + + + + + In the case of interleaved samples, the second argument + (channel) is not used. The third argument + (pos) points the + current position offset in frames. + + + + The meaning of the fourth argument is different between + playback and capture. For playback, it holds the source data + pointer, and for capture, it's the destination data pointer. + + + + The last argument is the number of frames to be copied. + + + + What you have to do in this callback is again different + between playback and capture directions. In the case of + playback, you do: copy the given amount of data + (count) at the specified pointer + (src) to the specified offset + (pos) on the hardware buffer. When + coded like memcpy-like way, the copy would be like: + + + + + + + + + + For the capture direction, you do: copy the given amount of + data (count) at the specified offset + (pos) on the hardware buffer to the + specified pointer (dst). + + + + + + + + Note that both of the position and the data amount are given + in frames. + + + + In the case of non-interleaved samples, the implementation + will be a bit more complicated. + + + + You need to check the channel argument, and if it's -1, copy + the whole channels. Otherwise, you have to copy only the + specified channel. Please check + isa/gus/gus_pcm.c as an example. + + + + The silence callback is also + implemented in a similar way. + + + + + + + + + + The meanings of arguments are identical with the + copy + callback, although there is no src/dst + argument. In the case of interleaved samples, the channel + argument has no meaning, as well as on + copy callback. + + + + The role of silence callback is to + set the given amount + (count) of silence data at the + specified offset (pos) on the hardware + buffer. Suppose that the data format is signed (that is, the + silent-data is 0), and the implementation using a memset-like + function would be like: + + + + + + + + + + In the case of non-interleaved samples, again, the + implementation becomes a bit more complicated. See, for example, + isa/gus/gus_pcm.c. + +
+ +
+ Non-Contiguous Buffers + + If your hardware supports the page table like emu10k1 or the + buffer descriptors like via82xx, you can use the scatter-gather + (SG) DMA. ALSA provides an interface for handling SG-buffers. + The API is provided in <sound/pcm_sgbuf.h>. + + + + For creating the SG-buffer handler, call + snd_pcm_lib_preallocate_sg_pages() or + snd_pcm_lib_preallocate_sg_pages_for_all() + in the PCM constructor like other PCI pre-allocator. + You need to pass the + pci_dev struct pointer of the chip. + The snd_sg_buf_t instance is created as + substream->dma_private. You can cast + the pointer like: + + + +dma_private; +]]> + + + + + + Then call snd_pcm_lib_malloc_pages() + in hw_params callback + as well as in the case of normal PCI buffer. + The SG-buffer handler will allocate the non-contiguous kernel + pages of the given size and map them onto the virtually contiguous + memory. The virtual pointer is addressed in runtime->dma_area. + The physical address (runtime->dma_addr) is set to zero, + because the buffer is physically non-contigous. + The physical address table is set up in sgbuf->table. + You can get the physical address at a certain offset via + snd_pcm_sgbuf_get_addr(). + + + + When a SG-handler is used, you need to set + snd_pcm_sgbuf_ops_page as + the page callback. + + + + For releasing the data, call + snd_pcm_lib_free_pages() in the + hw_free callback as usual. + +
+ +
+ Vmalloc'ed Buffers + + It's possible to use a buffer allocated via + vmalloc, for example, for an intermediate + buffer. Since the allocated pages are not contiguous, you need + to set the page callback to obtain + the physical address at every offset. + + + + The implementation of page callback + would be like this: + + + + + + /* get the physical page pointer on the given offset */ + static struct page *mychip_page(snd_pcm_substream_t *substream, + unsigned long offset) + { + void *pageptr = substream->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); + } +]]> + + + +
+ +
+ + + + + + + Proc Interface + + ALSA provides an easy interface for procfs. The proc files are + very useful for debugging. I recommend you set up proc files if + you write a driver and want to get a running status or register + dumps. The API is found in + <sound/info.h>. + + + + For creating a proc file, call + snd_card_proc_new(). + + + + + + + + where the second argument specifies the proc-file name to be + created. The above example will create a file + my-file under the card directory, + e.g. /proc/asound/card0/my-file. + + + + Like other components, the proc entry created via + snd_card_proc_new() will be registered and + released automatically in the card registration and release + functions. + + + + When the creation is successful, the function stores a new + instance at the pointer given in the third argument. + It is initialized as a text proc file for read only. For using + this proc file as a read-only text file as it is, set the read + callback with a private data via + snd_info_set_text_ops(). + + + + + + + + where the second argument (chip) is the + private data to be used in the callbacks and the third + (my_proc_read) is the callback function, which + is defined like + + + + + + + + + + + In the read callback, use snd_iprintf() for + output strings, which works just like normal + printf(). For example, + + + +private_data, return); + + snd_iprintf(buffer, "This is my chip!\n"); + snd_iprintf(buffer, "Port = %ld\n", chip->port); + } +]]> + + + + + + The file permission can be changed afterwards. As default, it's + set as read only for all users. If you want to add the write + permission to the user (root as default), set like below: + + + +mode = S_IFREG | S_IRUGO | S_IWUSR; +]]> + + + + and set the write buffer size and the callback + + + +c.text.write_size = 256; + entry->c.text.write = my_proc_write; +]]> + + + + + + The buffer size for read is set to 1024 implicitly by + snd_info_set_text_ops(). It should suffice + in most cases (the size will be aligned to + PAGE_SIZE anyway), but if you need to handle + very large text files, you can set it explicitly, too. + + + +c.text.read_size = 65536; +]]> + + + + + + For the write callback, you can use + snd_info_get_line() to get a text line, and + snd_info_get_str() to retrieve a string from + the line. Some examples are found in + core/oss/mixer_oss.c, core/oss/and + pcm_oss.c. + + + + For a raw-data proc-file, set the attributes like the following: + + + +content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &my_file_io_ops; + entry->size = 4096; + entry->mode = S_IFREG | S_IRUGO; +]]> + + + + + + The callback is much more complicated than the text-file + version. You need to use a low-level i/o functions such as + copy_from/to_user() to transfer the + data. Also, you have to keep tracking the file position, too. + + + +f_pos + size > local_max_size) + size = local_max_size - file->f_pos; + if (copy_to_user(buf, local_data + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + return size; + } +]]> + + + + + + + + + + + + Power Management + + If the chip is supposed to work with with suspend/resume + functions, you need to add the power-management codes to the + driver. The additional codes for the power-management should be + ifdef'ed with + CONFIG_PM. + + + + Basic jobs of suspend/resume are done in + suspend and + resume callbacks of + pci_driver struct. Unfortunately, the + API of these callbacks was changed at the middle time of Linux + 2.4.x, if you want to keep the support for older kernels, you + have to write two different callbacks. The example below is the + skeleton callbacks which just call the real suspend and resume + functions. + + + + + + + + + + The scheme of the real suspend job is as following. + + + Check whether the power-state is already D3hot. If yes, skip the job. + Call snd_pcm_suspend_all() to suspend the running PCM streams. + Save the register values if necessary. + Stop the hardware if necessary. + Set the power-state as D3hot by calling snd_power_change_state(). + + + + + A typical code would be like: + + + +card; + // (1) + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + // (2) + snd_pcm_suspend_all(chip->pcm); + // (3) + snd_mychip_save_registers(chip); + // (4) + snd_mychip_stop_hardware(chip); + // (5) + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + } +]]> + + + + + + The scheme of the real resume job is as following. + + + Check whether the power-state is already D0. + If yes, skip the job. + Enable the pci device again by calling + pci_enable_device(). + Re-initialize the chip. + Restore the saved registers if necessary. + Resume the mixer, e.g. calling + snd_ac97_resume(). + Restart the hardware (if any). + Set the power-state as D0 by calling + snd_power_change_state(). + + + + + A typical code would be like: + + + +card; + // (1) + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + // (2) + pci_enable_device(chip->pci); + // (3) + snd_mychip_reinit_chip(chip); + // (4) + snd_mychip_restore_registers(chip); + // (5) + snd_ac97_resume(chip->ac97); + // (6) + snd_mychip_restart_chip(chip); + // (7) + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + } +]]> + + + + + + In addition to the callbacks above, you should define a callback + for the changes via the ALSA control interface. It's defined + like below: + + + +power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + mychip_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + mychip_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; + } +]]> + + + + + + OK, we have all callbacks now. Let's set up them now. In the + initialization of the card, add the following: + + + +set_power_state = snd_mychip_set_power_state; + card->power_state_private_data = chip; + #endif + .... + } +]]> + + + + + + If you need a space for saving the registers, you'll need to + allocate the buffer for it here, too, since you cannot call + kmalloc() with + GFP_KERNEL flag or + vmalloc() in the suspend callback. + The allocated buffer should be released in the corresponding + destructor. + + + + And next, set suspend/resume callbacks to the pci_driver, + + + + + + + + + + Last but not least: Please keep in mind that you cannot call + schedule() during the suspend and the resume + callbacks. If any delay is necessary, you have to use + mdelay() or udelay() + instead of schedule_timeout()! + Of course, semaphores cannot be used, too, which will invoke sleep + inside. + + + + + + + + + Module Parameters + + There are standard module options for ALSA. At least, each + module should have index, + id and enable + options. + + + + If the module supports multiple cards (usually up to + 8 = SNDRV_CARDS cards), they should be + arrays. The default initial values are defined already as + constants for ease of programming: + + + + + + + + + + If the module supports only a single card, they could be single + variables, instead. enable option is not + always necessary in this case, but it wouldn't be so bad to have a + dummy option for compatibility. + + + + The module parameters must be declared with the standard + MODULE_PARM() and + MODULE_PARM_DESC() macros. The ALSA provides + an additional macro, MODULE_PARM_SYNTAX(), + for describing its syntax. The strings will be written to + /lib/modules/XXX/modules.generic_string + file. + + + + For convenience, the typical string arguments given to + MODULE_PARM_SYNTAX() are defined in + <sound/initval.h>, such as + SNDRV_ID_DESC or + SNDRV_ENABLED. + + + + The typical coding would be like below: + + + + + + + + + + Also, don't forget to define the module description, classes, + license and devices. Especially, the recent modprobe requires to + define the module license as GPL, etc., otherwise the system is + shown as tainted. + + + + + + + + + + For building the driver into kernel, you should define the + setup() function in addition, too. + ALSA provides get_id() function to retrieve + a string argument from the kernel boot parameters. + + + += SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; + } + + __setup("snd-mychip=", alsa_card_mychip_setup); + + #endif /* ifndef MODULE */ +]]> + + + + + + + + + + + Useful Functions + +
+ <function>snd_printk()</function> and friends + + ALSA provides a verbose version of + printk() function. If a kernel config + CONFIG_SND_VERBOSE_PRINTK is set, this + function prints the given message together with the file name + and the line of the caller. The KERN_XXX + prefix is processed as + well as the original printk() does, so it's + recommended to add this prefix, e.g. + + + + + + + + + + There are also printk()'s for + debugging. snd_printd() can be used for + general debugging purposes. If + CONFIG_SND_DEBUG is set, this function is + compiled, and works just like + snd_printk(). If the ALSA is compiled + without the debugging flag, it's ignored. + + + + snd_printdd() is compiled in only when + CONFIG_SND_DEBUG_DETECT is set. Please note + that DEBUG_DETECT is not set as default + even if you configure the alsa-driver with + option. You need to give + explicitly option instead. + +
+ +
+ <function>snd_assert()</function> + + snd_assert() macro is similar with the + normal assert() macro. For example, + + + + + + + + + + The first argument is the expression to evaluate, and the + second argument is the action if it fails. When + CONFIG_SND_DEBUG, is set, it will show an + error message such as BUG? (xxx) (called from + yyy). When no debug flag is set, this is + ignored. + +
+ +
+ <function>snd_runtime_check()</function> + + This macro is quite similar with + snd_assert(). Unlike + snd_assert(), the expression is always + evaluated regardless of + CONFIG_SND_DEBUG. When + CONFIG_SND_DEBUG is set, the macro will + show a message like ERROR (xx) (called from + yyy). + +
+ +
+ <function>snd_BUG()</function> + + It calls snd_assert(0,) -- that is, just + prints the error message at the point. It's useful to show that + a fatal error happens there. + +
+
+ + + + + + + Acknowledgments + + I would like to thank Phil Kerr for his help for improvement and + corrections of this document. + + + Kevin Conder reformatted the original plain-text to the + DocBook format. + + + + +
diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/OSS-Emulation.txt linux/Documentation/ALSA/OSS-Emulation.txt --- linux-2.4.21-rc1.orig/Documentation/ALSA/OSS-Emulation.txt 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/OSS-Emulation.txt 2003-01-18 11:44:51.000000000 -0700 @@ -0,0 +1,302 @@ + NOTES ON KERNEL OSS-EMULATION + ============================= + + Jan. 9, 2003 Takashi Iwai + + +Modules +======= + +ALSA provides a powerful OSS emulation on the kernel. +The OSS emulation for PCM, mixer and sequencer devices is implemented +as add-on kernel modules, snd-pcm-oss, snd-mixer-oss and snd-seq-oss. +When you need to access the OSS PCM, mixer or sequencer devices, the +corresponding module has to be loaded. + +For loading these modules automatically, define the aliases in +/etc/modules.conf like below: + + alias sound-service-0-0 snd-mixer-oss + alias sound-service-0-1 snd-seq-oss + alias sound-service-0-3 snd-pcm-oss + alias sound-service-0-8 snd-seq-oss + alias sound-service-0-12 snd-pcm-oss + +Then the access to an OSS device file such as /dev/dsp0 triggers to +load the necessary module via KMOD. + +For auto-loading the secondary card device like /dev/dsp1, the +following aliases are necessary in addition: + + alias sound-service-1-0 snd-mixer-oss + alias sound-service-1-3 snd-pcm-oss + alias sound-service-1-12 snd-pcm-oss + +Here you don't need to define service-1-1 and service-1-8 because +there is only one sequencer device. +Similarly, you can add definitions for the third or later cards as +sound-service-X-Y. + +The OSS-MIDI is emulated directly in the ALSA rawmidi module, +therefore no extra module exists for that purpose. + +The currently available OSS configuration is shown in +/proc/asound/oss/sndstat. This shows in the same syntax of +/dev/sndstat, which is available on the commercial OSS driver. +On ALSA, you can symlink /dev/sndstat to this proc file. + +Please note that the devices listed in this proc file appear only +after the corresponding OSS-emulation module is loaded. Don't worry +even if "NOT ENABLED IN CONFIG" is shown in it. + + +Device Mapping +============== + +ALSA supports the following OSS device files: + + PCM: + /dev/dspX + /dev/adspX + + Mixer: + /dev/mixerX + + MIDI: + /dev/midi0X + /dev/amidi0X + + Sequencer: + /dev/sequencer + /dev/sequencer2 (aka /dev/music) + +where X is the card number from 0 to 7. + +(NOTE: Some distributions have the device files like /dev/midi0 and + /dev/midi1. They are NOT for OSS but for tclmidi, which is + a totally different thing.) + +Unlike the real OSS, ALSA cannot use the device files more than the +assigned ones. For example, the first card cannot use /dev/dsp1 or +/dev/dsp2, but only /dev/dsp0 and /dev/adsp0. + +As seen above, PCM and MIDI may have two devices. Usually, the first +PCM device (hw:0,0 in ALSA) is mapped to /dev/dsp and the secondary +device (hw:0,1) to /dev/adsp (if available). For MIDI, /dev/midi and +/dev/amidi, respectively. + +You can change this device mapping via the module options of +snd-pcm-oss and snd-rawmidi. In the case of PCM, the following +options are available for snd-pcm-oss: + + dsp_map PCM device number assigned to /dev/dspX + (default = 0) + adsp_map PCM device number assigned to /dev/adspX + (default = 1) + +For example, to map the third PCM device (hw:0,2) to /dev/adsp0, +define like this: + + options snd-pcm-oss adsp_map=2 + +The options take arrays. For configuring the second card, specify +two entries separated by comma. For example, to map the third PCM +device on the second card to /dev/adsp1, define like below: + + options snd-pcm-oss adsp_map=0,2 + +To change the mapping of MIDI devices, the following options are +available for snd-rawmidi: + + midi_map MIDI device number assigned to /dev/midi0X + (default = 0) + amidi_map MIDI device number assigned to /dev/amidi0X + (default = 1) + +For example, to assign the third MIDI device on the first card to +/dev/midi00, define as follows: + + options snd-rawmidi midi_map=2 + + +PCM Mode +======== + +As default, ALSA emulates the OSS PCM with so-called plugin layer, +i.e. tries to convert the sample format, rate or channels +automatically when the card doesn't support it natively. +This will lead to some problems for some applications like quake or +wine, especially if they use the card only in the MMAP mode. + +In such a case, you can change the behavior of PCM per application by +writing a command to the proc file. There is a proc file for each PCM +stream, /proc/asound/cardX/pcmY[cp]/oss, where X is the card number +(zero-based), Y the PCM device number (zero-based), and 'p' is for +playback and 'c' for capture, respectively. Note that this proc file +exists only after snd-pcm-oss module is loaded. + +The command sequence has the following syntax: + + app_name fragments fragment_size [options] + +app_name is the name of application with (higher priority) or without +path. +fragments specifies the number of fragments or zero if no specific +number is given. +fragment_size is the size of fragment in bytes or zero if not given. +options is the optional parameters. The following options are +available: + + disable the application tries to open a pcm device for + this channel but does not want to use it. + direct don't use plugins + block force block open mode + non-block force non-block open mode + +The disable option is useful when one stream direction (playback or +capture) is not handled correctly by the application although the +hardware itself does support both directions. +The direct option is used, as mentioned above, to bypass the automatic +conversion and useful for MMAP-applications. +For example, to playback the first PCM device without plugins for +quake, send a command via echo like the following: + + % echo "quake 0 0 direct" > /proc/asound/card0/pcm0p/oss + +While quake wants only playback, you may append the second command +to notify driver that only this direction is about to be allocated: + + % echo "quake 0 0 disable" > /proc/asound/card0/pcm0c/oss + +The permission of proc files depend on the module options of snd. +As default it's set as root, so you'll likely need to be superuser for +sending the command above. + +The block and non-block options are used to change the behavior of +opening the device file. +As default, ALSA behaves as defined in POSIX, i.e. blocks the file +when it's busy until the device becomes free (unless O_NONBLOCK is +specified). Some applications assume the non-block open behavior, +which are actually implemented in some real OSS drivers. + +This blocking behavior can be changed globally via nonblock_open +module option of snd-pcm-oss. For using the non-block mode as default +for OSS devices, define like the following: + + options snd-pcm-oss nonblock_open=1 + +You can check the currently defined configuration by reading the proc +file. The read image can be sent to the proc file again, hence you +can save the current configuration + + % cat /proc/asound/card0/pcm0p/oss > /somewhere/oss-cfg + +and restore it like + + % cat /somewhere/oss-cfg > /proc/asound/card0/pcm0p/oss + +Also, for clearing all the current configuration, send "erase" command +as below: + + % echo "erase" > /proc/asound/card0/pcm0p/oss + + +Mixer Elements +============== + +Since ALSA has completely different mixer interface, the emulation of +OSS mixer is relatively complicated. ALSA builds up a mixer element +from several different ALSA (mixer) controls based on the name +string. For example, the volume element SOUND_MIXER_PCM is composed +from "PCM Playback Volume" and "PCM Playback Switch" controls for the +playback direction and from "PCM Capture Volume" and "PCM Capture +Switch" for the capture directory (if exists). When the PCM volume of +OSS is changed, all the volume and switch controls above are adjusted +automatically. + +As default, ALSA uses the following control for OSS volumes: + + OSS volume ALSA control Index + ----------------------------------------------------- + SOUND_MIXER_VOLUME Master 0 + SOUND_MIXER_BASS Tone Control - Bass 0 + SOUND_MIXER_TREBLE Tone Control - Treble 0 + SOUND_MIXER_SYNTH Synth 0 + SOUND_MIXER_PCM PCM 0 + SOUND_MIXER_SPEAKER PC Speaker 0 + SOUND_MIXER_LINE Line 0 + SOUND_MIXER_MIC Mic 0 + SOUND_MIXER_CD CD 0 + SOUND_MIXER_IMIX Monitor Mix 0 + SOUND_MIXER_ALTPCM PCM 1 + SOUND_MIXER_RECLEV (not assigned) + SOUND_MIXER_IGAIN Capture 0 + SOUND_MIXER_OGAIN Playback 0 + SOUND_MIXER_LINE1 Aux 0 + SOUND_MIXER_LINE2 Aux 1 + SOUND_MIXER_LINE3 Aux 2 + SOUND_MIXER_DIGITAL1 Digital 0 + SOUND_MIXER_DIGITAL2 Digital 1 + SOUND_MIXER_DIGITAL3 Digital 2 + SOUND_MIXER_PHONEIN Phone 0 + SOUND_MIXER_PHONEOUT Phone 1 + SOUND_MIXER_VIDEO Video 0 + SOUND_MIXER_RADIO Radio 0 + SOUND_MIXER_MONITOR Monitor 0 + +The second column is the base-string of the corresponding ALSA +control. In fact, the controls with "XXX [Playback|Capture] +[Volume|Switch]" will be checked in addition. + +The current assignment of these mixer elements is listed in the proc +file, /proc/asound/cardX/mixer_oss, which will be like the following + + VOLUME "Master" 0 + BASS "" 0 + TREBLE "" 0 + SYNTH "" 0 + PCM "PCM" 0 + ... + +where the first column is the OSS volume element, the second column +the base-string of the corresponding ALSA control, and the third the +control index. When the string is empty, it means that the +corresponding OSS control is not available. + +For changing the assignment, you can write the configuration to this +proc file. For example, to map "Wave Playback" to the PCM volume, +send the command like the following: + + % echo 'VOLUME "Wave Playback" 0' > /proc/asound/card0/mixer_oss + +The command is exactly as same as listed in the proc file. You can +change one or more elements, one volume per line. In the last +example, both "Wave Playback Volume" and "Wave Playback Switch" will +be affected when PCM volume is changed. + +Like the case of PCM proc file, the permission of proc files depend on +the module options of snd. you'll likely need to be superuser for +sending the command above. + +As well as in the case of PCM proc file, you can save and restore the +current mixer configuration by reading and writing the whole file +image. + + +Unsupported Features +==================== + +MMAP on ICE1712 driver +---------------------- +ICE1712 supports only the unconventional format, interleaved +10-channels 24bit (packed in 32bit) format. Therefore you cannot mmap +the buffer as the conventional (mono or 2-channels, 8 or 16bit) format +on OSS. + +USB devices +----------- +Some USB devices support only 24bit format packed in 3bytes. This +format is not supported by OSS and no conversion is provided by kernel +OSS emulation. You can use the user-space OSS emulation via libaoss +instead. + diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/SB-Live-mixer.txt linux/Documentation/ALSA/SB-Live-mixer.txt --- linux-2.4.21-rc1.orig/Documentation/ALSA/SB-Live-mixer.txt 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/SB-Live-mixer.txt 2002-03-06 00:49:31.000000000 -0700 @@ -0,0 +1,356 @@ + + Sound Blaster Live mixer / default DSP code + =========================================== + + +The EMU10K1 chips have a DSP part which can be programmed to support +various ways of sample processing, which is described here. +(This acticle does not deal with the overall functionality of the +EMU10K1 chips. See the manuals section for further details.) + +The ALSA driver programs this portion of chip by default code +(can be altered later) which offers the following functionality: + + +1) IEC958 (S/PDIF) raw PCM +-------------------------- + +This PCM device (it's the 4th PCM device (index 3!) and first subdevice +(index 0) for a given card) allows to forward 48kHz, stereo, 16-bit +little endian streams without any modifications to the digital output +(coaxial or optical). The universal interface allows the creation of up +to 8 raw PCM devices operating at 48kHz, 16-bit little endian. It would +be easy to add support for multichannel devices to the current code, +but the conversion routines exist only for stereo (2-channel streams) +at the time. + +Look to tram_poke routines in lowlevel/emu10k1/emufx.c for more details. + + +2) Digital mixer controls +------------------------- + +These controls are built using the DSP instructions. They offer extended +functionality. Only the default build-in code in the ALSA driver is described +here. Note that the controls work as attenuators: the maximum value is the +neutral position leaving the signal unchanged. Note that if the same destination +is mentioned in multiple controls, the signal is accumulated and can be wrapped +(set to maximal or minimal value without checking of overflow). + + +Explanation of used abbreviations: + +DAC - digital to analog converter +ADC - analog to digital converter +I2S - one-way three wire serial bus for digital sound by Philips Semiconductors + (this standard is used for connecting standalone DAC and ADC converters) +LFE - low frequency effects (subwoofer signal) +AC97 - a chip containing an analog mixer, DAC and ADC converters +IEC958 - S/PDIF +FX-bus - the EMU10K1 chip has an effect bus containing 16 accumulators. + Each of the synthesizer voices can feed its output to these accumulators + and the DSP microcontroller can operate with the resulting sum. + + +name='Wave Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Wave Surround Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result samples are forwarded to the rear I2S DACs. These DACs operates +separately (they are not inside the AC97 codec). + +name='Wave Center Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result is mixed to mono signal (single channel) and forwarded to +the ??rear?? right DAC PCM slot of the AC97 codec. + +name='Wave LFE Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is mixed to mono signal (single channel) and forwarded to +the ??rear?? left DAC PCM slot of the AC97 codec. + +name='Wave Capture Volume',index=0 +name='Wave Capture Switch',index=0 + +These controls are used to attenuate samples for left and right PCM FX-bus +accumulator. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Music Playback Volume',index=0 + +This control is used to attenuate samples for left and right MIDI FX-bus +accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Music Capture Volume',index=0 +name='Music Capture Switch',index=0 + +These controls are used to attenuate samples for left and right MIDI FX-bus +accumulator. ALSA uses accumulators 4 and 5 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Surround Digital Playback Volume',index=0 + +This control is used to attenuate samples for left and right rear PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples. +The result samples are forwarded to the rear I2S DACs. These DACs operate +separately (they are not inside the AC97 codec). + +name='Surround Digital Capture Volume',index=0 +name='Surround Digital Capture Switch',index=0 + +These controls are used to attenuate samples for left and right rear PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Center Playback Volume',index=0 + +This control is used to attenuate sample for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded +to the ??rear?? right DAC PCM slot of the AC97 codec. + +name='LFE Playback Volume',index=0 + +This control is used to attenuate sample for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded +to the ??rear?? left DAC PCM slot of the AC97 codec. + +name='AC97 Playback Volume',index=0 + +This control is used to attenuate samples for left and right front ADC PCM slots +of the AC97 codec. The result samples are forwarded to the front DAC PCM +slots of the AC97 codec. +******************************************************************************** +*** Note: This control should be zero for the standard operations, otherwise *** +*** a digital loopback is activated. *** +******************************************************************************** + +name='AC97 Capture Volume',index=0 + +This control is used to attenuate samples for left and right front ADC PCM slots +of the AC97 codec. The result is forwarded to the ADC capture FIFO (thus to +the standard capture PCM device). +******************************************************************************** +*** Note: This control should be 100 (maximal value), otherwise no analog *** +*** inputs of the AC97 codec can be captured (recorded). *** +******************************************************************************** + +name='IEC958 TTL Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the AC97 codec. + +name='IEC958 TTL Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='Zoom Video Playback Volume',index=0 + +This control is used to attenuate samples from left and right zoom video +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the AC97 codec. + +name='Zoom Video Capture Volume',index=0 + +This control is used to attenuate samples from left and right zoom video +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='IEC958 Optical Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital input. The result samples are forwarded to the front DAC PCM slots +of the AC97 codec. + +name='IEC958 Optical Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='IEC958 Coaxial Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 coaxial +digital inputs. The result samples are forwarded to the front DAC PCM slots +of the AC97 codec. + +name='IEC958 Coaxial Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 coaxial +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='Line LiveDrive Playback Volume',index=0 +name='Line LiveDrive Playback Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the LiveDrive). The result samples are forwarded to the front +DAC PCM slots of the AC97 codec. + +name='Line LiveDrive Capture Volume',index=1 +name='Line LiveDrive Capture Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the LiveDrive). The result samples are forwarded to the ADC +capture FIFO (thus to the standard capture PCM device). + +name='Tone Control - Switch',index=0 + +This control turns the tone control on or off. The samples for front, rear +and center / LFE outputs are affected. + +name='Tone Control - Bass',index=0 + +This control sets the bass intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='Tone Control - Treble',index=0 + +This control sets the treble intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='IEC958 Optical Raw Playback Switch',index=0 + +If this switch is on, then the samples for the IEC958 (S/PDIF) digital +output are taken only from the raw FX8010 PCM, otherwise standard front +PCM samples are taken. + +name='Headphone Playback Volume',index=1 + +This control attenuates the samples for the headphone output. + +name='Headphone Center Playback Switch',index=1 + +If this switch is on, then the sample for the center PCM is put to the +left headphone output (useful for SB Live cards without separate center/LFE +output). + +name='Headphone LFE Playback Switch',index=1 + +If this switch is on, then the sample for the center PCM is put to the +right headphone output (useful for SB Live cards without separate center/LFE +output). + + +3) PCM stream related controls +------------------------------ + +name='EMU10K1 PCM Volume',index 0-31 + +Channel volume attenuation in range 0-0xffff. The maximum value (no +attenuation) is default. The channel mapping for three values is +as follows: + + 0 - mono, default 0xffff (no attenuation) + 1 - left, default 0xffff (no attenuation) + 2 - right, default 0xffff (no attenuation) + +name='EMU10K1 PCM Send Routing',index 0-31 + +This control specifies the destination - FX-bus accumulators. There are +twelve values with this mapping: + + 0 - mono, A destination (FX-bus 0-15), default 0 + 1 - mono, B destination (FX-bus 0-15), default 1 + 2 - mono, C destination (FX-bus 0-15), default 2 + 3 - mono, D destination (FX-bus 0-15), default 3 + 4 - left, A destination (FX-bus 0-15), default 0 + 5 - left, B destination (FX-bus 0-15), default 1 + 6 - left, C destination (FX-bus 0-15), default 2 + 7 - left, D destination (FX-bus 0-15), default 3 + 8 - right, A destination (FX-bus 0-15), default 0 + 9 - right, B destination (FX-bus 0-15), default 1 + 10 - right, C destination (FX-bus 0-15), default 2 + 11 - right, D destination (FX-bus 0-15), default 3 + +Don't forget that it's illegal to assign a channel to the same FX-bus accumulator +more than once (it means 0=0 && 1=0 is an invalid combination). + +name='EMU10K1 PCM Send Volume',index 0-31 + +It specifies the attenuation (amount) for given destination in range 0-255. +The channel mapping is following: + + 0 - mono, A destination attn, default 255 (no attenuation) + 1 - mono, B destination attn, default 255 (no attenuation) + 2 - mono, C destination attn, default 0 (mute) + 3 - mono, D destination attn, default 0 (mute) + 4 - left, A destination attn, default 255 (no attenuation) + 5 - left, B destination attn, default 0 (mute) + 6 - left, C destination attn, default 0 (mute) + 7 - left, D destination attn, default 0 (mute) + 8 - right, A destination attn, default 0 (mute) + 9 - right, B destination attn, default 255 (no attenuation) + 10 - right, C destination attn, default 0 (mute) + 11 - right, D destination attn, default 0 (mute) + + + +4) MANUALS/PATENTS: +------------------- + +ftp://opensource.creative.com/pub/doc +------------------------------------- + + Files: + LM4545.pdf AC97 Codec + + m2049.pdf The EMU10K1 Digital Audio Processor + + hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects + + +WIPO Patents +------------ + Patent numbers: + WO 9901813 (A1) Audio Effects Processor with multiple asynchronous (Jan. 14, 1999) + streams + + WO 9901814 (A1) Processor with Instruction Set for Audio Effects (Jan. 14, 1999) + + WO 9901953 (A1) Audio Effects Processor having Decoupled Instruction + Execution and Audio Data Sequencing (Jan. 14, 1999) + + +US Patents (http://www.uspto.gov/) +---------------------------------- + + US 5925841 Digital Sampling Instrument employing cache memory (Jul. 20, 1999) + + US 5928342 Audio Effects Processor integrated on a single chip (Jul. 27, 1999) + with a multiport memory onto which multiple asynchronous + digital sound samples can be concurrently loaded + + US 5930158 Processor with Instruction Set for Audio Effects (Jul. 27, 1999) + + US 6032235 Memory initialization circuit (Tram) (Feb. 29, 2000) + + US 6138207 Interpolation looping of audio samples in cache connected to (Oct. 24, 2000) + system bus with prioritization and modification of bus transfers + in accordance with loop ends and minimum block sizes + + US 6151670 Method for conserving memory storage using a (Nov. 21, 2000) + pool of short term memory registers + + US 6195715 Interrupt control for multiple programs communicating with (Feb. 27, 2001) + a common interrupt by associating programs to GP registers, + defining interrupt register, polling GP registers, and invoking + callback routine associated with defined interrupt register diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/seq_oss.html linux/Documentation/ALSA/seq_oss.html --- linux-2.4.21-rc1.orig/Documentation/ALSA/seq_oss.html 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/seq_oss.html 2002-02-27 11:06:49.000000000 -0700 @@ -0,0 +1,409 @@ + + + + OSS Sequencer Emulation on ALSA + + + +
+

+ +

+ +
+

+OSS Sequencer Emulation on ALSA

+ +
+

Copyright (c) 1998,1999 by Takashi Iwai +<iwai@ww.uni-erlangen.de> +

ver.0.1.8; Nov. 16, 1999 +

+ +

+ +

+1. Description

+This directory contains the OSS sequencer emulation driver on ALSA. Note +that this program is still in the development state. +

What this does - it provides the emulation of the OSS sequencer, access +via +/dev/sequencer and /dev/music devices. +The most of applications using OSS can run if the appropriate ALSA +sequencer is prepared. +

The following features are emulated by this driver: +

    +
  • +Normal sequencer and MIDI events:
  • + +
    They are converted to the ALSA sequencer events, and sent to the corresponding +port. +
  • +Timer events:
  • + +
    The timer is not selectable by ioctl. The control rate is fixed to +100 regardless of HZ. That is, even on Alpha system, a tick is always +1/100 second. The base rate and tempo can be changed in /dev/music. + +
  • +Patch loading:
  • + +
    It purely depends on the synth drivers whether it's supported since +the patch loading is realized by callback to the synth driver. +
  • +I/O controls:
  • + +
    Most of controls are accepted. Some controls +are dependent on the synth driver, as well as even on original OSS.
+Furthermore, you can find the following advanced features: +
    +
  • +Better queue mechanism:
  • + +
    The events are queued before processing them. +
  • +Multiple applications:
  • + +
    You can run two or more applications simultaneously (even for OSS sequencer)! +However, each MIDI device is exclusive - that is, if a MIDI device is opened +once by some application, other applications can't use it. No such a restriction +in synth devices. +
  • +Real-time event processing:
  • + +
    The events can be processed in real time without using out of bound +ioctl. To switch to real-time mode, send ABSTIME 0 event. The followed +events will be processed in real-time without queued. To switch off the +real-time mode, send RELTIME 0 event. +
  • +/proc interface:
  • + +
    The status of applications and devices can be shown via /proc/asound/seq/oss +at any time. In the later version, configuration will be changed via /proc +interface, too.
+ +

+2. Installation

+Run configure script with both sequencer support (--with-sequencer=yes) +and OSS emulation (--with-oss=yes) options. A module snd-seq-oss.o +will be created. If the synth module of your sound card supports for OSS +emulation (so far, only Emu8000 driver), this module will be loaded automatically. +Otherwise, you need to load this module manually. +

At beginning, this module probes all the MIDI ports which have been +already connected to the sequencer. Once after that, the creation and deletion +of ports are watched by announcement mechanism of ALSA sequencer. +

The available synth and MIDI devices can be found in proc interface. +Run "cat /proc/asound/seq/oss", and check the devices. For example, +if you use an AWE64 card, you'll see like the following: +

        OSS sequencer emulation version 0.1.8
+        ALSA client number 63
+        ALSA receiver port 0
+
+        Number of applications: 0
+
+        Number of synth devices: 1
+
+        synth 0: [EMU8000]
+          type 0x1 : subtype 0x20 : voices 32
+          capabilties : ioctl enabled / load_patch enabled
+
+        Number of MIDI devices: 3
+
+        midi 0: [Emu8000 Port-0] ALSA port 65:0
+          capability write / opened none
+
+        midi 1: [Emu8000 Port-1] ALSA port 65:1
+          capability write / opened none
+
+        midi 2: [0: MPU-401 (UART)] ALSA port 64:0
+          capability read/write / opened none
+Note that the device number may be different from the information of +/proc/asound/oss-devices +or ones of the original OSS driver. Use the device number listed in /proc/asound/seq/oss +to play via OSS sequencer emulation. +

+3. Using Synthesizer Devices

+Run your favorite program. I've tested playmidi-2.4, awemidi-0.4.3, gmod-3.1 +and xmp-1.1.5. You can load samples via /dev/sequencer like sfxload, +too. +

If the lowlevel driver supports multiple access to synth devices (like +Emu8000 driver), two or more applications are allowed to run at the same +time. +

+4. Using MIDI Devices

+So far, only MIDI output was tested. MIDI input was not checked at all, +but hopefully it will work. Use the device number listed in /proc/asound/seq/oss. +Be aware that these numbers are mostly different from the list in +/proc/asound/oss-devices. +

+5. Module Options

+The following module options are available: +
    +
  • +maxqlen
  • + +
    specifies the maximum read/write queue length. This queue is private +for OSS sequencer, so that it is independent from the queue length of ALSA +sequencer. Default value is 1024. +
  • +seq_oss_debug
  • + +
    specifies the debug level and accepts zero (= no debug message) or +positive integer. Default value is 0.
+ +

+6. Queue Mechanism

+OSS sequencer emulation uses an ALSA priority queue. The +events from /dev/sequencer are processed and put onto the queue +specified by module option. +

All the events from /dev/sequencer are parsed at beginning. +The timing events are also parsed at this moment, so that the events may +be processed in real-time. Sending an event ABSTIME 0 switches the operation +mode to real-time mode, and sending an event RELTIME 0 switches it off. +In the real-time mode, all events are dispatched immediately. +

The queued events are dispatched to the corresponding ALSA sequencer +ports after scheduled time by ALSA sequencer dispatcher. +

If the write-queue is full, the application sleeps until a certain amount +(as default one half) becomes empty in blocking mode. The synchronization +to write timing was implemented, too. +

The input from MIDI devices or echo-back events are stored on read FIFO +queue. If application reads /dev/sequencer in blocking mode, the +process will be awaked. + +

+7. Interface to Synthesizer Device

+ +

+7.1. Registration

+To register an OSS synthesizer device, use snd_seq_oss_synth_register +function. +
int snd_seq_oss_synth_register(char *name, int type, int subtype, int nvoices,
+                              snd_seq_oss_callback_t *oper, void *private_data)
+The arguments name, type, subtype and +nvoices +are used for making the appropriate synth_info structure for ioctl. The +return value is an index number of this device. This index must be remembered +for unregister. If registration is failed, -errno will be returned. +

To release this device, call snd_seq_oss_synth_unregister function: +

int snd_seq_oss_synth_unregister(int index),
+where the index is the index number returned by register function. +

+7.2. Callbacks

+OSS synthesizer devices have capability for sample downloading and ioctls +like sample reset. In OSS emulation, these special features are realized +by using callbacks. The registration argument oper is used to specify these +callbacks. The following callback functions must be defined: +
snd_seq_oss_callback_t:
+        int (*open)(snd_seq_oss_arg_t *p, void *closure);
+        int (*close)(snd_seq_oss_arg_t *p);
+        int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
+        int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
+        int (*reset)(snd_seq_oss_arg_t *p);
+Except for open and close callbacks, they are allowed
+to be NULL.
+

Each callback function takes the argument type snd_seq_oss_arg_t as the +first argument. +

struct snd_seq_oss_arg_t {
+        int app_index;
+        int file_mode;
+        int seq_mode;
+        snd_seq_addr_t addr;
+        void *private_data;
+        int event_passing;
+};
+The first three fields, app_index, file_mode and +seq_mode +are initialized by OSS sequencer. The app_index is the application +index which is unique to each application opening OSS sequencer. The +file_mode +is bit-flags indicating the file operation mode. See +seq_oss.h +for its meaning. The seq_mode is sequencer operation mode. In +the current version, only SND_OSSSEQ_MODE_SYNTH is used. +

The next two fields, addr and private_data, must be +filled by the synth driver at open callback. The addr contains +the address of ALSA sequencer port which is assigned to this device. If +the driver allocates memory for private_data, it must be released +in close callback by itself. +

The last field, event_passing, indicates how to translate note-on +/ off events. In PROCESS_EVENTS mode, the note 255 is regarded +as velocity change, and key pressure event is passed to the port. In PASS_EVENTS +mode, all note on/off events are passed to the port without modified. PROCESS_KEYPRESS +mode checks the note above 128 and regards it as key pressure event (mainly +for Emu8000 driver). +

+7.2.1. Open Callback

+The open is called at each time this device is opened by an application +using OSS sequencer. This must not be NULL. Typically, the open callback +does the following procedure: +
    +
  1. +Allocate private data record.
  2. + +
  3. +Create an ALSA sequencer port.
  4. + +
  5. +Set the new port address on arg->addr.
  6. + +
  7. +Set the private data record pointer on arg->private_data.
  8. +
+Note that the type bit-flags in port_info of this synth port must NOT contain +TYPE_MIDI_GENERIC +bit. Instead, TYPE_SPECIFIC should be used. Also, CAP_SUBSCRIPTION +bit should NOT be included, too. This is necessary to tell it from other +normal MIDI devices. If the open procedure succeeded, return zero. Otherwise, +return -errno. +

+7.2.2 Ioctl Callback

+The ioctl callback is called when the sequencer receives device-specific +ioctls. The following two ioctls should be processed by this callback: +
    +
  • +IOCTL_SEQ_RESET_SAMPLES
  • + +
    reset all samples on memory -- return 0 +
  • +IOCTL_SYNTH_MEMAVL
  • + +
    return the available memory size +
  • +FM_4OP_ENABLE
  • + +
    can be ignored usually
+The other ioctls are processed inside the sequencer without passing to +the lowlevel driver. +

+7.2.3 Load_Patch Callback

+The load_patch callback is used for sample-downloading. This callback +must read the data on user-space and transfer to each device. Return 0 +if succeeded, and -errno if failed. The format argument is the patch key +in patch_info record. The buf is user-space pointer where patch_info record +is stored. The offs can be ignored. The count is total data size of this +sample data. +

+7.2.4 Close Callback

+The close callback is called when this device is closed by the +applicaion. If any private data was allocated in open callback, it must +be released in the close callback. The deletion of ALSA port should be +done here, too. This callback must not be NULL. +

+7.2.5 Reset Callback

+The reset callback is called when sequencer device is reset or +closed by applications. The callback should turn off the sounds on the +relevant port immediately, and initialize the status of the port. If this +callback is undefined, OSS seq sends a HEARTBEAT event to the +port. +

+7.3 Events

+Most of the events are processed by sequencer and translated to the adequate +ALSA sequencer events, so that each synth device can receive by input_event +callback of ALSA sequencer port. The following ALSA events should be implemented +by the driver: +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ALSA eventOriginal OSS events
NOTEONSEQ_NOTEON +
MIDI_NOTEON
NOTESEQ_NOTEOFF +
MIDI_NOTEOFF
KEYPRESSMIDI_KEY_PRESSURE
CHANPRESSSEQ_AFTERTOUCH +
MIDI_CHN_PRESSURE
PGMCHANGESEQ_PGMCHANGE +
MIDI_PGM_CHANGE
PITCHBENDSEQ_CONTROLLER(CTRL_PITCH_BENDER) +
MIDI_PITCH_BEND
CONTROLLERMIDI_CTL_CHANGE +
SEQ_BALANCE (with CTL_PAN)
CONTROL14SEQ_CONTROLLER
REGPARAMSEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)
SYSEXSEQ_SYSEX
+ +

The most of these behavior can be realized by MIDI emulation driver +included in the Emu8000 lowlevel driver. In the future release, this module +will be independent. +

Some OSS events (SEQ_PRIVATE and SEQ_VOLUME events) are passed as event +type SND_SEQ_OSS_PRIVATE. The OSS sequencer passes these event 8 byte +packets without any modification. The lowlevel driver should process these +events appropriately. +

+8. Interface to MIDI Device

+Since the OSS emulation probes the creation and deletion of ALSA MIDI sequencer +ports automatically by receiving announcement from ALSA sequencer, the +MIDI devices don't need to be registered explicitly like synth devices. +However, the MIDI port_info registered to ALSA sequencer must include a group +name SND_SEQ_GROUP_DEVICE and a capability-bit CAP_READ or +CAP_WRITE. Also, subscription capabilities, CAP_SUBS_READ or CAP_SUBS_WRITE, +must be defined, too. If these conditions are not satisfied, the port is not +registered as OSS sequencer MIDI device. +

The events via MIDI devices are parsed in OSS sequencer and converted +to the corresponding ALSA sequencer events. The input from MIDI sequencer +is also converted to MIDI byte events by OSS sequencer. This works just +a reverse way of seq_midi module. +

+9. Known Problems / TODO's

+ +
    +
  • +Patch loading via ALSA instrument layer is not implemented yet.
  • +
+ + + diff -urN linux-2.4.21-rc1.orig/Documentation/ALSA/serial-u16550.txt linux/Documentation/ALSA/serial-u16550.txt --- linux-2.4.21-rc1.orig/Documentation/ALSA/serial-u16550.txt 1969-12-31 17:00:00.000000000 -0700 +++ linux/Documentation/ALSA/serial-u16550.txt 2003-02-20 02:24:41.000000000 -0700 @@ -0,0 +1,88 @@ + + Serial UART 16450/16550 MIDI driver + =================================== + +The adaptor module parameter allows you to select either: + + 0 - Roland Soundcanvas support (default) + 1 - Midiator MS-124T support (1) + 2 - Midiator MS-124W S/A mode (2) + 3 - MS-124W M/B mode support (3) + 4 - Generic device with multiple input support (4) + +For the Midiator MS-124W, you must set the physical M-S and A-B +switches on the Midiator to match the driver mode you select. + +In Roland Soundcanvas mode, multiple ALSA raw MIDI substreams are supported +(midiCnD0-midiCnD15). Whenever you write to a different substream, the driver +sends the nonstandard MIDI command sequence F5 NN, where NN is the substream +number plus 1. Roland modules use this command to switch between different +"parts", so this feature lets you treat each part as a distinct raw MIDI +substream. The driver provides no way to send F5 00 (no selection) or to not +send the F5 NN command sequence at all; perhaps it ought to. + +Usage example for simple serial converter: + + /sbin/setserial /dev/ttyS0 none + /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 speed=115200 + +Usage example for Roland SoundCanvas with 4 MIDI ports: + + /sbin/setserial /dev/ttyS0 none + /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 outs=4 + +In MS-124T mode, one raw MIDI substream is supported (midiCnD0); the outs +module parameter is automatically set to 1. The driver sends the same data to +all four MIDI Out connectors. Set the A-B switch and the speed module +parameter to match (A=19200, B=9600). + +Usage example for MS-124T, with A-B switch in A position: + + /sbin/setserial /dev/ttyS0 none + /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=1 \ + speed=19200 + +In MS-124W S/A mode, one raw MIDI substream is supported (midiCnD0); +the outs module parameter is automatically set to 1. The driver sends +the same data to all four MIDI Out connectors at full MIDI speed. + +Usage example for S/A mode: + + /sbin/setserial /dev/ttyS0 none + /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=2 + +In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI substreams; +the outs module parameter is automatically set to 16. The substream +number gives a bitmask of which MIDI Out connectors the data should be +sent to, with midiCnD1 sending to Out 1, midiCnD2 to Out 2, midiCnD4 to +Out 3, and midiCnD8 to Out 4. Thus midiCnD15 sends the data to all 4 ports. +As a special case, midiCnD0 also sends to all ports, since it is not useful +to send the data to no ports. M/B mode has extra overhead to select the MIDI +Out for each byte, so the aggregate data rate across all four MIDI Outs is +at most one byte every 520 us, as compared with the full MIDI data rate of +one byte every 320 us per port. + +Usage example for M/B mode: + + /sbin/setserial /dev/ttyS0 none + /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=3 + +The MS-124W hardware's M/A mode is currently not supported. This mode allows +the MIDI Outs to act independently at double the aggregate throughput of M/B, +but does not allow sending the same byte simultaneously to multiple MIDI Outs. +The M/A protocol requires the driver to twiddle the modem control lines under +timing constraints, so it would be a bit more complicated to implement than +the other modes. + +Midiator models other than MS-124W and MS-124T are currently not supported. +Note that the suffix letter is significant; the MS-124 and MS-124B are not +compatible, nor are the other known models MS-101, MS-101B, MS-103, and MS-114. +I do have documentation (tim.mann@compaq.com) that partially covers these models, +but no units to experiment with. The MS-124W support is tested with a real unit. +The MS-124T support is untested, but should work. + +The Generic driver supports multiple input and output substreams over a single +serial port. Similar to Roland Soundcanvas mode, F5 NN is used to select the +appropriate input or output stream (depending on the data direction). +Additionally, the CTS signal is used to regulate the data flow. The number of +inputs is specified by the ins parameter. diff -urN linux-2.4.21-rc1.orig/Documentation/Configure.help linux/Documentation/Configure.help --- linux-2.4.21-rc1.orig/Documentation/Configure.help 2003-04-29 04:47:25.000000000 -0600 +++ linux/Documentation/Configure.help 2003-04-29 05:15:52.000000000 -0600 @@ -19663,6 +19663,303 @@ driver as a module, you can specify the number of presses at load time with "insmod button reboot_count=". +CONFIG_SOUND_SUPPORT + If you have a sound card in your computer, i.e. if it can say more + than an occasional beep, say Y. Be sure to have all the information + about your sound card and its configuration down (I/O port, + interrupt and DMA channel), because you will be asked for it. + + You want to read the Sound-HOWTO, available from + . General information about + the modular sound system is contained in the files + . The file + contains some slightly + outdated but still useful information as well. + + If you have a PnP sound card and you want to configure it at boot + time using the ISA PnP tools (read + ), then you need to + compile the sound card support as a module ( = code which can be + inserted in and removed from the running kernel whenever you want) + and load that module after the PnP configuration is finished. To do + this, say M here and read as well + as ; the module will be + called soundcore.o. + + I'm told that even without a sound card, you can make your computer + say more than an occasional beep, by programming the PC speaker. + Kernel patches and supporting utilities to do that are in the pcsp + package, available at . + +CONFIG_SND + Say 'Y' or 'M' to enable Advanced Linux Sound Architecture (ALSA) drivers. + You need to install also alsa-lib and alsa-utils packages available on + the ALSA website at . + +CONFIG_SND_SA11XX_UDA1341 + Say Y or M if you have a Compaq iPaq H3x00 handheld computer and want + to use its Philips UDA 1341 audio chip. + +CONFIG_SND_SEQUENCER + Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature + allows routing and enqueing MIDI events. Events can be processed at given + time. + +CONFIG_SND_SEQ_DUMMY + Say 'Y' or 'M' to enable dummy sequencer client. This client is a simple + midi-through client. All normal input events are redirected to output port + immediately. + +CONFIG_SND_OSSEMUL + Say 'Y' to enable OSS (Open Sound System) API emulation code. + +CONFIG_SND_MIXER_OSS + Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*). + +CONFIG_SND_PCM_OSS + Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*). + +CONFIG_SND_SEQUENCER_OSS + Say 'Y' or 'M' to enable OSS sequencer emulation (both /dev/sequencer and + /dev/music interfaces). + +CONFIG_SND_RTCTIMER + Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC + timer as precise timing source and maps the RTC timer to the ALSA's timer + interface. ALSA sequencer code can also use this timing source. + +CONFIG_SND_VERBOSE_PRINTK + Say 'Y' to enable verbose log messages. These messages will help to + identify source file and position containing printed messages. + +CONFIG_SND_DEBUG + Say 'Y' to enable ALSA debug code. + +CONFIG_SND_DEBUG_MEMORY + Say 'Y' to enable debugging of memory allocation. + +CONFIG_SND_DEBUG_DETECTION + Say 'Y' to enable debugging of hardware detection. + +CONFIG_SND_DUMMY + Say 'Y' or 'M' to include dummy driver. This driver does nothing, but + emulates various mixer controls and PCM devices. + +CONFIG_SND_VIRMIDI + Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to + connect applications using raw MIDI devices to sequencer. + +CONFIG_SND_MTPAV + Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport + MIDI adapter. + +CONFIG_SND_SERIAL_U16550 + Say 'Y' or 'M' to include support for MIDI serial port driver. It works + with serial UARTs 16550 and better. + +CONFIG_SND_MPU401 + Say 'Y' or 'M' to include support for MPU401 hardware using UART access. + +CONFIG_SND_AD1816A + Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or + compatible sound chips. + +CONFIG_SND_AD1848 + Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 + (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips + from Cirrus Logic, use CS4231, CS4232 or CS4236+ driver. + +CONFIG_SND_CS4231 + Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4232 + Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4236 + Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239 + chips from Cirrus Logic - Crystal Semiconductors. + +CONFIG_SND_ES968 + Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip. + +CONFIG_SND_ES1688 + Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips. + +CONFIG_SND_ES18XX + Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. + +CONFIG_SND_GUSCLASSIC + Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. + +CONFIG_SND_GUSEXTREME + Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. + +CONFIG_SND_GUSMAX + Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. + +CONFIG_SND_INTERWAVE + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, + Panasonic PCA761AW). + +CONFIG_SND_INTERWAVE_STB + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + with TEA6330T bass and treble regulator (UltraSound 32-Pro). + +CONFIG_SND_OPTI92X_AD1848 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + AD1848 codec. + +CONFIG_SND_OPTI92X_CS4231 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + CS4231 codec. + +CONFIG_SND_OPTI93X + Say 'Y' or 'M' to include support for Opti93x soundcards. + +CONFIG_SND_SB8 + Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit) + soundcards or 100% compatible from Creative. + +CONFIG_SND_SB16 + Say 'Y' or 'M' to include support for Sound Blaster 16 (including + Plug and Play version). + +CONFIG_SND_SBAWE + Say 'Y' or 'M' to include support for Sound Blaster AWE (including + Plug and Play version). + +CONFIG_SND_SB16_CSP + Say 'Y' to include support for CSP core. This special coprocessor + can do variable tasks like various compression and decompression + algorithms. + +CONFIG_SND_WAVEFRONT + Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez + and Tropez+ soundcards based on Wavefront chip. + +CONFIG_SND_ALS100 + Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110, + ALS120 and ALS200 soundcards. + +CONFIG_SND_AZT2320 + Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard. + +CONFIG_SND_CMI8330 + Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards. + +CONFIG_SND_DT019X + Say 'Y' or 'M' to include support for Diamond Technologies DT-019X and + Avance Logic ALS-007 soundcards. + +CONFIG_SND_OPL3SA2 + Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips. + +CONFIG_SND_SGALAXY + Say 'Y' or 'M' to include support for Aztech Sound Galaxy. + +CONFIG_SND_ALI5451 + Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core. + +CONFIG_SND_CS46XX + Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 / + CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips. + +CONFIG_SND_CS46XX_NEW_DSP + Say 'Y' to use a new DSP image for SPDIF and dual codecs. + +CONFIG_SND_CS4281 + Say 'Y' or 'M' to include support for Cirrus Logic CS4281. + +CONFIG_SND_EMU10K1 + Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!, + Audigy and E-mu APS (partially supported). + +CONFIG_SND_KORG1212 + Say 'Y' or 'M' to include support for Korg 1212IO. + +CONFIG_SND_NM256 + Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips. + +CONFIG_SND_RME96 + Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and + Digi96/8 PRO/PAD/PST. + +CONFIG_SND_RME32 + Say 'Y' or 'M' to include support for RME Digi32, Digi32/8 and + Digi32 PRO audio devices. + +CONFIG_SND_RME9652 + Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / + Digi9636) soundcards. + +CONFIG_SND_HDSP + Say 'Y' or 'M' to include support for RME Hammerfall DSP Audio + soundcards. + +CONFIG_SND_TRIDENT + Say 'Y' or 'M' to include support for Trident 4D-Wave DX/NX and + SiS 7018 soundcards. + +CONFIG_SND_YMFPCI + Say 'Y' or 'M' to include support for Yamaha PCI audio chips - + YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. + +CONFIG_SND_ALS4000 + Say 'Y' or 'M' to include support for Avance Logic ALS4000. + +CONFIG_SND_CMIPCI + Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI + soundcards. + +CONFIG_SND_ENS1370 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370. + +CONFIG_SND_ENS1371 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and + Sound Blaster PCI 64 or 128 soundcards. + +CONFIG_SND_ES1938 + Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946) + soundcard. + +CONFIG_SND_ES1968 + Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E. + +CONFIG_SND_MAESTRO3 + Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard. + +CONFIG_SND_FM801 + Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards. + +CONFIG_SND_ICE1712 + Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards. + +CONFIG_SND_ICE1724 + Say 'Y' or 'M' to include support for ICE1724 (Envy24HT) based soundcards, + such as the MidiMan M Audio - Revolution 7.1, and AMP Ltd AUDIO2000. + +CONFIG_SND_INTEL8X0 + Say 'Y' or 'M' to include support for Intel8x0 based soundcards, + SiS 7012, AMD768/8111 and NVidia NForce chips. + +CONFIG_SND_SONICVIBES + Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. + +CONFIG_SND_VIA686 + Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge. + +CONFIG_SND_VIA8233 + Say 'Y' or 'M' to include support for VIA VT8233 South Bridge. + +CONFIG_SND_USB_AUDIO + Say 'Y' or 'M' to include support for USB audio devices. + +CONFIG_SND_USB_MIDI + Say 'Y' or 'M' to include support for MIDI devices connected via USB. + Sound card support CONFIG_SOUND If you have a sound card in your computer, i.e. if it can say more diff -urN linux-2.4.21-rc1.orig/Makefile linux/Makefile --- linux-2.4.21-rc1.orig/Makefile 2003-04-29 04:47:25.000000000 -0600 +++ linux/Makefile 2003-04-29 04:50:47.000000000 -0600 @@ -125,7 +125,7 @@ NETWORKS =net/network.o LIBS =$(TOPDIR)/lib/lib.a -SUBDIRS =kernel drivers mm fs net ipc lib +SUBDIRS =kernel drivers mm fs net ipc lib sound DRIVERS-n := DRIVERS-y := @@ -159,6 +159,7 @@ endif DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o +DRIVERS-$(CONFIG_SND) += sound/sound.o DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o @@ -365,7 +366,7 @@ init/do_mounts.o: init/do_mounts.c include/config/MARKER $(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $@ $< -fs lib mm ipc kernel drivers net: dummy +fs lib mm ipc kernel drivers net sound: dummy $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@) TAGS: dummy diff -urN linux-2.4.21-rc1.orig/arch/alpha/config.in linux/arch/alpha/config.in --- linux-2.4.21-rc1.orig/arch/alpha/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/alpha/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -411,14 +411,8 @@ endmenu fi -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/arch/arm/config.in linux/arch/arm/config.in --- linux-2.4.21-rc1.orig/arch/arm/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/arm/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -615,14 +615,9 @@ "$CONFIG_ARCH_SHARK" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" -o \ "$CONFIG_PCI" = "y" ]; then - mainmenu_option next_comment - comment 'Sound' - tristate 'Sound card support' CONFIG_SOUND - if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in - fi - endmenu + # OSS / ALSA + source sound/Config.in fi source drivers/misc/Config.in diff -urN linux-2.4.21-rc1.orig/arch/cris/config.in linux/arch/cris/config.in --- linux-2.4.21-rc1.orig/arch/cris/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/cris/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -241,14 +241,8 @@ source fs/Config.in -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/arch/i386/config.in linux/arch/i386/config.in --- linux-2.4.21-rc1.orig/arch/i386/config.in 2003-04-29 04:47:22.000000000 -0600 +++ linux/arch/i386/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -457,14 +457,8 @@ endmenu fi -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/arch/ia64/config.in linux/arch/ia64/config.in --- linux-2.4.21-rc1.orig/arch/ia64/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/ia64/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -231,14 +231,9 @@ fi if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then - mainmenu_option next_comment - comment 'Sound' - tristate 'Sound card support' CONFIG_SOUND - if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in - fi - endmenu + # OSS / ALSA + source sound/Config.in source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/arch/m68k/config.in linux/arch/m68k/config.in --- linux-2.4.21-rc1.orig/arch/m68k/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/m68k/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -532,14 +532,8 @@ endmenu -mainmenu_option next_comment -comment 'Sound support' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/dmasound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in source fs/Config.in diff -urN linux-2.4.21-rc1.orig/arch/mips/config-shared.in linux/arch/mips/config-shared.in --- linux-2.4.21-rc1.orig/arch/mips/config-shared.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/mips/config-shared.in 2003-04-29 04:50:47.000000000 -0600 @@ -768,14 +768,8 @@ endmenu fi -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in if [ "$CONFIG_SGI_IP22" = "y" ]; then source drivers/sgi/Config.in diff -urN linux-2.4.21-rc1.orig/arch/parisc/config.in linux/arch/parisc/config.in --- linux-2.4.21-rc1.orig/arch/parisc/config.in 2002-11-28 16:53:10.000000000 -0700 +++ linux/arch/parisc/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -176,14 +176,8 @@ endmenu fi -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in if [ "$CONFIG_SUPERIO" = "y" ]; then source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/arch/ppc/config.in linux/arch/ppc/config.in --- linux-2.4.21-rc1.orig/arch/ppc/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/ppc/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -389,15 +389,8 @@ source fs/Config.in -mainmenu_option next_comment -comment 'Sound' -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/dmasound/Config.in - source drivers/sound/Config.in -fi - -endmenu +# OSS / ALSA +source sound/Config.in if [ "$CONFIG_8xx" = "y" ]; then source arch/ppc/8xx_io/Config.in diff -urN linux-2.4.21-rc1.orig/arch/ppc64/config.in linux/arch/ppc64/config.in --- linux-2.4.21-rc1.orig/arch/ppc64/config.in 2003-04-29 04:47:18.000000000 -0600 +++ linux/arch/ppc64/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -215,15 +215,9 @@ source fs/Config.in if [ "$CONFIG_PPC_ISERIES" != "y" ]; then -mainmenu_option next_comment -comment 'Sound' -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/dmasound/Config.in - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in source drivers/media/Config.in source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/arch/sh/config.in linux/arch/sh/config.in --- linux-2.4.21-rc1.orig/arch/sh/config.in 2003-04-29 04:47:19.000000000 -0600 +++ linux/arch/sh/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -367,15 +367,8 @@ endmenu fi - -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in mainmenu_option next_comment comment 'Kernel hacking' diff -urN linux-2.4.21-rc1.orig/arch/x86_64/config.in linux/arch/x86_64/config.in --- linux-2.4.21-rc1.orig/arch/x86_64/config.in 2003-04-29 04:47:19.000000000 -0600 +++ linux/arch/x86_64/config.in 2003-04-29 04:50:47.000000000 -0600 @@ -219,14 +219,8 @@ endmenu fi -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in -fi -endmenu +# OSS / ALSA +source sound/Config.in source drivers/usb/Config.in diff -urN linux-2.4.21-rc1.orig/drivers/ieee1394/oui.c linux/drivers/ieee1394/oui.c --- linux-2.4.21-rc1.orig/drivers/ieee1394/oui.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/drivers/ieee1394/oui.c 2003-04-30 00:15:10.000000000 -0600 @@ -0,0 +1,4943 @@ +/* Generated file for OUI database */ + +#include + +#ifdef CONFIG_IEEE1394_OUI_DB +struct oui_list_struct { + int oui; + char *name; +} oui_list[] = { + { 0x000000, "XEROX CORPORATION" }, + { 0x000001, "XEROX CORPORATION" }, + { 0x000002, "XEROX CORPORATION" }, + { 0x000003, "XEROX CORPORATION" }, + { 0x000004, "XEROX CORPORATION" }, + { 0x000005, "XEROX CORPORATION" }, + { 0x000006, "XEROX CORPORATION" }, + { 0x000007, "XEROX CORPORATION" }, + { 0x000008, "XEROX CORPORATION" }, + { 0x000009, "XEROX CORPORATION" }, + { 0x00000A, "OMRON TATEISI ELECTRONICS CO." }, + { 0x00000B, "MATRIX CORPORATION" }, + { 0x00000C, "CISCO SYSTEMS, INC." }, + { 0x00000D, "FIBRONICS LTD." }, + { 0x00000E, "FUJITSU LIMITED" }, + { 0x00000F, "NEXT, INC." }, + { 0x000010, "SYTEK INC." }, + { 0x000011, "NORMEREL SYSTEMES" }, + { 0x000012, "INFORMATION TECHNOLOGY LIMITED" }, + { 0x000013, "CAMEX" }, + { 0x000014, "NETRONIX" }, + { 0x000015, "DATAPOINT CORPORATION" }, + { 0x000016, "DU PONT PIXEL SYSTEMS ." }, + { 0x000017, "TEKELEC" }, + { 0x000018, "WEBSTER COMPUTER CORPORATION" }, + { 0x000019, "APPLIED DYNAMICS INTERNATIONAL" }, + { 0x00001A, "ADVANCED MICRO DEVICES" }, + { 0x00001B, "NOVELL INC." }, + { 0x00001C, "BELL TECHNOLOGIES" }, + { 0x00001D, "CABLETRON SYSTEMS, INC." }, + { 0x00001E, "TELSIST INDUSTRIA ELECTRONICA" }, + { 0x00001F, "Telco Systems, Inc." }, + { 0x000020, "DATAINDUSTRIER DIAB AB" }, + { 0x000021, "SUREMAN COMP. & COMMUN. CORP." }, + { 0x000022, "VISUAL TECHNOLOGY INC." }, + { 0x000023, "ABB INDUSTRIAL SYSTEMS AB" }, + { 0x000024, "CONNECT AS" }, + { 0x000025, "RAMTEK CORP." }, + { 0x000026, "SHA-KEN CO., LTD." }, + { 0x000027, "JAPAN RADIO COMPANY" }, + { 0x000028, "PRODIGY SYSTEMS CORPORATION" }, + { 0x000029, "IMC NETWORKS CORP." }, + { 0x00002A, "TRW - SEDD/INP" }, + { 0x00002B, "CRISP AUTOMATION, INC" }, + { 0x00002C, "AUTOTOTE LIMITED" }, + { 0x00002D, "CHROMATICS INC" }, + { 0x00002E, "SOCIETE EVIRA" }, + { 0x00002F, "TIMEPLEX INC." }, + { 0x000030, "VG LABORATORY SYSTEMS LTD" }, + { 0x000031, "QPSX COMMUNICATIONS PTY LTD" }, + { 0x000032, "Marconi plc" }, + { 0x000033, "EGAN MACHINERY COMPANY" }, + { 0x000034, "NETWORK RESOURCES CORPORATION" }, + { 0x000035, "SPECTRAGRAPHICS CORPORATION" }, + { 0x000036, "ATARI CORPORATION" }, + { 0x000037, "OXFORD METRICS LIMITED" }, + { 0x000038, "CSS LABS" }, + { 0x000039, "TOSHIBA CORPORATION" }, + { 0x00003A, "CHYRON CORPORATION" }, + { 0x00003B, "i Controls, Inc." }, + { 0x00003C, "AUSPEX SYSTEMS INC." }, + { 0x00003D, "UNISYS" }, + { 0x00003E, "SIMPACT" }, + { 0x00003F, "SYNTREX, INC." }, + { 0x000040, "APPLICON, INC." }, + { 0x000041, "ICE CORPORATION" }, + { 0x000042, "METIER MANAGEMENT SYSTEMS LTD." }, + { 0x000043, "MICRO TECHNOLOGY" }, + { 0x000044, "CASTELLE CORPORATION" }, + { 0x000045, "FORD AEROSPACE & COMM. CORP." }, + { 0x000046, "OLIVETTI NORTH AMERICA" }, + { 0x000047, "NICOLET INSTRUMENTS CORP." }, + { 0x000048, "SEIKO EPSON CORPORATION" }, + { 0x000049, "APRICOT COMPUTERS, LTD" }, + { 0x00004A, "ADC CODENOLL TECHNOLOGY CORP." }, + { 0x00004B, "ICL DATA OY" }, + { 0x00004C, "NEC CORPORATION" }, + { 0x00004D, "DCI CORPORATION" }, + { 0x00004E, "AMPEX CORPORATION" }, + { 0x00004F, "LOGICRAFT, INC." }, + { 0x000050, "RADISYS CORPORATION" }, + { 0x000051, "HOB ELECTRONIC GMBH & CO. KG" }, + { 0x000052, "Intrusion.com, Inc." }, + { 0x000053, "COMPUCORP" }, + { 0x000054, "MODICON, INC." }, + { 0x000055, "COMMISSARIAT A L`ENERGIE ATOM." }, + { 0x000056, "DR. B. STRUCK" }, + { 0x000057, "SCITEX CORPORATION LTD." }, + { 0x000058, "RACORE COMPUTER PRODUCTS INC." }, + { 0x000059, "HELLIGE GMBH" }, + { 0x00005A, "SysKonnect GmbH" }, + { 0x00005B, "ELTEC ELEKTRONIK AG" }, + { 0x00005C, "TELEMATICS INTERNATIONAL INC." }, + { 0x00005D, "CS TELECOM" }, + { 0x00005E, "USC INFORMATION SCIENCES INST" }, + { 0x00005F, "SUMITOMO ELECTRIC IND., LTD." }, + { 0x000060, "KONTRON ELEKTRONIK GMBH" }, + { 0x000061, "GATEWAY COMMUNICATIONS" }, + { 0x000062, "BULL HN INFORMATION SYSTEMS" }, + { 0x000063, "DR.ING.SEUFERT GMBH" }, + { 0x000064, "YOKOGAWA DIGITAL COMPUTER CORP" }, + { 0x000065, "NETWORK ASSOCIATES, INC." }, + { 0x000066, "TALARIS SYSTEMS, INC." }, + { 0x000067, "SOFT * RITE, INC." }, + { 0x000068, "ROSEMOUNT CONTROLS" }, + { 0x000069, "CONCORD COMMUNICATIONS INC" }, + { 0x00006A, "COMPUTER CONSOLES INC." }, + { 0x00006B, "SILICON GRAPHICS INC./MIPS" }, + { 0x00006D, "CRAY COMMUNICATIONS, LTD." }, + { 0x00006E, "ARTISOFT, INC." }, + { 0x00006F, "MADGE NETWORKS LTD." }, + { 0x000070, "HCL LIMITED" }, + { 0x000071, "ADRA SYSTEMS INC." }, + { 0x000072, "MINIWARE TECHNOLOGY" }, + { 0x000073, "SIECOR CORPORATION" }, + { 0x000074, "RICOH COMPANY LTD." }, + { 0x000075, "Nortel Networks" }, + { 0x000076, "ABEKAS VIDEO SYSTEM" }, + { 0x000077, "INTERPHASE CORPORATION" }, + { 0x000078, "LABTAM LIMITED" }, + { 0x000079, "NETWORTH INCORPORATED" }, + { 0x00007A, "DANA COMPUTER INC." }, + { 0x00007B, "RESEARCH MACHINES" }, + { 0x00007C, "AMPERE INCORPORATED" }, + { 0x00007D, "SUN MICROSYSTEMS, INC." }, + { 0x00007E, "CLUSTRIX CORPORATION" }, + { 0x00007F, "LINOTYPE-HELL AG" }, + { 0x000080, "CRAY COMMUNICATIONS A/S" }, + { 0x000081, "BAY NETWORKS" }, + { 0x000082, "LECTRA SYSTEMES SA" }, + { 0x000083, "TADPOLE TECHNOLOGY PLC" }, + { 0x000084, "SUPERNET" }, + { 0x000085, "CANON INC." }, + { 0x000086, "MEGAHERTZ CORPORATION" }, + { 0x000087, "HITACHI, LTD." }, + { 0x000088, "COMPUTER NETWORK TECH. CORP." }, + { 0x000089, "CAYMAN SYSTEMS INC." }, + { 0x00008A, "DATAHOUSE INFORMATION SYSTEMS" }, + { 0x00008B, "INFOTRON" }, + { 0x00008C, "ALLOY COMPUTER PRODUCTS, INC." }, + { 0x00008D, "VERDIX CORPORATION" }, + { 0x00008E, "SOLBOURNE COMPUTER, INC." }, + { 0x00008F, "RAYTHEON COMPANY" }, + { 0x000090, "MICROCOM" }, + { 0x000091, "ANRITSU CORPORATION" }, + { 0x000092, "COGENT DATA TECHNOLOGIES" }, + { 0x000093, "PROTEON INC." }, + { 0x000094, "ASANTE TECHNOLOGIES" }, + { 0x000095, "SONY TEKTRONIX CORP." }, + { 0x000096, "MARCONI ELECTRONICS LTD." }, + { 0x000097, "EPOCH SYSTEMS" }, + { 0x000098, "CROSSCOMM CORPORATION" }, + { 0x000099, "MTX, INC." }, + { 0x00009A, "RC COMPUTER A/S" }, + { 0x00009B, "INFORMATION INTERNATIONAL, INC" }, + { 0x00009C, "ROLM MIL-SPEC COMPUTERS" }, + { 0x00009D, "LOCUS COMPUTING CORPORATION" }, + { 0x00009E, "MARLI S.A." }, + { 0x00009F, "AMERISTAR TECHNOLOGIES INC." }, + { 0x0000A0, "TOKYO SANYO ELECTRIC CO. LTD." }, + { 0x0000A1, "MARQUETTE ELECTRIC CO." }, + { 0x0000A2, "BAY NETWORKS" }, + { 0x0000A3, "NETWORK APPLICATION TECHNOLOGY" }, + { 0x0000A4, "ACORN COMPUTERS LIMITED" }, + { 0x0000A5, "COMPATIBLE SYSTEMS CORP." }, + { 0x0000A6, "NETWORK GENERAL CORPORATION" }, + { 0x0000A7, "NETWORK COMPUTING DEVICES INC." }, + { 0x0000A8, "STRATUS COMPUTER INC." }, + { 0x0000A9, "NETWORK SYSTEMS CORP." }, + { 0x0000AA, "XEROX CORPORATION" }, + { 0x0000AB, "LOGIC MODELING CORPORATION" }, + { 0x0000AC, "CONWARE COMPUTER CONSULTING" }, + { 0x0000AD, "BRUKER INSTRUMENTS INC." }, + { 0x0000AE, "DASSAULT ELECTRONIQUE" }, + { 0x0000AF, "NUCLEAR DATA INSTRUMENTATION" }, + { 0x0000B0, "RND-RAD NETWORK DEVICES" }, + { 0x0000B1, "ALPHA MICROSYSTEMS INC." }, + { 0x0000B2, "TELEVIDEO SYSTEMS, INC." }, + { 0x0000B3, "CIMLINC INCORPORATED" }, + { 0x0000B4, "EDIMAX COMPUTER COMPANY" }, + { 0x0000B5, "DATABILITY SOFTWARE SYS. INC." }, + { 0x0000B6, "MICRO-MATIC RESEARCH" }, + { 0x0000B7, "DOVE COMPUTER CORPORATION" }, + { 0x0000B8, "SEIKOSHA CO., LTD." }, + { 0x0000B9, "MCDONNELL DOUGLAS COMPUTER SYS" }, + { 0x0000BA, "SIIG, INC." }, + { 0x0000BB, "TRI-DATA" }, + { 0x0000BC, "ALLEN-BRADLEY CO. INC." }, + { 0x0000BD, "MITSUBISHI CABLE COMPANY" }, + { 0x0000BE, "THE NTI GROUP" }, + { 0x0000BF, "SYMMETRIC COMPUTER SYSTEMS" }, + { 0x0000C0, "WESTERN DIGITAL CORPORATION" }, + { 0x0000C1, "Madge Networks Ltd." }, + { 0x0000C2, "INFORMATION PRESENTATION TECH." }, + { 0x0000C3, "HARRIS CORP COMPUTER SYS DIV" }, + { 0x0000C4, "WATERS DIV. OF MILLIPORE" }, + { 0x0000C5, "FARALLON COMPUTING/NETOPIA" }, + { 0x0000C6, "EON SYSTEMS" }, + { 0x0000C7, "ARIX CORPORATION" }, + { 0x0000C8, "ALTOS COMPUTER SYSTEMS" }, + { 0x0000C9, "EMULEX CORPORATION" }, + { 0x0000CA, "APPLITEK" }, + { 0x0000CB, "COMPU-SHACK ELECTRONIC GMBH" }, + { 0x0000CC, "DENSAN CO., LTD." }, + { 0x0000CD, "Centrecom Systems, Ltd." }, + { 0x0000CE, "MEGADATA CORP." }, + { 0x0000CF, "HAYES MICROCOMPUTER PRODUCTS" }, + { 0x0000D0, "DEVELCON ELECTRONICS LTD." }, + { 0x0000D1, "ADAPTEC INCORPORATED" }, + { 0x0000D2, "SBE, INC." }, + { 0x0000D3, "WANG LABORATORIES INC." }, + { 0x0000D4, "PURE DATA LTD." }, + { 0x0000D5, "MICROGNOSIS INTERNATIONAL" }, + { 0x0000D6, "PUNCH LINE HOLDING" }, + { 0x0000D7, "DARTMOUTH COLLEGE" }, + { 0x0000D8, "NOVELL, INC." }, + { 0x0000D9, "NIPPON TELEGRAPH & TELEPHONE" }, + { 0x0000DA, "ATEX" }, + { 0x0000DB, "BRITISH TELECOMMUNICATIONS PLC" }, + { 0x0000DC, "HAYES MICROCOMPUTER PRODUCTS" }, + { 0x0000DD, "TCL INCORPORATED" }, + { 0x0000DE, "CETIA" }, + { 0x0000DF, "BELL & HOWELL PUB SYS DIV" }, + { 0x0000E0, "QUADRAM CORP." }, + { 0x0000E1, "GRID SYSTEMS" }, + { 0x0000E2, "ACER TECHNOLOGIES CORP." }, + { 0x0000E3, "INTEGRATED MICRO PRODUCTS LTD" }, + { 0x0000E4, "IN2 GROUPE INTERTECHNIQUE" }, + { 0x0000E5, "SIGMEX LTD." }, + { 0x0000E6, "APTOR PRODUITS DE COMM INDUST" }, + { 0x0000E7, "STAR GATE TECHNOLOGIES" }, + { 0x0000E8, "ACCTON TECHNOLOGY CORP." }, + { 0x0000E9, "ISICAD, INC." }, + { 0x0000EA, "UPNOD AB" }, + { 0x0000EB, "MATSUSHITA COMM. IND. CO. LTD." }, + { 0x0000EC, "MICROPROCESS" }, + { 0x0000ED, "APRIL" }, + { 0x0000EE, "NETWORK DESIGNERS, LTD." }, + { 0x0000EF, "KTI" }, + { 0x0000F0, "SAMSUNG ELECTRONICS CO., LTD." }, + { 0x0000F1, "MAGNA COMPUTER CORPORATION" }, + { 0x0000F2, "SPIDER COMMUNICATIONS" }, + { 0x0000F3, "GANDALF DATA LIMITED" }, + { 0x0000F4, "ALLIED TELESYN INTERNATIONAL" }, + { 0x0000F5, "DIAMOND SALES LIMITED" }, + { 0x0000F6, "APPLIED MICROSYSTEMS CORP." }, + { 0x0000F7, "YOUTH KEEP ENTERPRISE CO LTD" }, + { 0x0000F8, "DIGITAL EQUIPMENT CORPORATION" }, + { 0x0000F9, "QUOTRON SYSTEMS INC." }, + { 0x0000FA, "MICROSAGE COMPUTER SYSTEMS INC" }, + { 0x0000FB, "RECHNER ZUR KOMMUNIKATION" }, + { 0x0000FC, "MEIKO" }, + { 0x0000FD, "HIGH LEVEL HARDWARE" }, + { 0x0000FE, "ANNAPOLIS MICRO SYSTEMS" }, + { 0x0000FF, "CAMTEC ELECTRONICS LTD." }, + { 0x000100, "EQUIP'TRANS" }, + { 0x000102, "3COM CORPORATION" }, + { 0x000103, "3COM CORPORATION" }, + { 0x000104, "DVICO Co., Ltd." }, + { 0x000105, "BECKHOFF GmbH" }, + { 0x000106, "Tews Datentechnik GmbH" }, + { 0x000107, "Leiser GmbH" }, + { 0x000108, "AVLAB Technology, Inc." }, + { 0x000109, "Nagano Japan Radio Co., Ltd." }, + { 0x00010A, "CIS TECHNOLOGY INC." }, + { 0x00010B, "Space CyberLink, Inc." }, + { 0x00010C, "System Talks Inc." }, + { 0x00010D, "CORECO, INC." }, + { 0x00010E, "Bri-Link Technologies Co., Ltd" }, + { 0x00010F, "Nishan Systems, Inc." }, + { 0x000110, "Gotham Networks" }, + { 0x000111, "iDigm Inc." }, + { 0x000112, "Shark Multimedia Inc." }, + { 0x000113, "OLYMPUS OPTICAL CO., LTD." }, + { 0x000114, "KANDA TSUSHIN KOGYO CO., LTD." }, + { 0x000115, "EXTRATECH CORPORATION" }, + { 0x000116, "Netspect Technologies, Inc." }, + { 0x000117, "CANAL +" }, + { 0x000118, "EZ Digital Co., Ltd." }, + { 0x000119, "Action Controls Pty. Ltd." }, + { 0x00011A, "EEH DataLink GmbH" }, + { 0x00011B, "Unizone Technologies, Inc." }, + { 0x00011C, "Universal Talkware Corporation" }, + { 0x00011D, "Centillium Communications" }, + { 0x00011E, "Precidia Technologies, Inc." }, + { 0x00011F, "RC Networks, Inc." }, + { 0x000120, "OSCILLOQUARTZ S.A." }, + { 0x000121, "RapidStream Inc." }, + { 0x000122, "Trend Communications, Ltd." }, + { 0x000123, "DIGITAL ELECTRONICS CORP." }, + { 0x000124, "Acer Incorporated" }, + { 0x000125, "YAESU MUSEN CO., LTD." }, + { 0x000126, "PAC Labs" }, + { 0x000127, "The OPEN Group Limited" }, + { 0x000128, "EnjoyWeb, Inc." }, + { 0x000129, "DFI Inc." }, + { 0x00012A, "Telematica Sistems Inteligente" }, + { 0x00012B, "TELENET Co., Ltd." }, + { 0x00012C, "Aravox Technologies, Inc." }, + { 0x00012D, "Komodo Technology" }, + { 0x00012E, "PC Partner Ltd." }, + { 0x00012F, "Twinhead International Corp" }, + { 0x000130, "Extreme Networks" }, + { 0x000131, "Detection Systems, Inc." }, + { 0x000132, "Dranetz - BMI" }, + { 0x000133, "KYOWA Electronic Instruments C" }, + { 0x000134, "SIG Positec Systems AG" }, + { 0x000135, "KDC Corp." }, + { 0x000136, "CyberTAN Technology, Inc." }, + { 0x000137, "IT Farm Corporation" }, + { 0x000138, "XAVi Technologies Corp." }, + { 0x000139, "Point Multimedia Systems" }, + { 0x00013A, "SHELCAD COMMUNICATIONS, LTD." }, + { 0x00013B, "BNA SYSTEMS" }, + { 0x00013C, "TIW SYSTEMS" }, + { 0x00013D, "RiscStation Ltd." }, + { 0x00013E, "Ascom Tateco AB" }, + { 0x00013F, "Neighbor World Co., Ltd." }, + { 0x000140, "Sendtek Corporation" }, + { 0x000141, "CABLE PRINT" }, + { 0x000142, "Cisco Systems, Inc." }, + { 0x000143, "Cisco Systems, Inc." }, + { 0x000144, "Cereva Networks, Inc." }, + { 0x000145, "WINSYSTEMS, INC." }, + { 0x000146, "Tesco Controls, Inc." }, + { 0x000147, "Zhone Technologies" }, + { 0x000148, "X-traWeb Inc." }, + { 0x000149, "T.D.T. Transfer Data Test GmbH" }, + { 0x00014A, "SONY COMPUTER SCIENCE LABS., I" }, + { 0x00014B, "Ennovate Networks, Inc." }, + { 0x00014C, "Berkeley Process Control" }, + { 0x00014D, "Shin Kin Enterprises Co., Ltd" }, + { 0x00014E, "WIN Enterprises, Inc." }, + { 0x00014F, "LUMINOUS Networks, Inc." }, + { 0x000150, "GILAT COMMUNICATIONS, LTD." }, + { 0x000151, "Ensemble Communications" }, + { 0x000152, "CHROMATEK INC." }, + { 0x000153, "ARCHTEK TELECOM CORPORATION" }, + { 0x000154, "G3M Corporation" }, + { 0x000155, "Promise Technology, Inc." }, + { 0x000156, "FIREWIREDIRECT.COM, INC." }, + { 0x000157, "SYSWAVE CO., LTD" }, + { 0x000158, "Electro Industries/Gauge Tech" }, + { 0x000159, "S1 Corporation" }, + { 0x00015A, "Digital Video Broadcasting" }, + { 0x00015B, "ITALTEL S.p.A/RF-UP-I" }, + { 0x00015C, "CADANT INC." }, + { 0x00015D, "Pirus Networks" }, + { 0x00015E, "BEST TECHNOLOGY CO., LTD." }, + { 0x00015F, "DIGITAL DESIGN GmbH" }, + { 0x000160, "ELMEX Co., LTD." }, + { 0x000161, "Meta Machine Technology" }, + { 0x000162, "Cygnet Technologies, Inc." }, + { 0x000163, "Cisco Systems, Inc." }, + { 0x000164, "Cisco Systems, Inc." }, + { 0x000165, "AirSwitch Corporation" }, + { 0x000166, "TC GROUP A/S" }, + { 0x000167, "HIOKI E.E. CORPORATION" }, + { 0x000168, "VITANA CORPORATION" }, + { 0x000169, "Celestix Networks Pte Ltd." }, + { 0x00016A, "ALITEC" }, + { 0x00016B, "LightChip, Inc." }, + { 0x00016C, "FOXCONN" }, + { 0x00016D, "Triton Network Systems" }, + { 0x00016E, "Conklin Corporation" }, + { 0x00016F, "HAITAI ELECTRONICS CO., LTD." }, + { 0x000170, "ESE Embedded System Engineer'g" }, + { 0x000171, "Allied Data Technologies" }, + { 0x000172, "TechnoLand Co., LTD." }, + { 0x000173, "JNI Corporation" }, + { 0x000174, "CyberOptics Corporation" }, + { 0x000175, "Radiant Communications Corp." }, + { 0x000176, "Orient Silver Enterprises" }, + { 0x000177, "EDSL" }, + { 0x000178, "MARGI Systems, Inc." }, + { 0x000179, "WIRELESS TECHNOLOGY, INC." }, + { 0x00017A, "Chengdu Maipu Electric Industrial Co., Ltd." }, + { 0x00017B, "Heidelberger Druckmaschinen AG" }, + { 0x00017C, "AG-E GmbH" }, + { 0x00017D, "ThermoQuest" }, + { 0x00017E, "ADTEK System Science Co., Ltd." }, + { 0x00017F, "Experience Music Project" }, + { 0x000180, "AOpen, Inc." }, + { 0x000181, "Nortel Networks" }, + { 0x000182, "DICA TECHNOLOGIES AG" }, + { 0x000183, "ANITE TELECOMS" }, + { 0x000184, "SIEB & MEYER AG" }, + { 0x000185, "Aloka Co., Ltd." }, + { 0x000186, "DISCH GmbH" }, + { 0x000187, "i2SE GmbH" }, + { 0x000188, "LXCO Technologies ag" }, + { 0x000189, "Refraction Technology, Inc." }, + { 0x00018A, "ROI COMPUTER AG" }, + { 0x00018B, "NetLinks Co., Ltd." }, + { 0x00018C, "Mega Vision" }, + { 0x00018D, "AudeSi Technologies" }, + { 0x00018E, "Logitec Corporation" }, + { 0x00018F, "Kenetec, Inc." }, + { 0x000190, "SMK-M" }, + { 0x000191, "SYRED Data Systems" }, + { 0x000192, "Texas Digital Systems" }, + { 0x000193, "Hanbyul Telecom Co., Ltd." }, + { 0x000194, "Capital Equipment Corporation" }, + { 0x000195, "Sena Technologies, Inc." }, + { 0x000196, "Cisco Systems, Inc." }, + { 0x000197, "Cisco Systems, Inc." }, + { 0x000198, "Darim Vision" }, + { 0x000199, "HeiSei Electronics" }, + { 0x00019A, "LEUNIG GmbH" }, + { 0x00019B, "Kyoto Microcomputer Co., Ltd." }, + { 0x00019C, "JDS Uniphase Inc." }, + { 0x00019D, "E-Control Systems, Inc." }, + { 0x00019E, "ESS Technology, Inc." }, + { 0x00019F, "Phonex Broadband" }, + { 0x0001A0, "Infinilink Corporation" }, + { 0x0001A1, "Mag-Tek, Inc." }, + { 0x0001A2, "Logical Co., Ltd." }, + { 0x0001A3, "GENESYS LOGIC, INC." }, + { 0x0001A4, "Microlink Corporation" }, + { 0x0001A5, "Nextcomm, Inc." }, + { 0x0001A6, "Scientific-Atlanta Arcodan A/S" }, + { 0x0001A7, "UNEX TECHNOLOGY CORPORATION" }, + { 0x0001A8, "Welltech Computer Co., Ltd." }, + { 0x0001A9, "BMW AG" }, + { 0x0001AA, "Airspan Communications, Ltd." }, + { 0x0001AB, "Main Street Networks" }, + { 0x0001AC, "Sitara Networks, Inc." }, + { 0x0001AD, "Coach Master International d.b.a. CMI Worldwide, Inc." }, + { 0x0001AE, "Trex Enterprises" }, + { 0x0001AF, "Motorola Computer Group" }, + { 0x0001B0, "Fulltek Technology Co., Ltd." }, + { 0x0001B1, "General Bandwidth" }, + { 0x0001B2, "Digital Processing Systems, Inc." }, + { 0x0001B3, "Precision Electronic Manufacturing" }, + { 0x0001B4, "Wayport, Inc." }, + { 0x0001B5, "Turin Networks, Inc." }, + { 0x0001B6, "SAEJIN T&M Co., Ltd." }, + { 0x0001B7, "Centos, Inc." }, + { 0x0001B8, "Netsensity, Inc." }, + { 0x0001B9, "SKF Condition Monitoring" }, + { 0x0001BA, "IC-Net, Inc." }, + { 0x0001BB, "Frequentis" }, + { 0x0001BC, "Brains Corporation" }, + { 0x0001BD, "Peterson Electro-Musical Products, Inc." }, + { 0x0001BE, "Gigalink Co., Ltd." }, + { 0x0001BF, "Teleforce Co., Ltd." }, + { 0x0001C0, "CompuLab, Ltd." }, + { 0x0001C1, "Exbit Technology" }, + { 0x0001C2, "ARK Research Corp." }, + { 0x0001C3, "Acromag, Inc." }, + { 0x0001C4, "NeoWave, Inc." }, + { 0x0001C5, "Simpler Networks" }, + { 0x0001C6, "Quarry Technologies" }, + { 0x0001C7, "Cisco Systems, Inc." }, + { 0x0001C8, "THOMAS CONRAD CORP." }, + { 0x0001C8, "CONRAD CORP." }, + { 0x0001C9, "Cisco Systems, Inc." }, + { 0x0001CA, "Geocast Network Systems, Inc." }, + { 0x0001CB, "NetGame, Ltd." }, + { 0x0001CC, "Japan Total Design Communication Co., Ltd." }, + { 0x0001CD, "ARtem" }, + { 0x0001CE, "Custom Micro Products, Ltd." }, + { 0x0001CF, "Alpha Data Parallel Systems, Ltd." }, + { 0x0001D0, "VitalPoint, Inc." }, + { 0x0001D1, "CoNet Communications, Inc." }, + { 0x0001D2, "MacPower Peripherals, Ltd." }, + { 0x0001D3, "PAXCOMM, Inc." }, + { 0x0001D4, "Leisure Time, Inc." }, + { 0x0001D5, "HAEDONG INFO & COMM CO., LTD" }, + { 0x0001D6, "MAN Roland Druckmaschinen AG" }, + { 0x0001D7, "F5 Networks, Inc." }, + { 0x0001D8, "Teltronics, Inc." }, + { 0x0001D9, "Sigma, Inc." }, + { 0x0001DA, "WINCOMM Corporation" }, + { 0x0001DB, "Freecom Technologies GmbH" }, + { 0x0001DC, "Activetelco" }, + { 0x0001DD, "Avail Networks" }, + { 0x0001DE, "Trango Systems, Inc." }, + { 0x0001DF, "ISDN Communications, Ltd." }, + { 0x0001E0, "Fast Systems, Inc." }, + { 0x0001E1, "Kinpo Electronics, Inc." }, + { 0x0001E2, "Ando Electric Corporation" }, + { 0x0001E3, "Siemens AG" }, + { 0x0001E4, "Sitera, Inc." }, + { 0x0001E5, "Supernet, Inc." }, + { 0x0001E6, "Hewlett-Packard Company" }, + { 0x0001E7, "Hewlett-Packard Company" }, + { 0x0001E8, "Force10 Networks, Inc." }, + { 0x0001E9, "Litton Marine Systems B.V." }, + { 0x0001EA, "Cirilium Corp." }, + { 0x0001EB, "C-COM Corporation" }, + { 0x0001EC, "Ericsson Group" }, + { 0x0001ED, "SETA Corp." }, + { 0x0001EE, "Comtrol Europe, Ltd." }, + { 0x0001EF, "Camtel Technology Corp." }, + { 0x0001F0, "Tridium, Inc." }, + { 0x0001F1, "Innovative Concepts, Inc." }, + { 0x0001F3, "QPS, Inc." }, + { 0x0001F4, "Enterasys Networks" }, + { 0x0001F5, "ERIM S.A." }, + { 0x0001F6, "Association of Musical Electronics Industry" }, + { 0x0001F7, "Image Display Systems, Inc." }, + { 0x0001F8, "Adherent Systems, Ltd." }, + { 0x0001F9, "TeraGlobal Communications Corp." }, + { 0x0001FA, "HOROSCAS" }, + { 0x0001FB, "DoTop Technology, Inc." }, + { 0x0001FC, "Keyence Corporation" }, + { 0x0001FD, "Digital Voice Systems, Inc." }, + { 0x0001FE, "DIGITAL EQUIPMENT CORPORATION" }, + { 0x0001FF, "Data Direct Networks, Inc." }, + { 0x000200, "Net & Sys Co., Ltd." }, + { 0x000201, "IFM Electronic gmbh" }, + { 0x000202, "Amino Communications, Ltd." }, + { 0x000203, "Woonsang Telecom, Inc." }, + { 0x000204, "Bodmann Industries Elektronik GmbH" }, + { 0x000205, "Hitachi Denshi, Ltd." }, + { 0x000206, "Telital R&D Denmark A/S" }, + { 0x000208, "Unify Networks, Inc." }, + { 0x000209, "Shenzhen SED Information Technology Co., Ltd." }, + { 0x00020A, "Gefran Spa" }, + { 0x00020B, "Native Networks, Inc." }, + { 0x00020C, "Metro-Optix" }, + { 0x00020D, "Micronpc.com" }, + { 0x00020E, "Laurel Networks, Inc." }, + { 0x00020F, "AATR" }, + { 0x000210, "Fenecom" }, + { 0x000211, "Nature Worldwide Technology Corp." }, + { 0x000212, "SierraCom" }, + { 0x000213, "S.D.E.L." }, + { 0x000214, "DTVRO" }, + { 0x000215, "Cotas Computer Technology A/B" }, + { 0x000216, "Cisco Systems, Inc." }, + { 0x000217, "Cisco Systems, Inc." }, + { 0x000218, "Advanced Scientific Corp" }, + { 0x000219, "Paralon Technologies" }, + { 0x00021A, "Zuma Networks" }, + { 0x00021B, "Kollmorgen-Servotronix" }, + { 0x00021C, "Network Elements, Inc." }, + { 0x00021D, "Data General Communication Ltd." }, + { 0x00021E, "SIMTEL S.R.L." }, + { 0x00021F, "Aculab PLC" }, + { 0x000220, "Canon Aptex, Inc." }, + { 0x000221, "DSP Application, Ltd." }, + { 0x000222, "Chromisys, Inc." }, + { 0x000223, "ClickTV" }, + { 0x000224, "Lantern Communications, Inc." }, + { 0x000225, "Certus Technology, Inc." }, + { 0x000226, "XESystems, Inc." }, + { 0x000227, "ESD GmbH" }, + { 0x000228, "Necsom, Ltd." }, + { 0x000229, "Adtec Corporation" }, + { 0x00022A, "Asound Electronic" }, + { 0x00022B, "Tamura Electric Works, Ltd." }, + { 0x00022C, "ABB Bomem, Inc." }, + { 0x00022D, "Agere Systems" }, + { 0x00022E, "TEAC Corp. R& D" }, + { 0x00022F, "P-Cube, Ltd." }, + { 0x000230, "Intersoft Electronics" }, + { 0x000231, "Ingersoll-Rand" }, + { 0x000232, "Avision, Inc." }, + { 0x000233, "Mantra Communications, Inc." }, + { 0x000234, "Imperial Technology, Inc." }, + { 0x000235, "Paragon Networks International" }, + { 0x000236, "INIT GmbH" }, + { 0x000237, "Cosmo Research Corp." }, + { 0x000238, "Serome Technology, Inc." }, + { 0x000239, "Visicom" }, + { 0x00023A, "ZSK Stickmaschinen GmbH" }, + { 0x00023B, "Redback Networks" }, + { 0x00023C, "Creative Technology, Ltd." }, + { 0x00023D, "NuSpeed, Inc." }, + { 0x00023E, "Selta Telematica S.p.a" }, + { 0x00023F, "Compal Electronics, Inc." }, + { 0x000240, "Seedek Co., Ltd." }, + { 0x000241, "Amer.com" }, + { 0x000242, "Videoframe Systems" }, + { 0x000243, "Raysis Co., Ltd." }, + { 0x000244, "SURECOM Technology Co." }, + { 0x000245, "Lampus Co, Ltd." }, + { 0x000246, "All-Win Tech Co., Ltd." }, + { 0x000247, "Great Dragon Information Technology (Group) Co., Ltd." }, + { 0x000248, "Pila GmbH & Co." }, + { 0x000249, "Aviv Infocom Co, Ltd." }, + { 0x00024A, "Cisco Systems, Inc." }, + { 0x00024B, "Cisco Systems, Inc." }, + { 0x00024C, "SiByte, Inc." }, + { 0x00024D, "Mannesman Dematic Colby Pty. Ltd." }, + { 0x00024E, "Datacard Group" }, + { 0x00024F, "IPM Datacom S.R.L." }, + { 0x000250, "Geyser Networks, Inc." }, + { 0x000251, "Soma Networks" }, + { 0x000252, "Carrier Corporation" }, + { 0x000253, "Televideo, Inc." }, + { 0x000254, "WorldGate" }, + { 0x000255, "IBM Corporation" }, + { 0x000256, "Alpha Processor, Inc." }, + { 0x000257, "Microcom Corp." }, + { 0x000258, "Flying Packets Communications" }, + { 0x000259, "Tsann Kuen China (Shanghai)Enterprise Co., Ltd. IT Group" }, + { 0x00025A, "Catena Networks" }, + { 0x00025B, "Cambridge Silicon Radio" }, + { 0x00025C, "SCI Systems (Kunshan) Co., Ltd." }, + { 0x00025E, "High Technology Ltd" }, + { 0x00025F, "Nortel Networks" }, + { 0x000260, "Accordion Networks, Inc." }, + { 0x000261, "i3 Micro Technology AB" }, + { 0x000262, "Soyo Group Soyo Com Tech Co., Ltd" }, + { 0x000263, "UPS Manufacturing SRL" }, + { 0x000264, "AudioRamp.com" }, + { 0x000265, "Virditech Co. Ltd." }, + { 0x000266, "Thermalogic Corporation" }, + { 0x000267, "NODE RUNNER, INC." }, + { 0x000268, "Harris Government Communications" }, + { 0x000269, "Nadatel Co., Ltd" }, + { 0x00026A, "Cocess Telecom Co., Ltd." }, + { 0x00026B, "BCM Computers Co., Ltd." }, + { 0x00026C, "Philips CFT" }, + { 0x00026D, "Adept Telecom" }, + { 0x00026E, "NeGeN Access, Inc." }, + { 0x00026F, "Senao International Co., Ltd." }, + { 0x000270, "Crewave Co., Ltd." }, + { 0x000271, "Vpacket Communications" }, + { 0x000272, "CC&C Technologies, Inc." }, + { 0x000273, "Coriolis Networks" }, + { 0x000274, "Tommy Technologies Corp." }, + { 0x000275, "SMART Technologies, Inc." }, + { 0x000276, "Primax Electronics Ltd." }, + { 0x000277, "Cash Systemes Industrie" }, + { 0x000278, "Samsung Electro-Mechanics Co., Ltd." }, + { 0x000279, "Control Applications, Ltd." }, + { 0x00027A, "IOI Technology Corporation" }, + { 0x00027B, "Amplify Net, Inc." }, + { 0x00027C, "Trilithic, Inc." }, + { 0x00027D, "Cisco Systems, Inc." }, + { 0x00027E, "Cisco Systems, Inc." }, + { 0x00027F, "ask-technologies.com" }, + { 0x000280, "Mu Net, Inc." }, + { 0x000281, "Red-M (Communications) Ltd." }, + { 0x000282, "ViaClix, Inc." }, + { 0x000283, "Spectrum Controls, Inc." }, + { 0x000284, "Alstom T&D P&C" }, + { 0x000285, "Riverstone Networks" }, + { 0x000286, "Occam Networks" }, + { 0x000287, "Adapcom" }, + { 0x000288, "GLOBAL VILLAGE COMMUNICATION" }, + { 0x000289, "DNE Technologies" }, + { 0x00028A, "Ambit Microsystems Corporation" }, + { 0x00028B, "VDSL Systems OY" }, + { 0x00028C, "Micrel-Synergy Semiconductor" }, + { 0x00028D, "Movita Technologies, Inc." }, + { 0x00028E, "Rapid 5 Networks, Inc." }, + { 0x00028F, "Globetek, Inc." }, + { 0x000290, "Woorigisool, Inc." }, + { 0x000291, "Open Network Co., Ltd." }, + { 0x000292, "Logic Innovations, Inc." }, + { 0x000293, "Solid Data Systems" }, + { 0x000294, "Tokyo Sokushin Co., Ltd." }, + { 0x000295, "IP.Access Limited" }, + { 0x000296, "Lectron Co,. Ltd." }, + { 0x000297, "C-COR.net" }, + { 0x000298, "Broadframe Corporation" }, + { 0x000299, "Apex, Inc." }, + { 0x00029A, "Storage Apps" }, + { 0x00029B, "Kreatel Communications AB" }, + { 0x00029D, "Merix Corp." }, + { 0x00029E, "Information Equipment Co., Ltd." }, + { 0x00029F, "L-3 Communication Aviation Recorders" }, + { 0x0002A0, "Flatstack Ltd." }, + { 0x0002A1, "World Wide Packets" }, + { 0x0002A2, "Hilscher GmbH" }, + { 0x0002A3, "ABB Power Automation" }, + { 0x0002A4, "AddPac Technology Co., Ltd." }, + { 0x0002A5, "Compaq Computer Corporation" }, + { 0x0002A6, "Effinet Systems Co., Ltd." }, + { 0x0002A7, "Vivace Networks" }, + { 0x0002A8, "Air Link Technology" }, + { 0x0002A9, "RACOM, s.r.o." }, + { 0x0002AA, "PLcom Co., Ltd." }, + { 0x0002AB, "CTC Union Technologies Co., Ltd." }, + { 0x0002AC, "3PAR data" }, + { 0x0002AD, "Asahi Optical Co., Ltd." }, + { 0x0002AE, "Scannex Electronics Ltd." }, + { 0x0002AF, "TeleCruz Technology, Inc." }, + { 0x0002B0, "Hokubu Communication & Industrial Co., Ltd." }, + { 0x0002B1, "Anritsu, Ltd." }, + { 0x0002B2, "Cablevision" }, + { 0x0002B3, "Intel Corporation" }, + { 0x0002B4, "DAPHNE" }, + { 0x0002B5, "Avnet, Inc." }, + { 0x0002B6, "Acrosser Technology Co., Ltd." }, + { 0x0002B7, "Watanabe Electric Industry Co., Ltd." }, + { 0x0002B8, "WHI KONSULT AB" }, + { 0x0002B9, "Cisco Systems, Inc." }, + { 0x0002BA, "Cisco Systems, Inc." }, + { 0x0002BB, "Continuous Computing" }, + { 0x0002BC, "LVL 7 Systems, Inc." }, + { 0x0002BD, "Bionet Co., Ltd." }, + { 0x0002BE, "Totsu Engineering, Inc." }, + { 0x0002BF, "dotRocket, Inc." }, + { 0x0002C0, "Bencent Tzeng Industry Co., Ltd." }, + { 0x0002C1, "Innovative Electronic Designs, Inc." }, + { 0x0002C2, "Net Vision Telecom" }, + { 0x0002C3, "Arelnet Ltd." }, + { 0x0002C4, "Vector International BUBA" }, + { 0x0002C5, "Evertz Microsystems Ltd." }, + { 0x0002C6, "Data Track Technology PLC" }, + { 0x0002C7, "ALPS ELECTRIC Co., Ltd." }, + { 0x0002C8, "Technocom Communications Technology (pte) Ltd" }, + { 0x0002C9, "Mellanox Technologies" }, + { 0x0002CA, "EndPoints, Inc." }, + { 0x0002CB, "TriState Ltd." }, + { 0x0002CC, "M.C.C.I" }, + { 0x0002CD, "TeleDream, Inc." }, + { 0x0002CE, "FoxJet, Inc." }, + { 0x0002CF, "ZyGate Communications, Inc." }, + { 0x0002D0, "Comdial Corporation" }, + { 0x0002D1, "Vivotek, Inc." }, + { 0x0002D2, "Workstation AG" }, + { 0x0002D3, "NetBotz, Inc." }, + { 0x0002D4, "PDA Peripherals, Inc." }, + { 0x0002D5, "ACR" }, + { 0x0002D6, "NICE Systems" }, + { 0x0002D7, "EMPEG Ltd" }, + { 0x0002D8, "BRECIS Communications Corporation" }, + { 0x0002D9, "Reliable Controls" }, + { 0x0002DA, "ExiO Communications, Inc." }, + { 0x0002DB, "NETSEC" }, + { 0x0002DC, "Fujitsu General Limited" }, + { 0x0002DD, "Bromax Communications, Ltd." }, + { 0x0002DE, "Astrodesign, Inc." }, + { 0x0002DF, "Net Com Systems, Inc." }, + { 0x0002E0, "ETAS GmbH" }, + { 0x0002E1, "Integrated Network Corporation" }, + { 0x0002E2, "NDC Infared Engineering" }, + { 0x0002E3, "LITE-ON Communications, Inc." }, + { 0x0002E4, "JC HYUN Systems, Inc." }, + { 0x0002E5, "Timeware Ltd." }, + { 0x0002E6, "Gould Instrument Systems, Inc." }, + { 0x0002E7, "CAB GmbH & Co KG" }, + { 0x0002E8, "E.D.&A." }, + { 0x0002E9, "CS Systemes De Securite - C3S" }, + { 0x0002EA, "Videonics, Inc." }, + { 0x0002EB, "Easent Communications" }, + { 0x0002EC, "Maschoff Design Engineering" }, + { 0x0002ED, "DXO Telecom Co., Ltd." }, + { 0x0002EE, "Nokia Danmark A/S" }, + { 0x0002EF, "CCC Network Systems Group Ltd." }, + { 0x0002F0, "AME Optimedia Technology Co., Ltd." }, + { 0x0002F1, "Pinetron Co., Ltd." }, + { 0x0002F2, "eDevice, Inc." }, + { 0x0002F3, "Media Serve Co., Ltd." }, + { 0x0002F4, "PCTEL, Inc." }, + { 0x0002F5, "VIVE Synergies, Inc." }, + { 0x0002F6, "Equipe Communications" }, + { 0x0002F7, "ARM" }, + { 0x0002F8, "SEAKR Engineering, Inc." }, + { 0x0002F9, "Mimos Semiconductor SDN BHD" }, + { 0x0002FA, "DX Antenna Co., Ltd." }, + { 0x0002FB, "Baumuller Aulugen-Systemtechnik GmbH" }, + { 0x0002FC, "Cisco Systems, Inc." }, + { 0x0002FD, "Cisco Systems, Inc." }, + { 0x0002FE, "Viditec, Inc." }, + { 0x0002FF, "Handan Broad InfoCom" }, + { 0x000300, "NetContinuum, Inc." }, + { 0x000301, "Avantas Networks Corporation" }, + { 0x000302, "Oasys Telecom, Inc." }, + { 0x000303, "JAMA Electronics Co., Ltd." }, + { 0x000304, "Pacific Broadband Communications" }, + { 0x000305, "Smart Network Devices GmbH" }, + { 0x000306, "Fusion In Tech Co., Ltd." }, + { 0x000307, "Secure Works, Inc." }, + { 0x000308, "AM Communications, Inc." }, + { 0x000309, "Texcel Technology PLC" }, + { 0x00030A, "Argus Technologies" }, + { 0x00030B, "Hunter Technology, Inc." }, + { 0x00030C, "Telesoft Technologies Ltd." }, + { 0x00030D, "Uniwill Computer Corp." }, + { 0x00030E, "Core Communications Co., Ltd." }, + { 0x00030F, "Legend Digital China Ltd." }, + { 0x000310, "Link Evolution Corp." }, + { 0x000311, "Micro Technology Co., Ltd." }, + { 0x000312, "TR-Systemtechnik GmbH" }, + { 0x000313, "Access Media SPA" }, + { 0x000314, "Teleware Network Systems" }, + { 0x000315, "Cidco Incorporated" }, + { 0x000316, "Nobell Communications, Inc." }, + { 0x000317, "Merlin Systems, Inc." }, + { 0x000318, "Cyras Systems, Inc." }, + { 0x000319, "Infineon AG" }, + { 0x00031A, "Beijing Broad Telecom Ltd., China" }, + { 0x00031B, "Cellvision Systems, Inc." }, + { 0x00031C, "Svenska Hardvarufabriken AB" }, + { 0x00031D, "Taiwan Commate Computer, Inc." }, + { 0x00031E, "Optranet, Inc." }, + { 0x00031F, "Condev Ltd." }, + { 0x000320, "Xpeed, Inc." }, + { 0x000321, "Reco Research Co., Ltd." }, + { 0x000322, "IDIS Co., Ltd." }, + { 0x000323, "Cornet Technology, Inc." }, + { 0x000324, "Tottori SANYO Electric Co., Ltd." }, + { 0x000325, "Arima Computer Corp." }, + { 0x000326, "Iwasaki Information Systems Co., Ltd." }, + { 0x000327, "ACT'L" }, + { 0x000328, "Mace Group, Inc." }, + { 0x000329, "F3, Inc." }, + { 0x00032A, "UniData Communication Systems, Inc." }, + { 0x00032B, "GAI Datenfunksysteme GmbH" }, + { 0x00032C, "ABB Industrie AG" }, + { 0x00032D, "IBASE Technology, Inc." }, + { 0x00032E, "Scope Information Management, Ltd." }, + { 0x00032F, "Global Sun Technology, Inc." }, + { 0x000330, "Imagenics, Co., Ltd." }, + { 0x000331, "Cisco Systems, Inc." }, + { 0x000332, "Cisco Systems, Inc." }, + { 0x000333, "Digitel Co., Ltd." }, + { 0x000334, "Newport Electronics" }, + { 0x000335, "Mirae Technology" }, + { 0x000336, "Zetes Technologies" }, + { 0x000337, "Vaone, Inc." }, + { 0x000338, "Oak Technology" }, + { 0x000339, "Eurologic Systems, Ltd." }, + { 0x00033A, "Silicon Wave, Inc." }, + { 0x00033B, "TAMI Tech Co., Ltd." }, + { 0x00033C, "Daiden Co., Ltd." }, + { 0x00033D, "ILSHin Lab" }, + { 0x00033E, "Tateyama System Laboratory Co., Ltd." }, + { 0x00033F, "BigBand Networks, Ltd." }, + { 0x000340, "Floware Wireless Systems, Ltd." }, + { 0x000341, "Axon Digital Design" }, + { 0x000342, "Nortel Networks" }, + { 0x000343, "Martin Professional A/S" }, + { 0x000344, "Tietech.Co., Ltd." }, + { 0x000345, "Routrek Networks Corporation" }, + { 0x000346, "Hitachi Kokusai Electric, Inc." }, + { 0x000347, "Intel Corporation" }, + { 0x000348, "Norscan Instruments, Ltd." }, + { 0x000349, "Vidicode Datacommunicatie B.V." }, + { 0x00034A, "RIAS Corporation" }, + { 0x00034B, "Nortel Networks" }, + { 0x00034C, "Shanghai DigiVision Technology Co., Ltd." }, + { 0x00034D, "Chiaro Networks, Ltd." }, + { 0x00034E, "Pos Data Company, Ltd." }, + { 0x00034F, "Sur-Gard Security" }, + { 0x000350, "BTICINO SPA" }, + { 0x000351, "Diebold, Inc." }, + { 0x000352, "Colubris Networks" }, + { 0x000353, "Mitac, Inc." }, + { 0x000354, "Fiber Logic Communications" }, + { 0x000355, "TeraBeam Internet Systems" }, + { 0x000356, "Wincor Nixdorf GmbH & Co KG" }, + { 0x000357, "Intervoice-Brite, Inc." }, + { 0x000358, "iCable System Co., Ltd." }, + { 0x000359, "DigitalSis" }, + { 0x00035A, "Phototron Limited" }, + { 0x00035B, "BridgeWave Communications" }, + { 0x00035C, "Saint Song Corp." }, + { 0x00035D, "Bosung Hi-Net Co., Ltd." }, + { 0x00035E, "Metropolitan Area Networks, Inc." }, + { 0x00035F, "Schuehle Mess - und. Kontrollsysteme" }, + { 0x000360, "PAC Interactive Technology, Inc." }, + { 0x000361, "Widcomm, Inc." }, + { 0x000362, "Vodtel Communications, Inc." }, + { 0x000363, "Miraesys Co., Ltd." }, + { 0x000364, "Scenix Semiconductor, Inc." }, + { 0x000365, "Kira Information & Communications, Ltd." }, + { 0x000366, "ASM Pacific Technology" }, + { 0x000367, "Jasmine Networks, Inc." }, + { 0x000368, "Embedone Co., Ltd." }, + { 0x000369, "Nippon Antenna Co., Ltd." }, + { 0x00036A, "Mainnet, Ltd." }, + { 0x00036B, "Cisco Systems, Inc." }, + { 0x00036C, "Cisco Systems, Inc." }, + { 0x00036D, "Runtop, Inc." }, + { 0x00036E, "Nicon Systems (Pty) Limited" }, + { 0x00036F, "Telsey SPA" }, + { 0x000370, "NXTV, Inc." }, + { 0x000371, "Acomz Networks Corp." }, + { 0x000372, "ULAN" }, + { 0x000373, "Aselsan A.S" }, + { 0x000374, "Hunter Watertech" }, + { 0x000375, "NetMedia, Inc." }, + { 0x000376, "Graphtec Technology, Inc." }, + { 0x000377, "Gigabit Wireless" }, + { 0x000378, "HUMAX Co., Ltd." }, + { 0x000379, "Proscend Communications, Inc." }, + { 0x00037A, "Taiyo Yuden Co., Ltd." }, + { 0x00037B, "IDEC IZUMI Corporation" }, + { 0x00037C, "Coax Media" }, + { 0x00037D, "Stellcom" }, + { 0x00037E, "PORTech Communications, Inc." }, + { 0x00037F, "Atheros Communications, Inc." }, + { 0x000381, "Ingenico International" }, + { 0x000382, "A-One Co., Ltd." }, + { 0x000383, "Metera Networks, Inc." }, + { 0x000384, "AETA" }, + { 0x000385, "Actelis Networks, Inc." }, + { 0x000386, "Ho Net, Inc." }, + { 0x000387, "Blaze Network Products" }, + { 0x000388, "Fastfame Technology Co., Ltd." }, + { 0x000389, "Plantronics" }, + { 0x00038A, "America Online, Inc." }, + { 0x00038B, "PLUS-ONE I&T, Inc." }, + { 0x00038C, "Total Impact" }, + { 0x00038D, "PCS Revenue Control Systems, Inc." }, + { 0x00038E, "Atoga Systems, Inc." }, + { 0x00038F, "Weinschel Corporation" }, + { 0x000390, "Digital Video Communications, Inc." }, + { 0x000392, "Hyundai Teletek Co., Ltd." }, + { 0x000393, "Apple Computer, Inc." }, + { 0x000394, "Connect One" }, + { 0x000395, "California Amplifier" }, + { 0x000396, "EZ Cast Co., Ltd." }, + { 0x000397, "Watchfront Electronics" }, + { 0x000398, "WISI" }, + { 0x000399, "Dongju Informations & Communications Co., Ltd." }, + { 0x00039A, "nSine, Ltd." }, + { 0x00039B, "NetChip Technology, Inc." }, + { 0x00039C, "OptiMight Communications, Inc." }, + { 0x00039D, "Acer Communications & Multimedia, Inc." }, + { 0x00039E, "Tera System Co., Ltd." }, + { 0x00039F, "Cisco Systems, Inc." }, + { 0x0003A0, "Cisco Systems, Inc." }, + { 0x0003A1, "HIPER Information & Communication, Inc." }, + { 0x0003A2, "Catapult Communications" }, + { 0x0003A3, "MAVIX, Ltd." }, + { 0x0003A4, "Data Storage and Information Management" }, + { 0x0003A5, "Medea Corporation" }, + { 0x0003A7, "Unixtar Technology, Inc." }, + { 0x0003A8, "IDOT Computers, Inc." }, + { 0x0003A9, "AXCENT Media AG" }, + { 0x0003AA, "Watlow" }, + { 0x0003AB, "Bridge Information Systems" }, + { 0x0003AC, "Fronius Schweissmaschinen" }, + { 0x0003AD, "Emerson Energy Systems AB" }, + { 0x0003AE, "Allied Advanced Manufacturing Pte, Ltd." }, + { 0x0003AF, "Paragea Communications" }, + { 0x0003B0, "Xsense Technology Corp." }, + { 0x0003B1, "Abbott Laboratories HPD" }, + { 0x0003B2, "Radware" }, + { 0x0003B3, "IA Link Systems Co., Ltd." }, + { 0x0003B4, "Macrotek International Corp." }, + { 0x0003B5, "Entra Technology Co." }, + { 0x0003B6, "QSI Corporation" }, + { 0x0003B7, "ZACCESS Systems" }, + { 0x0003B8, "NetKit Solutions, LLC" }, + { 0x0003B9, "Hualong Telecom Co., Ltd." }, + { 0x0003BA, "Sun Microsystems" }, + { 0x0003BB, "Signal Communications Limited" }, + { 0x0003BC, "COT GmbH" }, + { 0x0003BD, "OmniCluster Technologies, Inc." }, + { 0x0003BE, "Netility" }, + { 0x0003BF, "Centerpoint Broadband Technologies, Inc." }, + { 0x0003C0, "RFTNC Co., Ltd." }, + { 0x0003C1, "Packet Dynamics Ltd" }, + { 0x0003C2, "Solphone K.K." }, + { 0x0003C3, "Micronik Multimedia" }, + { 0x0003C4, "Tomra Systems ASA" }, + { 0x0003C5, "Mobotix AG" }, + { 0x0003C6, "ICUE Systems, Inc." }, + { 0x0003C7, "hopf Elektronik GmbH" }, + { 0x0003C8, "CML Emergency Services" }, + { 0x0003C9, "TECOM Co., Ltd." }, + { 0x0003CA, "MTS Systems Corp." }, + { 0x0003CB, "Nippon Systems Development Co., Ltd." }, + { 0x0003CC, "Momentum Computer, Inc." }, + { 0x0003CD, "Clovertech, Inc." }, + { 0x0003CE, "ETEN Technologies, Inc." }, + { 0x0003CF, "Muxcom, Inc." }, + { 0x0003D0, "KOANKEISO Co., Ltd." }, + { 0x0003D1, "Takaya Corporation" }, + { 0x0003D2, "Crossbeam Systems, Inc." }, + { 0x0003D3, "Internet Energy Systems, Inc." }, + { 0x0003D4, "Alloptic, Inc." }, + { 0x0003D5, "Advanced Communications Co., Ltd." }, + { 0x0003D6, "RADVision, Ltd." }, + { 0x0003D7, "NextNet Wireless, Inc." }, + { 0x0003D8, "iMPath Networks, Inc." }, + { 0x0003D9, "Secheron SA" }, + { 0x0003DA, "Takamisawa Cybernetics Co., Ltd." }, + { 0x0003DB, "Apogee Electronics Corp." }, + { 0x0003DC, "Lexar Media, Inc." }, + { 0x0003DD, "Comark Corp." }, + { 0x0003DE, "OTC Wireless" }, + { 0x0003DF, "Desana Systems" }, + { 0x0003E0, "RadioFrame Networks, Inc." }, + { 0x0003E1, "Winmate Communication, Inc." }, + { 0x0003E2, "Comspace Corporation" }, + { 0x0003E3, "Cisco Systems, Inc." }, + { 0x0003E4, "Cisco Systems, Inc." }, + { 0x0003E5, "Hermstedt SG" }, + { 0x0003E6, "Entone Technologies, Inc." }, + { 0x0003E7, "Logostek Co. Ltd." }, + { 0x0003E8, "Wavelength Digital Limited" }, + { 0x0003E9, "Akara Canada, Inc." }, + { 0x0003EA, "Mega System Technologies, Inc." }, + { 0x0003EB, "Atrica" }, + { 0x0003EC, "ICG Research, Inc." }, + { 0x0003ED, "Shinkawa Electric Co., Ltd." }, + { 0x0003EE, "MKNet Corporation" }, + { 0x0003EF, "Oneline AG" }, + { 0x0003F0, "Redfern Broadband Networks" }, + { 0x0003F1, "Cicada Semiconductor, Inc." }, + { 0x0003F2, "Seneca Networks" }, + { 0x0003F3, "Dazzle Multimedia, Inc." }, + { 0x0003F4, "NetBurner" }, + { 0x0003F5, "Chip2Chip" }, + { 0x0003F6, "Allegro Networks, Inc." }, + { 0x0003F7, "Plast-Control GmbH" }, + { 0x0003F8, "SanCastle Technologies, Inc." }, + { 0x0003F9, "Pleiades Communications, Inc." }, + { 0x0003FA, "TiMetra Networks" }, + { 0x0003FB, "Toko Seiki Company, Ltd." }, + { 0x0003FC, "Intertex Data AB" }, + { 0x0003FD, "Cisco Systems, Inc." }, + { 0x0003FE, "Cisco Systems, Inc." }, + { 0x0003FF, "Connectix" }, + { 0x000400, "LEXMARK INTERNATIONAL, INC." }, + { 0x000401, "Osaki Electric Co., Ltd." }, + { 0x000402, "Nexsan Technologies, Ltd." }, + { 0x000403, "Nexsi Corporation" }, + { 0x000404, "Makino Milling Machine Co., Ltd." }, + { 0x000405, "ACN Technologies" }, + { 0x000406, "Fa. Metabox AG" }, + { 0x000407, "Topcon Positioning Systems, Inc." }, + { 0x000408, "Sanko Electronics Co., Ltd." }, + { 0x000409, "Cratos Networks" }, + { 0x00040A, "Sage Systems" }, + { 0x00040B, "3com Europe Ltd." }, + { 0x00040C, "KANNO Work's Ltd." }, + { 0x00040D, "Avaya, Inc." }, + { 0x00040E, "AVM GmbH" }, + { 0x00040F, "Asus Network Technologies, Inc." }, + { 0x000410, "Spinnaker Networks, Inc." }, + { 0x000411, "Inkra Networks, Inc." }, + { 0x000412, "WaveSmith Networks, Inc." }, + { 0x000413, "SNOM Technology AG" }, + { 0x000414, "Umezawa Musen Denki Co., Ltd." }, + { 0x000415, "Rasteme Systems Co., Ltd." }, + { 0x000416, "Parks S/A Comunicacoes Digitais" }, + { 0x000417, "ELAU AG" }, + { 0x000418, "Teltronic S.A.U." }, + { 0x000419, "Fibercycle Networks, Inc." }, + { 0x00041A, "ines GmbH" }, + { 0x00041B, "Digital Interfaces Ltd." }, + { 0x00041C, "ipDialog, Inc." }, + { 0x00041D, "Corega of America" }, + { 0x00041E, "Shikoku Instrumentation Co., Ltd." }, + { 0x00041F, "Sony Computer Entertainment, Inc." }, + { 0x000420, "Slim Devices, Inc." }, + { 0x000421, "Ocular Networks" }, + { 0x000422, "Gordon Kapes, Inc." }, + { 0x000423, "Intel Corporation" }, + { 0x000424, "TMC s.r.l." }, + { 0x000425, "Atmel Corporation" }, + { 0x000426, "Autosys" }, + { 0x000427, "Cisco Systems, Inc." }, + { 0x000428, "Cisco Systems, Inc." }, + { 0x000429, "Pixord Corporation" }, + { 0x00042A, "Wireless Networks, Inc." }, + { 0x00042B, "IT Access Co., Ltd." }, + { 0x00042C, "Minet, Inc." }, + { 0x00042D, "Sarian Systems, Ltd." }, + { 0x00042E, "Netous Technologies, Ltd." }, + { 0x00042F, "International Communications Products, Inc." }, + { 0x000430, "Netgem" }, + { 0x000431, "Play Industries" }, + { 0x000432, "Voyetra Turtle Beach, Inc." }, + { 0x000433, "Cyberboard A/S" }, + { 0x000434, "Accelent Systems, Inc." }, + { 0x000435, "Comptek International, Inc." }, + { 0x000436, "ELANsat Technologies, Inc." }, + { 0x000437, "Powin Information Technology, Inc." }, + { 0x000438, "Nortel Networks" }, + { 0x000439, "Rosco Entertainment Technology, Inc." }, + { 0x00043A, "Intelligent Telecommunications, Inc." }, + { 0x00043B, "Lava Computer Mfg., Inc." }, + { 0x00043C, "SONOS Co., Ltd." }, + { 0x00043D, "INDEL AG" }, + { 0x00043E, "Telencomm" }, + { 0x00043F, "Electronic Systems Technology, Inc." }, + { 0x000440, "cyberPIXIE, Inc." }, + { 0x000441, "Half Dome Systems, Inc." }, + { 0x000442, "NACT" }, + { 0x000443, "Agilent Technologies, Inc." }, + { 0x000444, "Wireless Home" }, + { 0x000445, "LMS Skalar Instruments GmbH" }, + { 0x000446, "CYZENTECH Co., Ltd." }, + { 0x000447, "Acrowave Systems Co., Ltd." }, + { 0x000448, "Polaroid Professional Imaging" }, + { 0x000449, "Mapletree Networks" }, + { 0x00044A, "iPolicy Networks, Inc." }, + { 0x00044B, "NVIDIA" }, + { 0x00044C, "JENOPTIK" }, + { 0x00044D, "Cisco Systems, Inc." }, + { 0x00044E, "Cisco Systems, Inc." }, + { 0x00044F, "Leukhardt Systemelektronik GmbH" }, + { 0x000450, "DMD Computers SRL" }, + { 0x000451, "Medrad, Inc." }, + { 0x000452, "RocketLogix, Inc." }, + { 0x000453, "Yotta Yotta, Inc." }, + { 0x000454, "Quadriga UK" }, + { 0x000455, "ANTARA.net" }, + { 0x000456, "PipingHot Networks" }, + { 0x000457, "Universal Access Technology, Inc." }, + { 0x000458, "Fusion X Co., Ltd." }, + { 0x000459, "Veristar Corporation" }, + { 0x00045A, "The Linksys Group, Inc." }, + { 0x00045B, "Techsan Electronics Co., Ltd." }, + { 0x00045C, "Mobiwave Pte Ltd" }, + { 0x00045D, "BEKA Elektronik" }, + { 0x00045E, "Poly Trax Information Technology AG" }, + { 0x00045F, "Evalue Technology, Inc." }, + { 0x000460, "Knilink Technology, Inc." }, + { 0x000461, "EPOX Computer Co., Ltd." }, + { 0x000462, "DAKOS Data & Communication Co., Ltd." }, + { 0x000463, "Philips Communication Security & Imaging" }, + { 0x000464, "Fantasma Networks, Inc." }, + { 0x000465, "ist isdn support tecknik GmbH" }, + { 0x000466, "ARMITEL Co." }, + { 0x000467, "Wuhan Research Institute" }, + { 0x000468, "Vivity, Inc." }, + { 0x000469, "Innocom, Inc." }, + { 0x00046A, "Navini Networks" }, + { 0x00046B, "Palm Wireless, Inc." }, + { 0x00046C, "Cyber Technology Co., Ltd." }, + { 0x00046D, "Cisco Systems, Inc." }, + { 0x00046E, "Cisco Systems, Inc." }, + { 0x00046F, "Digitel S/A Industria Eletronica" }, + { 0x000470, "ipUnplugged AB" }, + { 0x000471, "IPrad" }, + { 0x000472, "Telelynx, Inc." }, + { 0x000473, "Photonex Corporation" }, + { 0x000474, "LEGRAND" }, + { 0x000475, "3 Com Corporation" }, + { 0x000476, "3 Com Corporation" }, + { 0x000477, "e-Appliance Corp." }, + { 0x000478, "G. Star Technology Corporation" }, + { 0x000479, "Radius Co., Ltd." }, + { 0x00047A, "AXXESSIT ASA" }, + { 0x00047B, "Schlumberger" }, + { 0x00047C, "Skidata AG" }, + { 0x00047D, "Pelco" }, + { 0x00047E, "NKF Electronics" }, + { 0x00047F, "Chr. Mayr GmbH & Co. KG" }, + { 0x000480, "Foundry Networks, Inc." }, + { 0x000481, "Econolite Control Products, Inc." }, + { 0x000482, "MediaLogic Corp." }, + { 0x000483, "Deltron Technology, Inc." }, + { 0x000484, "Amann GmbH" }, + { 0x000485, "PicoLight" }, + { 0x000486, "ITTC, University of Kansas" }, + { 0x000487, "Cogency Semiconductor, Inc." }, + { 0x000488, "Eurotherm Action Incorporated." }, + { 0x000489, "YAFO Networks, Inc." }, + { 0x00048A, "Temia Vertriebs GmbH" }, + { 0x00048B, "Poscon Corporation" }, + { 0x00048C, "Nayna Networks, Inc." }, + { 0x00048D, "Tone Commander Systems, Inc." }, + { 0x00048E, "Ohm Tech Labs, Inc." }, + { 0x00048F, "TD Systems Corp." }, + { 0x000490, "Optical Access" }, + { 0x000491, "Technovision, Inc." }, + { 0x000492, "Hive Internet, Ltd." }, + { 0x000493, "Tsinghua Unisplendour Co., Ltd." }, + { 0x000494, "Breezecom, Ltd." }, + { 0x000495, "Tejas Networks" }, + { 0x000496, "Extreme Networks" }, + { 0x000497, "MacroSystem Digital Video AG" }, + { 0x000499, "Chino Corporation" }, + { 0x00049A, "Cisco Systems, Inc." }, + { 0x00049B, "Cisco Systems, Inc." }, + { 0x00049C, "Surgient Networks, Inc." }, + { 0x00049D, "Ipanema Technologies" }, + { 0x00049E, "Wirelink Co., Ltd." }, + { 0x00049F, "Metrowerks" }, + { 0x0004A0, "Verity Instruments, Inc." }, + { 0x0004A1, "Pathway Connectivity" }, + { 0x0004A2, "L.S.I. Japan Co., Ltd." }, + { 0x0004A3, "Microchip Technology, Inc." }, + { 0x0004A4, "NetEnabled, Inc." }, + { 0x0004A5, "Barco Projection Systems NV" }, + { 0x0004A6, "SAF Tehnika Ltd." }, + { 0x0004A7, "FabiaTech Corporation" }, + { 0x0004A8, "Broadmax Technologies, Inc." }, + { 0x0004A9, "SandStream Technologies, Inc." }, + { 0x0004AA, "Jetstream Communications" }, + { 0x0004AB, "Comverse Network Systems, Inc." }, + { 0x0004AC, "IBM CORP." }, + { 0x0004AD, "Malibu Networks" }, + { 0x0004AE, "Liquid Metronics" }, + { 0x0004AF, "Digital Fountain, Inc." }, + { 0x0004B0, "ELESIGN Co., Ltd." }, + { 0x0004B1, "Signal Technology, Inc." }, + { 0x0004B2, "ESSEGI SRL" }, + { 0x0004B3, "Videotek, Inc." }, + { 0x0004B4, "CIAC" }, + { 0x0004B5, "Equitrac Corporation" }, + { 0x0004B6, "Tellumat (Pty) Ltd." }, + { 0x0004B7, "AMB i.t. Holding" }, + { 0x0004B8, "Kumahira Co., Ltd." }, + { 0x0004B9, "S.I. Soubou, Inc." }, + { 0x0004BA, "KDD Media Will Corporation" }, + { 0x0004BB, "Bardac Corporation" }, + { 0x0004BC, "Giantec, Inc." }, + { 0x0004BD, "Motorola BCS" }, + { 0x0004BE, "OptXCon, Inc." }, + { 0x0004BF, "Versa Logic Corp." }, + { 0x0004C0, "Cisco Systems, Inc." }, + { 0x0004C1, "Cisco Systems, Inc." }, + { 0x0004C2, "Magnipix, Inc." }, + { 0x0004C3, "CASTOR Informatique" }, + { 0x0004C4, "Allen & Health" }, + { 0x0004C5, "ASE Technologies, USA" }, + { 0x0004C6, "Yamaha Motor Co., Ltd." }, + { 0x0004C7, "NetMount" }, + { 0x0004C8, "LIBA Maschinefabrik GmbH" }, + { 0x0004C9, "Micro Electron Co., Ltd." }, + { 0x0004CA, "FreeMs Corp." }, + { 0x0004CB, "Tdsoft Communication, Ltd." }, + { 0x0004CC, "Peek Traffic BV." }, + { 0x0004CD, "Informedia Research Group" }, + { 0x0004CE, "Patria Ailon" }, + { 0x0004CF, "Seagate Technology" }, + { 0x0004D0, "Softlink s.r.o." }, + { 0x0004D1, "Drew Technologies, Inc." }, + { 0x0004D2, "Adcon Telemetry AG" }, + { 0x0004D3, "Toyokeiki Co., Ltd." }, + { 0x0004D4, "Proview Electronics Co., Ltd." }, + { 0x0004D5, "Hitachi Communication Systems, Inc." }, + { 0x0004D6, "Takagi Industrial Co., Ltd." }, + { 0x0004D7, "Omitec Instrumentation Ltd." }, + { 0x0004D8, "IPWireless, Inc." }, + { 0x0004D9, "Titan Electronics, Inc." }, + { 0x0004DA, "Relax Technology, Inc." }, + { 0x0004DB, "Tellus Group Corp." }, + { 0x0004DC, "Nortel Networks" }, + { 0x0004DD, "Cisco Systems, Inc." }, + { 0x0004DE, "Cisco Systems, Inc." }, + { 0x0004DF, "Teracom Telematica Ltda." }, + { 0x0004E0, "Procket Networks" }, + { 0x0004E1, "Infinior Microsystems" }, + { 0x0004E2, "SMC Networks, Inc." }, + { 0x0004E3, "Accton Technology Corp." }, + { 0x0004E4, "Daeryung Ind., Inc." }, + { 0x0004E5, "Glonet Systems, Inc." }, + { 0x0004E6, "Banyan Network Private Limited" }, + { 0x0004E7, "Lightpointe Communications, Inc" }, + { 0x0004E8, "IER, Inc." }, + { 0x0004E9, "Infiniswitch Corporation" }, + { 0x0004EA, "Hewlett-Packard Company" }, + { 0x0004EB, "Paxonet Communications, Inc." }, + { 0x0004EC, "Memobox SA" }, + { 0x0004ED, "Billion Electric Co., Ltd." }, + { 0x0004EE, "Lincoln Electric Company" }, + { 0x0004EF, "Polestar Corp." }, + { 0x0004F0, "International Computers, Ltd" }, + { 0x0004F1, "WhereNet" }, + { 0x0004F2, "Circa Communications, Ltd." }, + { 0x0004F3, "FS FORTH-SYSTEME GmbH" }, + { 0x0004F4, "Infinite Electronics Inc." }, + { 0x0004F5, "SnowShore Networks, Inc." }, + { 0x0004F6, "Amphus" }, + { 0x0004F7, "Omega Band, Inc." }, + { 0x0004F8, "QUALICABLE TV Industria E Com., Ltda" }, + { 0x0004F9, "Xtera Communications, Inc." }, + { 0x0004FA, "MIST Inc." }, + { 0x0004FB, "Commtech, Inc." }, + { 0x0004FC, "Stratus Computer (DE), Inc." }, + { 0x0004FD, "Japan Control Engineering Co., Ltd." }, + { 0x0004FE, "Pelago Networks" }, + { 0x0004FF, "Acronet Co., Ltd." }, + { 0x000500, "Cisco Systems, Inc." }, + { 0x000501, "Cisco Systems, Inc." }, + { 0x000502, "APPLE COMPUTER" }, + { 0x000503, "ICONAG" }, + { 0x000504, "Naray Information & Communication Enterprise" }, + { 0x000505, "Systems Integration Solutions, Inc." }, + { 0x000506, "Reddo Networks AB" }, + { 0x000507, "Fine Appliance Corp." }, + { 0x000508, "Inetcam, Inc." }, + { 0x000509, "AVOC Nishimura Ltd." }, + { 0x00050A, "ICS Spa" }, + { 0x00050B, "SICOM Systems, Inc." }, + { 0x00050C, "Network Photonics, Inc." }, + { 0x00050D, "Midstream Technologies, Inc." }, + { 0x00050E, "3ware, Inc." }, + { 0x00050F, "Tanaka S/S Ltd." }, + { 0x000510, "Infinite Shanghai Communication Terminals Ltd." }, + { 0x000511, "Complementaty Technologies Ltd" }, + { 0x000512, "MeshNetworks, Inc." }, + { 0x000513, "VTLinx Multimedia Systems, Inc." }, + { 0x000514, "KDT Systems Co., Ltd." }, + { 0x000515, "Nuark Co., Ltd." }, + { 0x000516, "SMART Modular Technologies" }, + { 0x000517, "Shellcomm, Inc." }, + { 0x000518, "Jupiters Technology" }, + { 0x000519, "Siemens Building Technologies AG," }, + { 0x00051A, "3Com Europe Ltd." }, + { 0x00051B, "Magic Control Technology Corporation" }, + { 0x00051C, "Xnet Technology Corp." }, + { 0x00051D, "Airocon, Inc." }, + { 0x00051E, "Rhapsody Networks" }, + { 0x00051F, "Taijin Media Co., Ltd." }, + { 0x000520, "Smartronix, Inc." }, + { 0x000521, "Control Microsystems" }, + { 0x000522, "LEA*D Corporation, Inc." }, + { 0x000523, "AVL List GmbH" }, + { 0x000524, "BTL System (HK) Limited" }, + { 0x000525, "Puretek Industrial Co., Ltd." }, + { 0x000526, "IPAS GmbH" }, + { 0x000527, "SJ Tek Co. Ltd" }, + { 0x000528, "New Focus, Inc." }, + { 0x000529, "Shanghai Broadan Communication Technology Co., Ltd" }, + { 0x00052A, "Ikegami Tsushinki Co., Ltd." }, + { 0x00052B, "HORIBA, Ltd." }, + { 0x00052C, "Supreme Magic Corporation" }, + { 0x00052D, "Zoltrix International Limited" }, + { 0x00052E, "Cinta Networks" }, + { 0x00052F, "Leviton Voice and Data" }, + { 0x000530, "Andiamo Systems, Inc." }, + { 0x000531, "Cisco Systems, Inc." }, + { 0x000532, "Cisco Systems, Inc." }, + { 0x000533, "Sanera Systems, Inc." }, + { 0x000534, "Northstar Engineering Ltd." }, + { 0x000535, "Chip PC Ltd." }, + { 0x000536, "Danam Communications, Inc." }, + { 0x000537, "Nets Technology Co., Ltd." }, + { 0x000538, "Merilus, Inc." }, + { 0x000539, "A Brand New World in Sweden AB" }, + { 0x00053A, "Willowglen Services Pte Ltd" }, + { 0x00053B, "Harbour Networks Ltd., Co. Beijing" }, + { 0x00053C, "Xircom" }, + { 0x00053D, "Agere Systems" }, + { 0x00053E, "KID Systeme GmbH" }, + { 0x00053F, "VisionTek, Inc." }, + { 0x000540, "FAST Corporation" }, + { 0x000541, "Advanced Systems Co., Ltd." }, + { 0x000542, "Otari, Inc." }, + { 0x000543, "IQ Wireless GmbH" }, + { 0x000544, "Valley Technologies, Inc." }, + { 0x000545, "Internet Photonics" }, + { 0x000546, "KDD Network Systems Co., Ltd." }, + { 0x000547, "Starent Networks" }, + { 0x000548, "Disco Corporation" }, + { 0x000549, "Salira Optical Network Systems" }, + { 0x00054A, "Ario Data Networks, Inc." }, + { 0x00054B, "Micro Innovation AG" }, + { 0x00054C, "RF Innovations Pty Ltd" }, + { 0x00054D, "Brans Technologies, Inc." }, + { 0x00054E, "Philips Components" }, + { 0x000550, "Digi-Tech Communications Limited" }, + { 0x000551, "F & S Elektronik Systeme GmbH" }, + { 0x000552, "Xycotec Computer GmbH" }, + { 0x000553, "DVC Company, Inc." }, + { 0x000554, "Rangestar Wireless" }, + { 0x000555, "Japan Cash Machine Co., Ltd." }, + { 0x000556, "360 Systems" }, + { 0x000557, "Agile TV Corporation" }, + { 0x000558, "Synchronous, Inc." }, + { 0x000559, "Intracom S.A." }, + { 0x00055A, "Power Dsine Ltd." }, + { 0x00055B, "Charles Industries, Ltd." }, + { 0x00055C, "Kowa Company, Ltd." }, + { 0x00055D, "D-Link Systems, Inc." }, + { 0x00055E, "Cisco Systems, Inc." }, + { 0x00055F, "Cisco Systems, Inc." }, + { 0x000560, "LEADER COMM.CO., LTD" }, + { 0x000561, "nac Image Technology, Inc." }, + { 0x000562, "Digital View Limited" }, + { 0x000563, "J-Works, Inc." }, + { 0x000564, "Tsinghua Bitway Co., Ltd." }, + { 0x000565, "Tailyn Communication Company Ltd." }, + { 0x000566, "Secui.com Corporation" }, + { 0x000567, "Etymonic Design, Inc." }, + { 0x000568, "Piltofish Networks AB" }, + { 0x000569, "VMWARE, Inc." }, + { 0x00056B, "C.P. Technology Co., Ltd." }, + { 0x00056C, "Hung Chang Co., Ltd." }, + { 0x00056D, "Pacific Corporation" }, + { 0x00056E, "National Enhance Technology, Inc." }, + { 0x00056F, "Innomedia Technologies Pvt. Ltd." }, + { 0x000570, "Baydel Ltd." }, + { 0x000571, "Seiwa Electronics Co." }, + { 0x000572, "Deonet Co., Ltd." }, + { 0x000573, "Cisco Systems, Inc." }, + { 0x000574, "Cisco Systems, Inc." }, + { 0x000575, "CDS-Electronics BV" }, + { 0x000576, "NSM Technology Ltd." }, + { 0x000577, "SM Information & Communication" }, + { 0x000579, "Universal Control Solution Corp." }, + { 0x00057A, "Hatteras Networks" }, + { 0x00057B, "Chung Nam Electronic Co., Ltd." }, + { 0x00057C, "RCO Security AB" }, + { 0x00057D, "Sun Communications, Inc." }, + { 0x00057E, "Eckelmann Steuerungstechnik GmbH" }, + { 0x00057F, "Acqis Technology" }, + { 0x000580, "Fibrolan Ltd." }, + { 0x000581, "Snell & Wilcox Ltd." }, + { 0x000582, "ClearCube Technology" }, + { 0x000583, "ImageCom Limited" }, + { 0x000584, "AbsoluteValue Systems, Inc." }, + { 0x000585, "Juniper Networks, Inc." }, + { 0x000586, "Lucent Technologies" }, + { 0x000587, "Locus, Incorporated" }, + { 0x000588, "Sensoria Corp." }, + { 0x000589, "National Datacomputer" }, + { 0x00058A, "Netcom Co., Ltd." }, + { 0x00058B, "IPmental, Inc." }, + { 0x00058C, "Opentech Inc." }, + { 0x00058D, "Lynx Photonic Networks, Inc." }, + { 0x00058E, "Ahead Communications System GmbH" }, + { 0x00058F, "CLCsoft co." }, + { 0x000590, "Ascom Business Systems" }, + { 0x000591, "Active Silicon Ltd." }, + { 0x000592, "Pultex Corp." }, + { 0x000593, "Grammar Engine Inc." }, + { 0x000594, "IXXAT Automation GmbH" }, + { 0x000595, "Alesis Corporation" }, + { 0x000596, "Genotech Co., Ltd." }, + { 0x000597, "Eagle Traffic Control Systems" }, + { 0x000598, "CRONOS S.r.l." }, + { 0x000599, "PEI Electronics, Inc." }, + { 0x00059A, "Cisco Systems, Inc." }, + { 0x00059B, "Cisco Systems, Inc." }, + { 0x00059C, "Kleinknecht GmbH, Ing. Buero" }, + { 0x00059D, "Daniel Computing Systems, Inc." }, + { 0x00059E, "Zinwell Corporation" }, + { 0x00059F, "Yotta Networks, Inc." }, + { 0x0005A0, "MOBILINE Kft." }, + { 0x0005A1, "Zenocom" }, + { 0x0005A2, "CELOX Networks" }, + { 0x0005A3, "QEI, Inc." }, + { 0x0005A4, "Lucid Voice Ltd." }, + { 0x0005A5, "KOTT" }, + { 0x0005A6, "Extron Electronics" }, + { 0x0005A7, "Hyperchip, Inc." }, + { 0x0005A8, "WYLE ELECTRONICS" }, + { 0x0005A9, "Princeton Networks, Inc." }, + { 0x0005AA, "Moore Industries Int." }, + { 0x0005AB, "Cyber Fone, Inc." }, + { 0x0005AC, "Northern Digital, Inc." }, + { 0x0005AD, "Topspin Communications, Inc." }, + { 0x0005AE, "Mediaport USA" }, + { 0x0005AF, "InnoScan Computing A/S" }, + { 0x0005B0, "Korea Computer Technology Co., Ltd." }, + { 0x0005B1, "ASB Technology BV" }, + { 0x0005B2, "Medison Co., Ltd." }, + { 0x0005B3, "Asahi-Engineering Co., Ltd." }, + { 0x0005B4, "Aceex Corporation" }, + { 0x0005B5, "Broadcom Technologies" }, + { 0x0005B6, "INSYS Microelectronics GmbH" }, + { 0x0005B7, "Arbor Technology Corp." }, + { 0x0005B8, "Electronic Design Associates, Inc." }, + { 0x0005B9, "Airvana, Inc." }, + { 0x0005BA, "Area Netwoeks, Inc." }, + { 0x0005BC, "Resorsys Ltd." }, + { 0x0005BD, "ROAX BV" }, + { 0x0005BE, "Kongsberg Seatex AS" }, + { 0x0005BF, "JustEzy Technology, Inc." }, + { 0x0005C0, "Digital Network Alacarte Co., Ltd." }, + { 0x0005C1, "A-Kyung Motion, Inc." }, + { 0x0005C2, "Digital Archway, Inc." }, + { 0x0005C3, "Pacific Instruments, Inc." }, + { 0x0005C4, "Telect, Inc." }, + { 0x0005C5, "Flaga HF" }, + { 0x0005C6, "Triz Communications" }, + { 0x0005C7, "I/F-COM A/S" }, + { 0x0005C8, "VERYTECH" }, + { 0x0005C9, "LG Innotek" }, + { 0x0005CA, "Hitron Technology, Inc." }, + { 0x0005CB, "ROIS Technologies, Inc." }, + { 0x0005CC, "Sumtel Communications, Inc." }, + { 0x0005CD, "Nippon Columbia" }, + { 0x0005CE, "Prolink Microsystems Corporation" }, + { 0x0005CF, "Thunder River Technologies, Inc." }, + { 0x0005D0, "Solinet Systems" }, + { 0x0005D1, "Metavector Technologies" }, + { 0x0005D2, "DAP Technologies" }, + { 0x0005D3, "CAC, Inc." }, + { 0x0005D4, "FutureSmart Networks, Inc." }, + { 0x0005D5, "Speedcom Wireless" }, + { 0x0005D6, "Titan Wireless" }, + { 0x0005D7, "Vista Imaging, Inc." }, + { 0x0005D8, "Arescom, Inc." }, + { 0x0005D9, "Techno Valley, Inc." }, + { 0x0005DA, "Apex Automationstechnik" }, + { 0x0005DB, "Nentec GmbH" }, + { 0x0005DC, "Cisco Systems, Inc." }, + { 0x0005DD, "Cisco Systems, Inc." }, + { 0x0005DE, "Gi Fone Korea, Inc." }, + { 0x0005DF, "Electronic Innovation, Inc." }, + { 0x0005E0, "Empirix Corp." }, + { 0x0005E1, "Trellis Photonics, Ltd." }, + { 0x0005E2, "Creativ Network Technologies" }, + { 0x0005E3, "LightSand Communications, Inc." }, + { 0x0005E4, "Red Lion Controls L.P." }, + { 0x0005E5, "Renishaw PLC" }, + { 0x0005E6, "Egenera, Inc." }, + { 0x0005E7, "Netrake" }, + { 0x0005E8, "TurboWave, Inc." }, + { 0x0005E9, "Unicess Networks, Inc." }, + { 0x0005EA, "Viewcast Corporation" }, + { 0x0005EB, "Blue Ridge Networks, Inc." }, + { 0x0005EC, "Mosaic Systems Inc." }, + { 0x0005ED, "Technikum Joanneaum GmbH" }, + { 0x0005EE, "BEWATOR Group" }, + { 0x0005EF, "ADOIR Digital Technology" }, + { 0x0005F0, "SATEC" }, + { 0x0005F1, "VRcom, Inc." }, + { 0x0005F2, "Power R, Inc." }, + { 0x0005F3, "Weboyn" }, + { 0x0005F4, "SystemBase Co., Ltd." }, + { 0x0005F5, "OYO Geospace Corp." }, + { 0x0005F6, "Young Chang Co. Ltd." }, + { 0x0005F7, "Analog Devices, Inc." }, + { 0x0005F8, "Real Time Access, Inc." }, + { 0x0005F9, "Diva Systems" }, + { 0x0005FA, "IPOptical, Inc." }, + { 0x0005FB, "Sharegate" }, + { 0x0005FC, "Schenck Pegasus Corp." }, + { 0x0005FD, "PacketLight Networks Ltd." }, + { 0x0005FE, "Traficon N.V." }, + { 0x0005FF, "SNS Solutions, Inc." }, + { 0x000600, "Tokyo Electronic Industry Co., Ltd." }, + { 0x000601, "Otanikeiki Co., Ltd." }, + { 0x000602, "Cirkitech Electronics Co." }, + { 0x000603, "Baker Hughes" }, + { 0x000604, "@Track Communications, Inc." }, + { 0x000605, "Inncom International, Inc." }, + { 0x000606, "RapidWan, Inc." }, + { 0x000607, "Omni-Directional Control Technology Inc." }, + { 0x000608, "At-Sky SAS" }, + { 0x000609, "Crossport Systems" }, + { 0x00060A, "Blue2space.com" }, + { 0x00060B, "Paceline Systems Corporation" }, + { 0x00060C, "Melco Industries, Inc." }, + { 0x00060D, "Wave7 Optics" }, + { 0x00060E, "IGSYS Systems, Inc." }, + { 0x00060F, "Narad Networks Inc" }, + { 0x000610, "Abeona Networks Inc" }, + { 0x000611, "Zeus Wireless, Inc." }, + { 0x000612, "Accusys, Inc." }, + { 0x000613, "Kawasaki Steel Corporation" }, + { 0x000614, "Prism Holdings" }, + { 0x000615, "Kimoto Electric Co., Ltd." }, + { 0x000616, "Tel Net Co., Ltd." }, + { 0x000617, "Redswitch Inc." }, + { 0x000618, "DigiPower Manufacturing Inc." }, + { 0x000619, "Connection Technology Systems" }, + { 0x00061A, "Zetari Inc." }, + { 0x00061B, "Portable Systems, IBM Japan Co, Ltd" }, + { 0x00061C, "Hoshino Metal Industries, Ltd." }, + { 0x00061D, "MIP Telecom, Inc." }, + { 0x00061E, "Maxan Systems" }, + { 0x00061F, "Vision Components GmbH" }, + { 0x000620, "Serial System Ltd." }, + { 0x000621, "Hinox, Co., Ltd." }, + { 0x000622, "Chung Fu Chen Yeh Enterprise Corp." }, + { 0x000623, "MGE UPS Systems France" }, + { 0x000624, "Gentner Communications" }, + { 0x000625, "The Linksys Group, Inc." }, + { 0x000626, "MWE GmbH" }, + { 0x000627, "Uniwide Technologies, Inc." }, + { 0x000628, "Cisco Systems, Inc." }, + { 0x000629, "IBM CORPORATION" }, + { 0x00062A, "Cisco Systems, Inc." }, + { 0x00062B, "INTRASERVER TECHNOLOGY" }, + { 0x00062C, "Network Robots, Inc." }, + { 0x00062D, "TouchStar Technologies, L.L.C." }, + { 0x00062E, "Aristos Logic Corp." }, + { 0x00062F, "Pivotech Systems Inc." }, + { 0x000630, "Adtranz Sweden" }, + { 0x000631, "Optical Solutions, Inc." }, + { 0x000632, "Mesco Engineering GmbH" }, + { 0x000633, "Heimann Biometric Systems GmbH" }, + { 0x000634, "GTE Airfone Inc." }, + { 0x000635, "PacketAir Networks, Inc." }, + { 0x000636, "Jedai Broadband Networks" }, + { 0x000637, "Toptrend-Meta Information (ShenZhen) Inc." }, + { 0x000638, "Sungjin C&C Co., Ltd." }, + { 0x000639, "Newtec" }, + { 0x00063A, "Dura Micro, Inc." }, + { 0x00063B, "Lineo Canada Corp." }, + { 0x00063C, "NMI Electronics Ltd" }, + { 0x00063D, "Microwave Data Systems Inc." }, + { 0x00063E, "Opthos Inc." }, + { 0x00063F, "Everex Communications Inc." }, + { 0x000640, "White Rock Networks" }, + { 0x000641, "ITCN" }, + { 0x000642, "Genetel Systems Inc." }, + { 0x000643, "SONO Computer Co., Ltd." }, + { 0x000644, "NEIX Inc." }, + { 0x000645, "Meisei Electric Co. Ltd." }, + { 0x000646, "ShenZhen XunBao Network Technology Co Ltd" }, + { 0x000647, "Etrali S.A." }, + { 0x000648, "Seedsware, Inc." }, + { 0x000649, "Quante" }, + { 0x00064A, "Honeywell Co., Ltd. (KOREA)" }, + { 0x00064B, "Alexon Co., Ltd." }, + { 0x00064C, "Invicta Networks, Inc." }, + { 0x00064D, "Sencore" }, + { 0x00064E, "Broad Net Technology Inc." }, + { 0x00064F, "PRO-NETS Technology Corporation" }, + { 0x000650, "Tiburon Networks, Inc." }, + { 0x000651, "Aspen Networks Inc." }, + { 0x000652, "Cisco Systems, Inc." }, + { 0x000653, "Cisco Systems, Inc." }, + { 0x000654, "Maxxio Technologies" }, + { 0x000655, "Yipee, Inc." }, + { 0x000656, "Tactel AB" }, + { 0x000657, "Market Central, Inc." }, + { 0x000658, "Helmut Fischer GmbH & Co. KG" }, + { 0x000659, "EAL (Apeldoorn) B.V." }, + { 0x00065A, "Strix Systems" }, + { 0x00065B, "Dell Computer Corp." }, + { 0x00065C, "Malachite Technologies, Inc." }, + { 0x00065D, "Heidelberg Web Systems" }, + { 0x00065E, "Photuris, Inc." }, + { 0x00065F, "ECI Telecom - NGTS Ltd." }, + { 0x000660, "NADEX Co., Ltd." }, + { 0x000661, "NIA Home Technologies Corp." }, + { 0x000662, "MBM Technology Ltd." }, + { 0x000663, "Human Technology Co., Ltd." }, + { 0x000664, "Fostex Corporation" }, + { 0x000665, "Summy Gikem, Inc." }, + { 0x000666, "Roving Networks" }, + { 0x000667, "Tripp Lite" }, + { 0x000668, "Vicon Industries Inc." }, + { 0x000669, "Datasound Laboratories Ltd" }, + { 0x00066A, "InfiniCon Systems, Inc." }, + { 0x00066B, "Sysmex Corporation" }, + { 0x00066C, "Robinson Corporation" }, + { 0x00066D, "Compuprint S.P.A." }, + { 0x00066E, "Delta Electronics, Inc." }, + { 0x00066F, "Korea Data Systems" }, + { 0x000670, "Upponetti Oy" }, + { 0x000671, "Softing AG" }, + { 0x000672, "Netezza" }, + { 0x000673, "Optelecom, Inc." }, + { 0x000674, "Spectrum Control, Inc." }, + { 0x000675, "Banderacom, Inc." }, + { 0x000676, "Novra Technologies, Inc." }, + { 0x000677, "SICK AG" }, + { 0x000678, "Marantz Japan, Inc." }, + { 0x000679, "Konami Corporation" }, + { 0x00067A, "JMP Systems" }, + { 0x00067B, "Toplink C&C Corporation" }, + { 0x00067C, "CISCO SYSTEMS, INC." }, + { 0x00067D, "Takasago Ltd." }, + { 0x0006C1, "CISCO SYSTEMS, INC." }, + { 0x000701, "RACAL-DATACOM" }, + { 0x000800, "MULTITECH SYSTEMS, INC." }, + { 0x0008C7, "COMPAQ COMPUTER CORPORATION" }, + { 0x000A27, "Apple Computer, Inc." }, + { 0x001000, "CABLE TELEVISION" }, + { 0x001001, "MCK COMMUNICATIONS" }, + { 0x001002, "ACTIA" }, + { 0x001003, "IMATRON, INC." }, + { 0x001004, "THE BRANTLEY COILE COMPANY,INC" }, + { 0x001005, "UEC COMMERCIAL" }, + { 0x001006, "RACAL RECORDERS LTD." }, + { 0x001007, "CISCO SYSTEMS, INC." }, + { 0x001008, "VIENNA SYSTEMS CORPORATION" }, + { 0x001009, "HORO QUARTZ" }, + { 0x00100A, "WILLIAMS COMMUNICATIONS GROUP" }, + { 0x00100B, "CISCO SYSTEMS, INC." }, + { 0x00100C, "ITO CO., LTD." }, + { 0x00100D, "CISCO SYSTEMS, INC." }, + { 0x00100E, "MICRO LINEAR COPORATION" }, + { 0x00100F, "INDUSTRIAL CPU SYSTEMS" }, + { 0x001010, "INITIO CORPORATION" }, + { 0x001011, "CISCO SYSTEMS, INC." }, + { 0x001012, "PROCESSOR SYSTEMS (I) PVT LTD" }, + { 0x001013, "INDUSTRIAL COMPUTER SOURCE" }, + { 0x001014, "CISCO SYSTEMS, INC." }, + { 0x001015, "OOMON INC." }, + { 0x001016, "T.SQWARE" }, + { 0x001017, "MICOS GMBH" }, + { 0x001018, "BROADCOM CORPORATION" }, + { 0x001019, "SIRONA DENTAL SYSTEMS" }, + { 0x00101A, "PICTURETEL CORP." }, + { 0x00101B, "CORNET TECHNOLOGY, INC." }, + { 0x00101C, "OHM TECHNOLOGIES INTL, LLC" }, + { 0x00101D, "WINBOND ELECTRONICS CORP." }, + { 0x00101E, "MATSUSHITA ELECTRONIC" }, + { 0x00101F, "CISCO SYSTEMS, INC." }, + { 0x001020, "WELCH ALLYN, DATA COLLECTION" }, + { 0x001021, "ENCANTO NETWORKS, INC." }, + { 0x001022, "SATCOM MEDIA CORPORATION" }, + { 0x001023, "FLOWWISE NETWORKS, INC." }, + { 0x001024, "NAGOYA ELECTRIC WORKS CO., LTD" }, + { 0x001025, "GRAYHILL INC." }, + { 0x001026, "ACCELERATED NETWORKS, INC." }, + { 0x001027, "L-3 COMMUNICATIONS EAST" }, + { 0x001028, "COMPUTER TECHNICA, INC." }, + { 0x001029, "CISCO SYSTEMS, INC." }, + { 0x00102A, "ZF MICROSYSTEMS, INC." }, + { 0x00102B, "UMAX DATA SYSTEMS, INC." }, + { 0x00102C, "Lasat Networks A/S" }, + { 0x00102D, "HITACHI SOFTWARE ENGINEERING" }, + { 0x00102E, "NETWORK SYSTEMS & TECHNOLOGIES" }, + { 0x00102F, "CISCO SYSTEMS, INC." }, + { 0x001030, "WI-LAN, INC." }, + { 0x001031, "OBJECTIVE COMMUNICATIONS, INC." }, + { 0x001032, "ALTA TECHNOLOGY" }, + { 0x001033, "ACCESSLAN COMMUNICATIONS, INC." }, + { 0x001034, "GNP COMPUTERS" }, + { 0x001035, "ELITEGROUP COMPUTER" }, + { 0x001036, "INTER-TEL INTEGRATED SYSTEMS" }, + { 0x001037, "CYQ'VE TECHNOLOGY CO., LTD." }, + { 0x001038, "MICRO RESEARCH INSTITUTE, INC." }, + { 0x001039, "VECTRON SYSTEMS GMBH" }, + { 0x00103A, "DIAMOND NETWORK TECH" }, + { 0x00103B, "HIPPI NETWORKING FORUM" }, + { 0x00103C, "IC ENSEMBLE, INC." }, + { 0x00103D, "PHASECOM, LTD." }, + { 0x00103E, "NETSCHOOLS CORPORATION" }, + { 0x00103F, "TOLLGRADE COMMUNICATIONS, INC." }, + { 0x001040, "INTERMEC CORPORATION" }, + { 0x001041, "BRISTOL BABCOCK, INC." }, + { 0x001042, "ALACRITECH" }, + { 0x001043, "A2 CORPORATION" }, + { 0x001044, "INNOLABS CORPORATION" }, + { 0x001045, "Nortel Networks" }, + { 0x001046, "ALCORN MCBRIDE INC." }, + { 0x001047, "ECHO ELETRIC CO. LTD." }, + { 0x001048, "HTRC AUTOMATION, INC." }, + { 0x001049, "SHORELINE TELEWORKS, INC." }, + { 0x00104A, "THE PARVUC CORPORATION" }, + { 0x00104B, "3COM CORPORATION" }, + { 0x00104C, "COMPUTER ACCESS TECHNOLOGY" }, + { 0x00104D, "SURTEC INDUSTRIES, INC." }, + { 0x00104E, "CEOLOGIC" }, + { 0x00104F, "STORAGE TECHNOLOGY CORPORATION" }, + { 0x001050, "RION CO., LTD." }, + { 0x001051, "CMICRO CORPORATION" }, + { 0x001052, "METTLER-TOLEDO (ALBSTADT) GMBH" }, + { 0x001053, "COMPUTER TECHNOLOGY CORP." }, + { 0x001054, "CISCO SYSTEMS, INC." }, + { 0x001055, "FUJITSU MICROELECTRONICS, INC." }, + { 0x001056, "SODICK CO., LTD." }, + { 0x001057, "Rebel.com, Inc." }, + { 0x001058, "ARROWPOINT COMMUNICATIONS,INC." }, + { 0x001059, "DIABLO RESEARCH CO. LLC" }, + { 0x00105A, "3COM CORPORATION" }, + { 0x00105B, "NET INSIGHT AB" }, + { 0x00105C, "QUANTUM DESIGNS (H.K.) LTD." }, + { 0x00105D, "DRAGER, BUSINESS UNIT" }, + { 0x00105E, "HEKIMIAN LABORATORIES, INC." }, + { 0x00105F, "IN-SNEC" }, + { 0x001060, "BILLIONTON SYSTEMS, INC." }, + { 0x001061, "HOSTLINK CORP." }, + { 0x001062, "NX SERVER, ILNC." }, + { 0x001063, "STARGUIDE DIGITAL NETWORKS" }, + { 0x001064, "DIGITAL EQUIPMENT CORP." }, + { 0x001065, "RADYNE CORPORATION" }, + { 0x001066, "ADVANCED CONTROL SYSTEMS, INC." }, + { 0x001067, "REDBACK NETWORKS, INC." }, + { 0x001068, "COMOS TELECOM" }, + { 0x001069, "HELIOSS COMMUNICATIONS, INC." }, + { 0x00106A, "DIGITAL MICROWAVE CORPORATION" }, + { 0x00106B, "SONUS NETWORKS, INC." }, + { 0x00106C, "INFRATEC PLUS GMBH" }, + { 0x00106D, "INTEGRITY COMMUNICATIONS, INC." }, + { 0x00106E, "TADIRAN COM. LTD." }, + { 0x00106F, "TRENTON TECHNOLOGY INC." }, + { 0x001070, "CARADON TREND LTD." }, + { 0x001071, "ADVANET INC." }, + { 0x001072, "GVN TECHNOLOGIES, INC." }, + { 0x001073, "TECHNOBOX, INC." }, + { 0x001074, "ATEN INTERNATIONAL CO., LTD." }, + { 0x001075, "Maxtor Corporation" }, + { 0x001076, "EUREM GMBH" }, + { 0x001077, "SAF DRIVE SYSTEMS, LTD." }, + { 0x001078, "NUERA COMMUNICATIONS, INC." }, + { 0x001079, "CISCO SYSTEMS, INC." }, + { 0x00107A, "AMBICOM, INC." }, + { 0x00107B, "CISCO SYSTEMS, INC." }, + { 0x00107C, "P-COM, INC." }, + { 0x00107D, "AURORA COMMUNICATIONS, LTD." }, + { 0x00107E, "BACHMANN ELECTRONIC GMBH" }, + { 0x00107F, "CRESTRON ELECTRONICS, INC." }, + { 0x001080, "METAWAVE COMMUNICATIONS" }, + { 0x001081, "DPS, INC." }, + { 0x001082, "JNA TELECOMMUNICATIONS LIMITED" }, + { 0x001083, "HEWLETT-PACKARD COMPANY" }, + { 0x001084, "K-BOT COMMUNICATIONS" }, + { 0x001085, "POLARIS COMMUNICATIONS, INC." }, + { 0x001086, "ATTO TECHNOLOGY, INC." }, + { 0x001087, "Xstreamis PLC" }, + { 0x001088, "AMERICAN NETWORKS INC." }, + { 0x001089, "WEBSONIC" }, + { 0x00108A, "TERALOGIC, INC." }, + { 0x00108B, "LASERANIMATION SOLLINGER GMBH" }, + { 0x00108C, "FUJITSU TELECOMMUNICATIONS" }, + { 0x00108D, "JOHNSON CONTROLS, INC." }, + { 0x00108E, "HUGH SYMONS CONCEPT" }, + { 0x00108F, "RAPTOR SYSTEMS" }, + { 0x001090, "CIMETRICS, INC." }, + { 0x001091, "NO WIRES NEEDED BV" }, + { 0x001092, "NETCORE INC." }, + { 0x001093, "CMS COMPUTERS, LTD." }, + { 0x001094, "ADTECH, INC." }, + { 0x001095, "THOMSON CONSUMER ELECTRONICS" }, + { 0x001096, "TRACEWELL SYSTEMS, INC." }, + { 0x001097, "WINNET METROPOLITAN" }, + { 0x001098, "STARNET TECHNOLOGIES, INC." }, + { 0x001099, "INNOMEDIA, INC." }, + { 0x00109A, "NETLINE" }, + { 0x00109B, "VIXEL CORPORATION" }, + { 0x00109C, "M-SYSTEM CO., LTD." }, + { 0x00109D, "CLARINET SYSTEMS, INC." }, + { 0x00109E, "AWARE, INC." }, + { 0x00109F, "PAVO, INC." }, + { 0x0010A0, "INNOVEX TECHNOLOGIES, INC." }, + { 0x0010A1, "KENDIN SEMICONDUCTOR, INC." }, + { 0x0010A2, "TNS" }, + { 0x0010A3, "OMNITRONIX, INC." }, + { 0x0010A4, "XIRCOM" }, + { 0x0010A5, "OXFORD INSTRUMENTS" }, + { 0x0010A6, "CISCO SYSTEMS, INC." }, + { 0x0010A7, "UNEX TECHNOLOGY CORPORATION" }, + { 0x0010A8, "RELIANCE COMPUTER CORP." }, + { 0x0010A9, "ADHOC TECHNOLOGIES" }, + { 0x0010AA, "MEDIA4, INC." }, + { 0x0010AB, "KOITO INDUSTRIES, LTD." }, + { 0x0010AC, "IMCI TECHNOLOGIES" }, + { 0x0010AD, "SOFTRONICS USB, INC." }, + { 0x0010AE, "SHINKO ELECTRIC INDUSTRIES CO." }, + { 0x0010AF, "TAC SYSTEMS, INC." }, + { 0x0010B0, "MERIDIAN TECHNOLOGY CORP." }, + { 0x0010B1, "FOR-A CO., LTD." }, + { 0x0010B2, "COACTIVE AESTHETICS" }, + { 0x0010B3, "NOKIA MULTIMEDIA TERMINALS" }, + { 0x0010B4, "ATMOSPHERE NETWORKS" }, + { 0x0010B5, "ACCTON TECHNOLOGY CORPORATION" }, + { 0x0010B6, "ENTRATA COMMUNICATIONS CORP." }, + { 0x0010B7, "COYOTE TECHNOLOGIES, LLC" }, + { 0x0010B8, "ISHIGAKI COMPUTER SYSTEM CO." }, + { 0x0010B9, "MAXTOR CORP." }, + { 0x0010BA, "MARTINHO-DAVIS SYSTEMS, INC." }, + { 0x0010BB, "DATA & INFORMATION TECHNOLOGY" }, + { 0x0010BC, "Nortel Networks" }, + { 0x0010BD, "THE TELECOMMUNICATION" }, + { 0x0010BE, "TELEXIS CORP." }, + { 0x0010BF, "INTER AIR WIRELESS" }, + { 0x0010C0, "ARMA, INC." }, + { 0x0010C1, "OI ELECTRIC CO., LTD." }, + { 0x0010C2, "WILLNET, INC." }, + { 0x0010C3, "CSI-CONTROL SYSTEMS" }, + { 0x0010C4, "MEDIA LINKS CO., LTD." }, + { 0x0010C5, "PROTOCOL TECHNOLOGIES, INC." }, + { 0x0010C6, "USI" }, + { 0x0010C7, "DATA TRANSMISSION NETWORK" }, + { 0x0010C8, "COMMUNICATIONS ELECTRONICS" }, + { 0x0010C9, "MITSUBISHI ELECTRONICS" }, + { 0x0010CA, "INTEGRAL ACCESS" }, + { 0x0010CB, "FACIT K.K." }, + { 0x0010CC, "CLP COMPUTER LOGISTIK" }, + { 0x0010CD, "INTERFACE CONCEPT" }, + { 0x0010CE, "VOLAMP, LTD." }, + { 0x0010CF, "FIBERLANE COMMUNICATIONS" }, + { 0x0010D0, "WITCOM, LTD." }, + { 0x0010D1, "Top Layer Networks, Inc." }, + { 0x0010D2, "NITTO TSUSHINKI CO., LTD" }, + { 0x0010D3, "GRIPS ELECTRONIC GMBH" }, + { 0x0010D4, "STORAGE COMPUTER CORPORATION" }, + { 0x0010D5, "IMASDE CANARIAS, S.A." }, + { 0x0010D6, "ITT A/CD" }, + { 0x0010D7, "ARGOSY RESEARCH INC." }, + { 0x0010D8, "CALISTA" }, + { 0x0010D9, "IBM JAPAN, FUJISAWA MT+D" }, + { 0x0010DA, "MOTION ENGINEERING, INC." }, + { 0x0010DB, "NETSCREEN TECHNOLOGIES, INC." }, + { 0x0010DC, "MICRO-STAR INTERNATIONAL" }, + { 0x0010DD, "ENABLE SEMICONDUCTOR, INC." }, + { 0x0010DE, "INTERNATIONAL DATACASTING" }, + { 0x0010DF, "RISE COMPUTER INC." }, + { 0x0010E0, "COBALT MICROSERVER, INC." }, + { 0x0010E1, "S.I. TECH, INC." }, + { 0x0010E2, "ARRAYCOMM, INC." }, + { 0x0010E3, "COMPAQ COMPUTER CORPORATION" }, + { 0x0010E4, "NSI CORPORATION" }, + { 0x0010E5, "SOLECTRON TEXAS" }, + { 0x0010E6, "APPLIED INTELLIGENT" }, + { 0x0010E7, "BREEZECOM" }, + { 0x0010E8, "TELOCITY, INCORPORATED" }, + { 0x0010E9, "RAIDTEC LTD." }, + { 0x0010EA, "ADEPT TECHNOLOGY" }, + { 0x0010EB, "SELSIUS SYSTEMS, ILNC." }, + { 0x0010EC, "RPCG, LLC" }, + { 0x0010ED, "SUNDANCE TECHNOLOGY, INC." }, + { 0x0010EE, "CTI PRODUCTS, INC." }, + { 0x0010EF, "DB NETWORKS, INC." }, + { 0x0010F0, "RITTAL-WERK RUDOLF LOH" }, + { 0x0010F1, "I-O CORPORATION" }, + { 0x0010F2, "ANTEC" }, + { 0x0010F3, "NEXCOM INTERNATIONAL CO., LTD." }, + { 0x0010F4, "VERTICAL NETWORKS, INC." }, + { 0x0010F5, "AMHERST SYSTEMS, INC." }, + { 0x0010F6, "CISCO SYSTEMS, INC." }, + { 0x0010F7, "IRIICHI TECHNOLOGIES" }, + { 0x0010F8, "KENWOOD TMI CORPORATION" }, + { 0x0010F9, "UNIQUE SYSTEMS, INC." }, + { 0x0010FA, "ZAYANTE, INC." }, + { 0x0010FB, "ZIDA TECHNOLOGIES LIMITED" }, + { 0x0010FC, "BROADBAND NETWORKS, INC." }, + { 0x0010FD, "COCOM A/S" }, + { 0x0010FE, "DIGITAL EQUIPMENT CORPORATION" }, + { 0x0010FF, "CISCO SYSTEMS, INC." }, + { 0x001C7C, "PERQ SYSTEMS CORPORATION" }, + { 0x002000, "LEXMARK INTERNATIONAL, INC." }, + { 0x002001, "DSP SOLUTIONS, INC." }, + { 0x002002, "SERITECH ENTERPRISE CO., LTD." }, + { 0x002003, "PIXEL POWER LTD." }, + { 0x002004, "YAMATAKE-HONEYWELL CO., LTD." }, + { 0x002005, "SIMPLE TECHNOLOGY" }, + { 0x002006, "GARRETT COMMUNICATIONS, INC." }, + { 0x002007, "SFA, INC." }, + { 0x002008, "CABLE & COMPUTER TECHNOLOGY" }, + { 0x002009, "PACKARD BELL ELEC., INC." }, + { 0x00200A, "SOURCE-COMM CORP." }, + { 0x00200B, "OCTAGON SYSTEMS CORP." }, + { 0x00200C, "ADASTRA SYSTEMS CORP." }, + { 0x00200D, "CARL ZEISS" }, + { 0x00200E, "SATELLITE TECHNOLOGY MGMT, INC" }, + { 0x00200F, "TANBAC CO., LTD." }, + { 0x002010, "JEOL SYSTEM TECHNOLOGY CO. LTD" }, + { 0x002011, "CANOPUS CO., LTD." }, + { 0x002012, "CAMTRONICS MEDICAL SYSTEMS" }, + { 0x002013, "DIVERSIFIED TECHNOLOGY, INC." }, + { 0x002014, "GLOBAL VIEW CO., LTD." }, + { 0x002015, "ACTIS COMPUTER SA" }, + { 0x002016, "SHOWA ELECTRIC WIRE & CABLE CO" }, + { 0x002017, "ORBOTECH" }, + { 0x002018, "CIS TECHNOLOGY INC." }, + { 0x002019, "OHLER GMBH" }, + { 0x00201A, "N-BASE SWITCH COMMUNICATIONS" }, + { 0x00201B, "NORTHERN TELECOM/NETWORK" }, + { 0x00201C, "EXCEL, INC." }, + { 0x00201D, "KATANA PRODUCTS" }, + { 0x00201E, "NETQUEST CORPORATION" }, + { 0x00201F, "BEST POWER TECHNOLOGY, INC." }, + { 0x002020, "MEGATRON COMPUTER INDUSTRIES" }, + { 0x002021, "ALGORITHMS SOFTWARE PVT. LTD." }, + { 0x002022, "TEKNIQUE, INC." }, + { 0x002023, "T.C. TECHNOLOGIES PTY. LTD" }, + { 0x002024, "PACIFIC COMMUNICATION SCIENCES" }, + { 0x002025, "CONTROL TECHNOLOGY, INC." }, + { 0x002026, "AMKLY SYSTEMS, INC." }, + { 0x002027, "MING FORTUNE INDUSTRY CO., LTD" }, + { 0x002028, "WEST EGG SYSTEMS, INC." }, + { 0x002029, "TELEPROCESSING PRODUCTS, INC." }, + { 0x00202A, "N.V. DZINE" }, + { 0x00202B, "ADVANCED TELECOMMUNICATIONS" }, + { 0x00202C, "WELLTRONIX CO., LTD." }, + { 0x00202D, "TAIYO CORPORATION" }, + { 0x00202E, "DAYSTAR DIGITAL" }, + { 0x00202F, "ZETA COMMUNICATIONS, LTD." }, + { 0x002030, "ANALOG & DIGITAL SYSTEMS" }, + { 0x002031, "ERTEC GMBH" }, + { 0x002032, "ALCATEL TAISEL" }, + { 0x002033, "SYNAPSE TECHNOLOGIES, INC." }, + { 0x002034, "ROTEC INDUSTRIEAUTOMATION GMBH" }, + { 0x002035, "IBM CORPORATION" }, + { 0x002036, "BMC SOFTWARE" }, + { 0x002037, "SEAGATE TECHNOLOGY" }, + { 0x002038, "VME MICROSYSTEMS INTERNATIONAL" }, + { 0x002039, "SCINETS" }, + { 0x00203A, "DIGITAL BI0METRICS INC." }, + { 0x00203B, "WISDM LTD." }, + { 0x00203C, "EUROTIME AB" }, + { 0x00203D, "NOVAR ELECTRONICS CORPORATION" }, + { 0x00203E, "LOGICAN TECHNOLOGIES, INC." }, + { 0x00203F, "JUKI CORPORATION" }, + { 0x002040, "Motorola Broadband Communications Sector" }, + { 0x002041, "DATA NET" }, + { 0x002042, "DATAMETRICS CORP." }, + { 0x002043, "NEURON COMPANY LIMITED" }, + { 0x002044, "GENITECH PTY LTD" }, + { 0x002045, "ION Networks, Inc." }, + { 0x002046, "CIPRICO, INC." }, + { 0x002047, "STEINBRECHER CORP." }, + { 0x002048, "Marconi Communications" }, + { 0x002049, "COMTRON, INC." }, + { 0x00204A, "PRONET GMBH" }, + { 0x00204B, "AUTOCOMPUTER CO., LTD." }, + { 0x00204C, "MITRON COMPUTER PTE LTD." }, + { 0x00204D, "INOVIS GMBH" }, + { 0x00204E, "NETWORK SECURITY SYSTEMS, INC." }, + { 0x00204F, "DEUTSCHE AEROSPACE AG" }, + { 0x002050, "KOREA COMPUTER INC." }, + { 0x002051, "PHOENIX DATA COMMUNUNICATIONS" }, + { 0x002052, "RAGULA SYSTEMS" }, + { 0x002053, "HUNTSVILLE MICROSYSTEMS, INC." }, + { 0x002054, "EASTERN RESEARCH, INC." }, + { 0x002055, "ALTECH CO., LTD." }, + { 0x002056, "NEOPRODUCTS" }, + { 0x002057, "TITZE DATENTECHNIK GMBH" }, + { 0x002058, "ALLIED SIGNAL INC." }, + { 0x002059, "MIRO COMPUTER PRODUCTS AG" }, + { 0x00205A, "COMPUTER IDENTICS" }, + { 0x00205B, "SKYLINE TECHNOLOGY" }, + { 0x00205C, "INTERNET SYSTEMS/ FLORIDA INC." }, + { 0x00205D, "NANOMATIC OY" }, + { 0x00205E, "CASTLE ROCK, INC." }, + { 0x00205F, "GAMMADATA COMPUTER GMBH" }, + { 0x002060, "ALCATEL ITALIA S.P.A." }, + { 0x002061, "DYNATECH COMMUNICATIONS, INC." }, + { 0x002062, "SCORPION LOGIC, LTD." }, + { 0x002063, "WIPRO INFOTECH LTD." }, + { 0x002064, "PROTEC MICROSYSTEMS, INC." }, + { 0x002065, "SUPERNET NETWORKING INC." }, + { 0x002066, "GENERAL MAGIC, INC." }, + { 0x002068, "ISDYNE" }, + { 0x002069, "ISDN SYSTEMS CORPORATION" }, + { 0x00206A, "OSAKA COMPUTER CORP." }, + { 0x00206B, "MINOLTA CO., LTD." }, + { 0x00206C, "EVERGREEN TECHNOLOGY CORP." }, + { 0x00206D, "DATA RACE, INC." }, + { 0x00206E, "XACT, INC." }, + { 0x00206F, "FLOWPOINT CORPORATION" }, + { 0x002070, "HYNET, LTD." }, + { 0x002071, "IBR GMBH" }, + { 0x002072, "WORKLINK INNOVATIONS" }, + { 0x002073, "FUSION SYSTEMS CORPORATION" }, + { 0x002074, "SUNGWOON SYSTEMS" }, + { 0x002075, "MOTOROLA COMMUNICATION ISRAEL" }, + { 0x002076, "REUDO CORPORATION" }, + { 0x002077, "KARDIOS SYSTEMS CORP." }, + { 0x002078, "RUNTOP, INC." }, + { 0x002079, "MIKRON GMBH" }, + { 0x00207A, "WISE COMMUNICATIONS, INC." }, + { 0x00207B, "LEVEL ONE COMMUNICATIONS" }, + { 0x00207C, "AUTEC GMBH" }, + { 0x00207D, "ADVANCED COMPUTER APPLICATIONS" }, + { 0x00207E, "FINECOM CO., LTD." }, + { 0x00207F, "KYOEI SANGYO CO., LTD." }, + { 0x002080, "SYNERGY (UK) LTD." }, + { 0x002081, "TITAN ELECTRONICS" }, + { 0x002082, "ONEAC CORPORATION" }, + { 0x002083, "PRESTICOM INCORPORATED" }, + { 0x002084, "OCE PRINTING SYSTEMS, GMBH" }, + { 0x002085, "EXIDE ELECTRONICS" }, + { 0x002086, "MICROTECH ELECTRONICS LIMITED" }, + { 0x002087, "MEMOTEC COMMUNICATIONS CORP." }, + { 0x002088, "GLOBAL VILLAGE COMMUNICATION" }, + { 0x002089, "T3PLUS NETWORKING, INC." }, + { 0x00208A, "SONIX COMMUNICATIONS, LTD." }, + { 0x00208B, "LAPIS TECHNOLOGIES, INC." }, + { 0x00208C, "GALAXY NETWORKS, INC." }, + { 0x00208D, "CMD TECHNOLOGY" }, + { 0x00208E, "CHEVIN SOFTWARE ENG. LTD." }, + { 0x00208F, "ECI TELECOM LTD." }, + { 0x002090, "ADVANCED COMPRESSION" }, + { 0x002091, "J125, NATIONAL SECURITY AGENCY" }, + { 0x002092, "CHESS ENGINEERING B.V." }, + { 0x002093, "LANDINGS TECHNOLOGY CORP." }, + { 0x002094, "CUBIX CORPORATION" }, + { 0x002095, "RIVA ELECTRONICS" }, + { 0x002096, "SIEBE ENVIRONMENTAL CONTROLS" }, + { 0x002097, "APPLIED SIGNAL TECHNOLOGY" }, + { 0x002098, "HECTRONIC AB" }, + { 0x002099, "BON ELECTRIC CO., LTD." }, + { 0x00209A, "THE 3DO COMPANY" }, + { 0x00209B, "ERSAT ELECTRONIC GMBH" }, + { 0x00209C, "PRIMARY ACCESS CORP." }, + { 0x00209D, "LIPPERT AUTOMATIONSTECHNIK" }, + { 0x00209E, "BROWN'S OPERATING SYSTEM" }, + { 0x00209F, "MERCURY COMPUTER SYSTEMS, INC." }, + { 0x0020A0, "OA LABORATORY CO., LTD." }, + { 0x0020A1, "DOVATRON" }, + { 0x0020A2, "GALCOM NETWORKING LTD." }, + { 0x0020A3, "DIVICOM INC." }, + { 0x0020A4, "MULTIPOINT NETWORKS" }, + { 0x0020A5, "API ENGINEERING" }, + { 0x0020A6, "PROXIM, INC." }, + { 0x0020A7, "PAIRGAIN TECHNOLOGIES, INC." }, + { 0x0020A8, "SAST TECHNOLOGY CORP." }, + { 0x0020A9, "WHITE HORSE INDUSTRIAL" }, + { 0x0020AA, "DIGIMEDIA VISION LTD." }, + { 0x0020AB, "MICRO INDUSTRIES CORP." }, + { 0x0020AC, "INTERFLEX DATENSYSTEME GMBH" }, + { 0x0020AD, "LINQ SYSTEMS" }, + { 0x0020AE, "ORNET DATA COMMUNICATION TECH." }, + { 0x0020AF, "3COM CORPORATION" }, + { 0x0020B0, "GATEWAY DEVICES, INC." }, + { 0x0020B1, "COMTECH RESEARCH INC." }, + { 0x0020B2, "GKD GESELLSCHAFT FUR" }, + { 0x0020B3, "SCLTEC COMMUNICATIONS SYSTEMS" }, + { 0x0020B4, "TERMA ELEKTRONIK AS" }, + { 0x0020B5, "YASKAWA ELECTRIC CORPORATION" }, + { 0x0020B6, "AGILE NETWORKS, INC." }, + { 0x0020B7, "NAMAQUA COMPUTERWARE" }, + { 0x0020B8, "PRIME OPTION, INC." }, + { 0x0020B9, "METRICOM, INC." }, + { 0x0020BA, "CENTER FOR HIGH PERFORMANCE" }, + { 0x0020BB, "ZAX CORPORATION" }, + { 0x0020BC, "JTEC PTY LTD." }, + { 0x0020BD, "NIOBRARA R & D CORPORATION" }, + { 0x0020BE, "LAN ACCESS CORP." }, + { 0x0020BF, "AEHR TEST SYSTEMS" }, + { 0x0020C0, "PULSE ELECTRONICS, INC." }, + { 0x0020C1, "TAIKO ELECTRIC WORKS, LTD." }, + { 0x0020C2, "TEXAS MEMORY SYSTEMS, INC." }, + { 0x0020C3, "COUNTER SOLUTIONS LTD." }, + { 0x0020C4, "INET,INC." }, + { 0x0020C5, "EAGLE TECHNOLOGY" }, + { 0x0020C6, "NECTEC" }, + { 0x0020C7, "AKAI Professional M.I. Corp." }, + { 0x0020C8, "LARSCOM INCORPORATED" }, + { 0x0020C9, "VICTRON BV" }, + { 0x0020CA, "DIGITAL OCEAN" }, + { 0x0020CB, "PRETEC ELECTRONICS CORP." }, + { 0x0020CC, "DIGITAL SERVICES, LTD." }, + { 0x0020CD, "HYBRID NETWORKS, INC." }, + { 0x0020CE, "LOGICAL DESIGN GROUP, INC." }, + { 0x0020CF, "TEST & MEASUREMENT SYSTEMS INC" }, + { 0x0020D0, "VERSALYNX CORPORATION" }, + { 0x0020D1, "MICROCOMPUTER SYSTEMS (M) SDN." }, + { 0x0020D2, "RAD DATA COMMUNICATIONS, LTD." }, + { 0x0020D3, "OST (OUEST STANDARD TELEMATIQU" }, + { 0x0020D4, "CABLETRON - ZEITTNET INC." }, + { 0x0020D5, "VIPA GMBH" }, + { 0x0020D6, "BREEZECOM" }, + { 0x0020D7, "JAPAN MINICOMPUTER SYSTEMS CO." }, + { 0x0020D8, "NETWAVE TECHNOLOGIES, INC." }, + { 0x0020D9, "PANASONIC TECHNOLOGIES, INC./" }, + { 0x0020DA, "XYLAN CORPORATION" }, + { 0x0020DB, "XNET TECHNOLOGY, INC." }, + { 0x0020DC, "DENSITRON TAIWAN LTD." }, + { 0x0020DD, "AWA LTD." }, + { 0x0020DE, "JAPAN DIGITAL LABORAT'Y CO.LTD" }, + { 0x0020DF, "KYOSAN ELECTRIC MFG. CO., LTD." }, + { 0x0020E0, "PREMAX ELECTRONICS, INC." }, + { 0x0020E1, "ALAMAR ELECTRONICS" }, + { 0x0020E2, "INFORMATION RESOURCE" }, + { 0x0020E3, "MCD KENCOM CORPORATION" }, + { 0x0020E4, "HSING TECH ENTERPRISE CO., LTD" }, + { 0x0020E5, "APEX DATA, INC." }, + { 0x0020E6, "LIDKOPING MACHINE TOOLS AB" }, + { 0x0020E7, "B&W NUCLEAR SERVICE COMPANY" }, + { 0x0020E8, "DATATREK CORPORATION" }, + { 0x0020E9, "DANTEL" }, + { 0x0020EA, "EFFICIENT NETWORKS, INC." }, + { 0x0020EB, "CINCINNATI MICROWAVE, INC." }, + { 0x0020EC, "TECHWARE SYSTEMS CORP." }, + { 0x0020ED, "GIGA-BYTE TECHNOLOGY CO., LTD." }, + { 0x0020EE, "GTECH CORPORATION" }, + { 0x0020EF, "USC CORPORATION" }, + { 0x0020F0, "UNIVERSAL MICROELECTRONICS CO." }, + { 0x0020F1, "ALTOS INDIA LIMITED" }, + { 0x0020F2, "SUN MICROSYSTEMS, INC." }, + { 0x0020F3, "RAYNET CORPORATION" }, + { 0x0020F4, "SPECTRIX CORPORATION" }, + { 0x0020F5, "PANDATEL AG" }, + { 0x0020F6, "NET TEK AND KARLNET, INC." }, + { 0x0020F7, "CYBERDATA" }, + { 0x0020F8, "CARRERA COMPUTERS, INC." }, + { 0x0020F9, "PARALINK NETWORKS, INC." }, + { 0x0020FA, "GDE SYSTEMS, INC." }, + { 0x0020FB, "OCTEL COMMUNICATIONS CORP." }, + { 0x0020FC, "MATROX" }, + { 0x0020FD, "ITV TECHNOLOGIES, INC." }, + { 0x0020FE, "TOPWARE INC. / GRAND COMPUTER" }, + { 0x0020FF, "SYMMETRICAL TECHNOLOGIES" }, + { 0x003000, "ALLWELL TECHNOLOGY CORP." }, + { 0x003001, "SMP" }, + { 0x003002, "Expand Networks" }, + { 0x003003, "Phasys Ltd." }, + { 0x003004, "LEADTEK RESEARCH INC." }, + { 0x003005, "Fujitsu Siemens Computers" }, + { 0x003006, "SUPERPOWER COMPUTER" }, + { 0x003007, "OPTI, INC." }, + { 0x003008, "AVIO DIGITAL, INC." }, + { 0x003009, "Tachion Networks, Inc." }, + { 0x00300A, "AZTECH SYSTEMS LTD." }, + { 0x00300B, "mPHASE Technologies, Inc." }, + { 0x00300C, "CONGRUENCY, LTD." }, + { 0x00300D, "MMC Technology, Inc." }, + { 0x00300E, "Klotz Digital AG" }, + { 0x00300F, "IMT - Information Management T" }, + { 0x003010, "VISIONETICS INTERNATIONAL" }, + { 0x003011, "HMS FIELDBUS SYSTEMS AB" }, + { 0x003012, "DIGITAL ENGINEERING LTD." }, + { 0x003013, "NEC Corporation" }, + { 0x003014, "DIVIO, INC." }, + { 0x003015, "CP CLARE CORP." }, + { 0x003016, "ISHIDA CO., LTD." }, + { 0x003017, "TERASTACK LTD." }, + { 0x003018, "Jetway Information Co., Ltd." }, + { 0x003019, "CISCO SYSTEMS, INC." }, + { 0x00301A, "SMARTBRIDGES PTE. LTD." }, + { 0x00301B, "SHUTTLE, INC." }, + { 0x00301C, "ALTVATER AIRDATA SYSTEMS" }, + { 0x00301D, "SKYSTREAM, INC." }, + { 0x00301E, "3COM Europe Ltd." }, + { 0x00301F, "OPTICAL NETWORKS, INC." }, + { 0x003020, "TSI, Inc.." }, + { 0x003021, "HSING TECH. ENTERPRISE CO.,LTD" }, + { 0x003022, "Fong Kai Industrial Co., Ltd." }, + { 0x003023, "COGENT COMPUTER SYSTEMS, INC." }, + { 0x003024, "CISCO SYSTEMS, INC." }, + { 0x003025, "CHECKOUT COMPUTER SYSTEMS, LTD" }, + { 0x003026, "HEITEL" }, + { 0x003027, "KERBANGO, INC." }, + { 0x003028, "FASE Saldatura srl" }, + { 0x003029, "OPICOM" }, + { 0x00302A, "SOUTHERN INFORMATION" }, + { 0x00302B, "INALP NETWORKS, INC." }, + { 0x00302C, "SYLANTRO SYSTEMS CORPORATION" }, + { 0x00302D, "QUANTUM BRIDGE COMMUNICATIONS" }, + { 0x00302E, "Hoft & Wessel AG" }, + { 0x00302F, "Smiths Industries" }, + { 0x003030, "HARMONIX CORPORATION" }, + { 0x003031, "LIGHTWAVE COMMUNICATIONS, INC." }, + { 0x003032, "MAGICRAM, INC." }, + { 0x003033, "ORIENT TELECOM CO., LTD." }, + { 0x003036, "RMP ELEKTRONIKSYSTEME GMBH" }, + { 0x003037, "Packard Bell Nec Services" }, + { 0x003038, "XCP, INC." }, + { 0x003039, "SOFTBOOK PRESS" }, + { 0x00303A, "MAATEL" }, + { 0x00303B, "PowerCom Technology" }, + { 0x00303C, "ONNTO CORP." }, + { 0x00303D, "IVA CORPORATION" }, + { 0x00303E, "Radcom Ltd." }, + { 0x00303F, "TurboComm Tech Inc." }, + { 0x003040, "CISCO SYSTEMS, INC." }, + { 0x003041, "SAEJIN T & M CO., LTD." }, + { 0x003042, "DeTeWe-Deutsche Telephonwerke" }, + { 0x003043, "IDREAM TECHNOLOGIES, PTE. LTD." }, + { 0x003044, "Portsmith LLC" }, + { 0x003045, "Village Networks, Inc. (VNI)" }, + { 0x003046, "Controlled Electronic Manageme" }, + { 0x003047, "NISSEI ELECTRIC CO., LTD." }, + { 0x003048, "Supermicro Computer, Inc." }, + { 0x003049, "BRYANT TECHNOLOGY, LTD." }, + { 0x00304A, "FRAUNHOFER INSTITUTE IMS" }, + { 0x00304B, "ORBACOM SYSTEMS, INC." }, + { 0x00304C, "APPIAN COMMUNICATIONS, INC." }, + { 0x00304D, "ESI" }, + { 0x00304E, "BUSTEC PRODUCTION LTD." }, + { 0x00304F, "PLANET Technology Corporation" }, + { 0x003050, "Versa Technology" }, + { 0x003051, "ORBIT AVIONIC & COMMUNICATION" }, + { 0x003052, "ELASTIC NETWORKS" }, + { 0x003053, "Basler AG" }, + { 0x003054, "CASTLENET TECHNOLOGY, INC." }, + { 0x003055, "Hitachi Semiconductor America," }, + { 0x003056, "Beck IPC GmbH" }, + { 0x003057, "E-Tel Corporation" }, + { 0x003058, "API MOTION" }, + { 0x003059, "DIGITAL-LOGIC AG" }, + { 0x00305A, "TELGEN CORPORATION" }, + { 0x00305B, "MODULE DEPARTMENT" }, + { 0x00305C, "SMAR Laboratories Corp." }, + { 0x00305D, "DIGITRA SYSTEMS, INC." }, + { 0x00305E, "Abelko Innovation" }, + { 0x00305F, "IMACON APS" }, + { 0x003060, "STARMATIX, INC." }, + { 0x003061, "MobyTEL" }, + { 0x003062, "PATH 1 NETWORK TECHNOL'S INC." }, + { 0x003063, "SANTERA SYSTEMS, INC." }, + { 0x003064, "ADLINK TECHNOLOGY, INC." }, + { 0x003065, "APPLE COMPUTER, INC." }, + { 0x003066, "DIGITAL WIRELESS CORPORATION" }, + { 0x003067, "BIOSTAR MICROTECH INT'L CORP." }, + { 0x003068, "CYBERNETICS TECH. CO., LTD." }, + { 0x003069, "IMPACCT TECHNOLOGY CORP." }, + { 0x00306A, "PENTA MEDIA CO., LTD." }, + { 0x00306B, "CMOS SYSTEMS, INC." }, + { 0x00306C, "Hitex Holding GmbH" }, + { 0x00306D, "LUCENT TECHNOLOGIES" }, + { 0x00306E, "HEWLETT PACKARD" }, + { 0x00306F, "SEYEON TECH. CO., LTD." }, + { 0x003070, "1Net Corporation" }, + { 0x003071, "Cisco Systems, Inc." }, + { 0x003072, "INTELLIBYTE INC." }, + { 0x003073, "International Microsystems, In" }, + { 0x003074, "EQUIINET LTD." }, + { 0x003075, "ADTECH" }, + { 0x003076, "Akamba Corporation" }, + { 0x003077, "ONPREM NETWORKS" }, + { 0x003078, "Cisco Systems, Inc." }, + { 0x003079, "CQOS, INC." }, + { 0x00307A, "Advanced Technology & Systems" }, + { 0x00307B, "Cisco Systems, Inc." }, + { 0x00307C, "ADID SA" }, + { 0x00307D, "GRE AMERICA, INC." }, + { 0x00307E, "Redflex Communication Systems" }, + { 0x00307F, "IRLAN LTD." }, + { 0x003080, "CISCO SYSTEMS, INC." }, + { 0x003081, "ALTOS C&C" }, + { 0x003082, "TAIHAN ELECTRIC WIRE CO., LTD." }, + { 0x003083, "Ivron Systems" }, + { 0x003084, "ALLIED TELESYN INTERNAIONAL" }, + { 0x003085, "CISCO SYSTEMS, INC." }, + { 0x003086, "Transistor Devices, Inc." }, + { 0x003087, "VEGA GRIESHABER KG" }, + { 0x003088, "Siara Systems, Inc." }, + { 0x003089, "Spectrapoint Wireless, LLC" }, + { 0x00308A, "NICOTRA SISTEMI S.P.A" }, + { 0x00308B, "Brix Networks" }, + { 0x00308C, "ADVANCED DIGITAL INFORMATION" }, + { 0x00308D, "PINNACLE SYSTEMS, INC." }, + { 0x00308E, "CROSS MATCH TECHNOLOGIES, INC." }, + { 0x00308F, "MICRILOR, Inc." }, + { 0x003090, "CYRA TECHNOLOGIES, INC." }, + { 0x003091, "TAIWAN FIRST LINE ELEC. CORP." }, + { 0x003092, "ModuNORM GmbH" }, + { 0x003093, "SONNET TECHNOLOGIES, INC." }, + { 0x003094, "Cisco Systems, Inc." }, + { 0x003095, "Procomp Informatics, Ltd." }, + { 0x003096, "CISCO SYSTEMS, INC." }, + { 0x003097, "EXOMATIC AB" }, + { 0x003098, "Global Converging Technologies" }, + { 0x003099, "BOENIG UND KALLENBACH OHG" }, + { 0x00309A, "ASTRO TERRA CORP." }, + { 0x00309B, "Smartware" }, + { 0x00309C, "Timing Applications, Inc." }, + { 0x00309D, "Nimble Microsystems, Inc." }, + { 0x00309E, "WORKBIT CORPORATION." }, + { 0x00309F, "AMBER NETWORKS" }, + { 0x0030A0, "TYCO SUBMARINE SYSTEMS, LTD." }, + { 0x0030A1, "OPTI TECH CO., LTD." }, + { 0x0030A2, "Lightner Engineering" }, + { 0x0030A3, "CISCO SYSTEMS, INC." }, + { 0x0030A4, "Woodwind Communications System" }, + { 0x0030A5, "ACTIVE POWER" }, + { 0x0030A6, "VIANET TECHNOLOGIES, LTD." }, + { 0x0030A7, "SCHWEITZER ENGINEERING" }, + { 0x0030A8, "OL'E COMMUNICATIONS, INC." }, + { 0x0030A9, "Netiverse, Inc." }, + { 0x0030AA, "AXUS MICROSYSTEMS, INC." }, + { 0x0030AB, "DELTA NETWORKS, INC." }, + { 0x0030AC, "Systeme Lauer GmbH & Co., Ltd." }, + { 0x0030AD, "SHANGHAI COMMUNICATION" }, + { 0x0030AE, "Times N System, Inc." }, + { 0x0030AF, "Honeywell Reqelsysteme GmbH" }, + { 0x0030B0, "Convergenet Technologies" }, + { 0x0030B1, "GOC GESELLSCHAFT FUR OPTISCHE" }, + { 0x0030B2, "WESCAM - HEALDSBURG" }, + { 0x0030B3, "San Valley Systems, Inc." }, + { 0x0030B4, "INTERSIL CORP." }, + { 0x0030B5, "Tadiran Microwave Networks" }, + { 0x0030B6, "CISCO SYSTEMS, INC." }, + { 0x0030B7, "Teletrol Systems, Inc." }, + { 0x0030B8, "RiverDelta Networks" }, + { 0x0030B9, "ECTEL" }, + { 0x0030BA, "AC&T SYSTEM CO., LTD." }, + { 0x0030BB, "CacheFlow, Inc." }, + { 0x0030BC, "Optronic AG" }, + { 0x0030BD, "BELKIN COMPONENTS" }, + { 0x0030BE, "City-Net Technology, Inc." }, + { 0x0030BF, "MULTIDATA GMBH" }, + { 0x0030C0, "Lara Technology, Inc." }, + { 0x0030C1, "HEWLETT-PACKARD" }, + { 0x0030C2, "COMONE" }, + { 0x0030C3, "FLUECKIGER ELEKTRONIK AG" }, + { 0x0030C4, "Niigata Canotec Co., Inc." }, + { 0x0030C5, "CADENCE DESIGN SYSTEMS" }, + { 0x0030C6, "CONTROL SOLUTIONS, INC." }, + { 0x0030C7, "MACROMATE CORP." }, + { 0x0030C8, "GAD LINE, LTD." }, + { 0x0030C9, "LuxN, N" }, + { 0x0030CA, "Discovery Com" }, + { 0x0030CB, "OMNI FLOW COMPUTERS, INC." }, + { 0x0030CC, "Tenor Networks, Inc." }, + { 0x0030CD, "CONEXANT SYSTEMS, INC." }, + { 0x0030CE, "Zaffire" }, + { 0x0030CF, "TWO TECHNOLOGIES, INC." }, + { 0x0030D1, "INOVA CORPORATION" }, + { 0x0030D2, "WIN TECHNOLOGIES, CO., LTD." }, + { 0x0030D3, "Agilent Technologies" }, + { 0x0030D4, "COMTIER" }, + { 0x0030D5, "DResearch GmbH" }, + { 0x0030D6, "MSC VERTRIEBS GMBH" }, + { 0x0030D7, "Innovative Systems, L.L.C." }, + { 0x0030D8, "SITEK" }, + { 0x0030D9, "DATACORE SOFTWARE CORP." }, + { 0x0030DA, "COMTREND CO." }, + { 0x0030DB, "Mindready Solutions, Inc." }, + { 0x0030DC, "RIGHTECH CORPORATION" }, + { 0x0030DD, "INDIGITA CORPORATION" }, + { 0x0030DE, "WAGO Kontakttechnik GmbH" }, + { 0x0030DF, "KB/TEL TELECOMUNICACIONES" }, + { 0x0030E0, "OXFORD SEMICONDUCTOR LTD." }, + { 0x0030E1, "ACROTRON SYSTEMS, INC." }, + { 0x0030E2, "GARNET SYSTEMS CO., LTD." }, + { 0x0030E3, "SEDONA NETWORKS CORP." }, + { 0x0030E4, "CHIYODA SYSTEM RIKEN" }, + { 0x0030E5, "Amper Datos S.A." }, + { 0x0030E6, "SIEMENS MEDICAL SYSTEMS" }, + { 0x0030E7, "CNF MOBILE SOLUTIONS, INC." }, + { 0x0030E8, "ENSIM CORP." }, + { 0x0030E9, "GMA COMMUNICATION MANUFACT'G" }, + { 0x0030EA, "TeraForce Technology Corporation" }, + { 0x0030EB, "TURBONET COMMUNICATIONS, INC." }, + { 0x0030EC, "BORGARDT" }, + { 0x0030ED, "Expert Magnetics Corp." }, + { 0x0030EE, "DSG Technology, Inc." }, + { 0x0030EF, "NEON TECHNOLOGY, INC." }, + { 0x0030F0, "Uniform Industrial Corp." }, + { 0x0030F1, "Accton Technology Corp." }, + { 0x0030F2, "CISCO SYSTEMS, INC." }, + { 0x0030F3, "At Work Computers" }, + { 0x0030F4, "STARDOT TECHNOLOGIES" }, + { 0x0030F5, "Wild Lab. Ltd." }, + { 0x0030F6, "SECURELOGIX CORPORATION" }, + { 0x0030F7, "RAMIX INC." }, + { 0x0030F8, "Dynapro Systems, Inc." }, + { 0x0030F9, "Sollae Systems Co., Ltd." }, + { 0x0030FA, "TELICA, INC." }, + { 0x0030FB, "AZS Technology AG" }, + { 0x0030FC, "Terawave Communications, Inc." }, + { 0x0030FD, "INTEGRATED SYSTEMS DESIGN" }, + { 0x0030FE, "DSA GmbH" }, + { 0x0030FF, "DATAFAB SYSTEMS, INC." }, + { 0x004000, "PCI COMPONENTES DA AMZONIA LTD" }, + { 0x004001, "ZYXEL COMMUNICATIONS, INC." }, + { 0x004002, "PERLE SYSTEMS LIMITED" }, + { 0x004003, "WESTINGHOUSE PROCESS CONTROL" }, + { 0x004004, "ICM CO. LTD." }, + { 0x004005, "ANI COMMUNICATIONS INC." }, + { 0x004006, "SAMPO TECHNOLOGY CORPORATION" }, + { 0x004007, "TELMAT INFORMATIQUE" }, + { 0x004008, "A PLUS INFO CORPORATION" }, + { 0x004009, "TACHIBANA TECTRON CO., LTD." }, + { 0x00400A, "PIVOTAL TECHNOLOGIES, INC." }, + { 0x00400B, "CISCO SYSTEMS, INC." }, + { 0x00400C, "GENERAL MICRO SYSTEMS, INC." }, + { 0x00400D, "LANNET DATA COMMUNICATIONS,LTD" }, + { 0x00400E, "MEMOTEC COMMUNICATIONS, INC." }, + { 0x00400F, "DATACOM TECHNOLOGIES" }, + { 0x004010, "SONIC SYSTEMS, INC." }, + { 0x004011, "ANDOVER CONTROLS CORPORATION" }, + { 0x004012, "WINDATA, INC." }, + { 0x004013, "NTT DATA COMM. SYSTEMS CORP." }, + { 0x004014, "COMSOFT GMBH" }, + { 0x004015, "ASCOM INFRASYS AG" }, + { 0x004016, "HADAX ELECTRONICS, INC." }, + { 0x004017, "XCD INC." }, + { 0x004018, "ADOBE SYSTEMS, INC." }, + { 0x004019, "AEON SYSTEMS, INC." }, + { 0x00401A, "FUJI ELECTRIC CO., LTD." }, + { 0x00401B, "PRINTER SYSTEMS CORP." }, + { 0x00401C, "AST RESEARCH, INC." }, + { 0x00401D, "INVISIBLE SOFTWARE, INC." }, + { 0x00401E, "ICC" }, + { 0x00401F, "COLORGRAPH LTD" }, + { 0x004020, "PINACL COMMUNICATION" }, + { 0x004021, "RASTER GRAPHICS" }, + { 0x004022, "KLEVER COMPUTERS, INC." }, + { 0x004023, "LOGIC CORPORATION" }, + { 0x004024, "COMPAC INC." }, + { 0x004025, "MOLECULAR DYNAMICS" }, + { 0x004026, "MELCO, INC." }, + { 0x004027, "SMC MASSACHUSETTS, INC." }, + { 0x004028, "NETCOMM LIMITED" }, + { 0x004029, "COMPEX" }, + { 0x00402A, "CANOGA-PERKINS" }, + { 0x00402B, "TRIGEM COMPUTER, INC." }, + { 0x00402C, "ISIS DISTRIBUTED SYSTEMS, INC." }, + { 0x00402D, "HARRIS ADACOM CORPORATION" }, + { 0x00402E, "PRECISION SOFTWARE, INC." }, + { 0x00402F, "XLNT DESIGNS INC." }, + { 0x004030, "GK COMPUTER" }, + { 0x004031, "KOKUSAI ELECTRIC CO., LTD" }, + { 0x004032, "DIGITAL COMMUNICATIONS" }, + { 0x004033, "ADDTRON TECHNOLOGY CO., LTD." }, + { 0x004034, "BUSTEK CORPORATION" }, + { 0x004035, "OPCOM" }, + { 0x004036, "TRIBE COMPUTER WORKS, INC." }, + { 0x004037, "SEA-ILAN, INC." }, + { 0x004038, "TALENT ELECTRIC INCORPORATED" }, + { 0x004039, "OPTEC DAIICHI DENKO CO., LTD." }, + { 0x00403A, "IMPACT TECHNOLOGIES" }, + { 0x00403B, "SYNERJET INTERNATIONAL CORP." }, + { 0x00403C, "FORKS, INC." }, + { 0x00403D, "TERADATA" }, + { 0x00403E, "RASTER OPS CORPORATION" }, + { 0x00403F, "SSANGYONG COMPUTER SYSTEMS" }, + { 0x004040, "RING ACCESS, INC." }, + { 0x004041, "FUJIKURA LTD." }, + { 0x004042, "N.A.T. GMBH" }, + { 0x004043, "NOKIA TELECOMMUNICATIONS" }, + { 0x004044, "QNIX COMPUTER CO., LTD." }, + { 0x004045, "TWINHEAD CORPORATION" }, + { 0x004046, "UDC RESEARCH LIMITED" }, + { 0x004047, "WIND RIVER SYSTEMS" }, + { 0x004048, "SMD INFORMATICA S.A." }, + { 0x004049, "TEGIMENTA AG" }, + { 0x00404A, "WEST AUSTRALIAN DEPARTMENT" }, + { 0x00404B, "MAPLE COMPUTER SYSTEMS" }, + { 0x00404C, "HYPERTEC PTY LTD." }, + { 0x00404D, "TELECOMMUNICATIONS TECHNIQUES" }, + { 0x00404E, "FLUENT, INC." }, + { 0x00404F, "SPACE & NAVAL WARFARE SYSTEMS" }, + { 0x004050, "IRONICS, INCORPORATED" }, + { 0x004051, "GRACILIS, INC." }, + { 0x004052, "STAR TECHNOLOGIES, INC." }, + { 0x004053, "AMPRO COMPUTERS" }, + { 0x004054, "CONNECTION MACHINES SERVICES" }, + { 0x004055, "METRONIX GMBH" }, + { 0x004056, "MCM JAPAN LTD." }, + { 0x004057, "LOCKHEED - SANDERS" }, + { 0x004058, "KRONOS, INC." }, + { 0x004059, "YOSHIDA KOGYO K. K." }, + { 0x00405A, "GOLDSTAR INFORMATION & COMM." }, + { 0x00405B, "FUNASSET LIMITED" }, + { 0x00405C, "FUTURE SYSTEMS, INC." }, + { 0x00405D, "STAR-TEK, INC." }, + { 0x00405E, "NORTH HILLS ISRAEL" }, + { 0x00405F, "AFE COMPUTERS LTD." }, + { 0x004060, "COMENDEC LTD" }, + { 0x004061, "DATATECH ENTERPRISES CO., LTD." }, + { 0x004062, "E-SYSTEMS, INC./GARLAND DIV." }, + { 0x004063, "VIA TECHNOLOGIES, INC." }, + { 0x004064, "KLA INSTRUMENTS CORPORATION" }, + { 0x004065, "GTE SPACENET" }, + { 0x004066, "HITACHI CABLE, LTD." }, + { 0x004067, "OMNIBYTE CORPORATION" }, + { 0x004068, "EXTENDED SYSTEMS" }, + { 0x004069, "LEMCOM SYSTEMS, INC." }, + { 0x00406A, "KENTEK INFORMATION SYSTEMS,INC" }, + { 0x00406B, "SYSGEN" }, + { 0x00406C, "COPERNIQUE" }, + { 0x00406D, "LANCO, INC." }, + { 0x00406E, "COROLLARY, INC." }, + { 0x00406F, "SYNC RESEARCH INC." }, + { 0x004070, "INTERWARE CO., LTD." }, + { 0x004071, "ATM COMPUTER GMBH" }, + { 0x004072, "APPLIED INNOVATION, INC." }, + { 0x004073, "BASS ASSOCIATES" }, + { 0x004074, "CABLE AND WIRELESS" }, + { 0x004075, "M-TRADE (UK) LTD" }, + { 0x004076, "Sun Conversion Technologies" }, + { 0x004077, "MAXTON TECHNOLOGY CORPORATION" }, + { 0x004078, "WEARNES AUTOMATION PTE LTD" }, + { 0x004079, "JUKO MANUFACTURE COMPANY, LTD." }, + { 0x00407A, "SOCIETE D'EXPLOITATION DU CNIT" }, + { 0x00407B, "SCIENTIFIC ATLANTA" }, + { 0x00407C, "QUME CORPORATION" }, + { 0x00407D, "EXTENSION TECHNOLOGY CORP." }, + { 0x00407E, "EVERGREEN SYSTEMS, INC." }, + { 0x00407F, "AGEMA INFRARED SYSTEMS AB" }, + { 0x004080, "ATHENIX CORPORATION" }, + { 0x004081, "MANNESMANN SCANGRAPHIC GMBH" }, + { 0x004082, "LABORATORY EQUIPMENT CORP." }, + { 0x004083, "TDA INDUSTRIA DE PRODUTOS" }, + { 0x004084, "HONEYWELL INC." }, + { 0x004085, "SAAB INSTRUMENTS AB" }, + { 0x004086, "MICHELS & KLEBERHOFF COMPUTER" }, + { 0x004087, "UBITREX CORPORATION" }, + { 0x004088, "MOBIUS TECHNOLOGIES, INC." }, + { 0x004089, "MEIDENSHA CORPORATION" }, + { 0x00408A, "TPS TELEPROCESSING SYS. GMBH" }, + { 0x00408B, "RAYLAN CORPORATION" }, + { 0x00408C, "AXIS COMMUNICATIONS AB" }, + { 0x00408D, "THE GOODYEAR TIRE & RUBBER CO." }, + { 0x00408E, "DIGILOG, INC." }, + { 0x00408F, "WM-DATA MINFO AB" }, + { 0x004090, "ANSEL COMMUNICATIONS" }, + { 0x004091, "PROCOMP INDUSTRIA ELETRONICA" }, + { 0x004092, "ASP COMPUTER PRODUCTS, INC." }, + { 0x004093, "PAXDATA NETWORKS LTD." }, + { 0x004094, "SHOGRAPHICS, INC." }, + { 0x004095, "R.P.T. INTERGROUPS INT'L LTD." }, + { 0x004096, "Aironet Wireless Communication" }, + { 0x004097, "DATEX DIVISION OF" }, + { 0x004098, "DRESSLER GMBH & CO." }, + { 0x004099, "NEWGEN SYSTEMS CORP." }, + { 0x00409A, "NETWORK EXPRESS, INC." }, + { 0x00409B, "HAL COMPUTER SYSTEMS INC." }, + { 0x00409C, "TRANSWARE" }, + { 0x00409D, "DIGIBOARD, INC." }, + { 0x00409E, "CONCURRENT TECHNOLOGIES LTD." }, + { 0x00409F, "LANCAST/CASAT TECHNOLOGY, INC." }, + { 0x0040A0, "GOLDSTAR CO., LTD." }, + { 0x0040A1, "ERGO COMPUTING" }, + { 0x0040A2, "KINGSTAR TECHNOLOGY INC." }, + { 0x0040A3, "MICROUNITY SYSTEMS ENGINEERING" }, + { 0x0040A4, "ROSE ELECTRONICS" }, + { 0x0040A5, "CLINICOMP INTL." }, + { 0x0040A6, "Cray, Inc." }, + { 0x0040A7, "ITAUTEC PHILCO S.A." }, + { 0x0040A8, "IMF INTERNATIONAL LTD." }, + { 0x0040A9, "DATACOM INC." }, + { 0x0040AA, "VALMET AUTOMATION INC." }, + { 0x0040AB, "ROLAND DG CORPORATION" }, + { 0x0040AC, "SUPER WORKSTATION, INC." }, + { 0x0040AD, "SMA REGELSYSTEME GMBH" }, + { 0x0040AE, "DELTA CONTROLS, INC." }, + { 0x0040AF, "DIGITAL PRODUCTS, INC." }, + { 0x0040B0, "BYTEX CORPORATION, ENGINEERING" }, + { 0x0040B1, "CODONICS INC." }, + { 0x0040B2, "SYSTEMFORSCHUNG" }, + { 0x0040B3, "PAR MICROSYSTEMS CORPORATION" }, + { 0x0040B4, "NEXTCOM K.K." }, + { 0x0040B5, "VIDEO TECHNOLOGY COMPUTERS LTD" }, + { 0x0040B6, "COMPUTERM CORPORATION" }, + { 0x0040B7, "STEALTH COMPUTER SYSTEMS" }, + { 0x0040B8, "IDEA ASSOCIATES" }, + { 0x0040B9, "MACQ ELECTRONIQUE SA" }, + { 0x0040BA, "ALLIANT COMPUTER SYSTEMS CORP." }, + { 0x0040BB, "GOLDSTAR CABLE CO., LTD." }, + { 0x0040BC, "ALGORITHMICS LTD." }, + { 0x0040BD, "STARLIGHT NETWORKS, INC." }, + { 0x0040BE, "BOEING DEFENSE & SPACE" }, + { 0x0040BF, "CHANNEL SYSTEMS INTERN'L INC." }, + { 0x0040C0, "VISTA CONTROLS CORPORATION" }, + { 0x0040C1, "BIZERBA-WERKE WILHEIM KRAUT" }, + { 0x0040C2, "APPLIED COMPUTING DEVICES" }, + { 0x0040C3, "FISCHER AND PORTER CO." }, + { 0x0040C4, "KINKEI SYSTEM CORPORATION" }, + { 0x0040C5, "MICOM COMMUNICATIONS INC." }, + { 0x0040C6, "FIBERNET RESEARCH, INC." }, + { 0x0040C7, "RUBY TECH CORPORATION" }, + { 0x0040C8, "MILAN TECHNOLOGY CORPORATION" }, + { 0x0040C9, "NCUBE" }, + { 0x0040CA, "FIRST INTERNAT'L COMPUTER, INC" }, + { 0x0040CB, "LANWAN TECHNOLOGIES" }, + { 0x0040CC, "SILCOM MANUF'G TECHNOLOGY INC." }, + { 0x0040CD, "TERA MICROSYSTEMS, INC." }, + { 0x0040CE, "NET-SOURCE, INC." }, + { 0x0040CF, "STRAWBERRY TREE, INC." }, + { 0x0040D0, "MITAC INTERNATIONAL CORP." }, + { 0x0040D1, "FUKUDA DENSHI CO., LTD." }, + { 0x0040D2, "PAGINE CORPORATION" }, + { 0x0040D3, "KIMPSION INTERNATIONAL CORP." }, + { 0x0040D4, "GAGE TALKER CORP." }, + { 0x0040D5, "SARTORIUS AG" }, + { 0x0040D6, "LOCAMATION B.V." }, + { 0x0040D7, "STUDIO GEN INC." }, + { 0x0040D8, "OCEAN OFFICE AUTOMATION LTD." }, + { 0x0040D9, "AMERICAN MEGATRENDS INC." }, + { 0x0040DA, "TELSPEC LTD" }, + { 0x0040DB, "ADVANCED TECHNICAL SOLUTIONS" }, + { 0x0040DC, "TRITEC ELECTRONIC GMBH" }, + { 0x0040DD, "HONG TECHNOLOGIES" }, + { 0x0040DE, "ELETTRONICA SAN GIORGIO" }, + { 0x0040DF, "DIGALOG SYSTEMS, INC." }, + { 0x0040E0, "ATOMWIDE LTD." }, + { 0x0040E1, "MARNER INTERNATIONAL, INC." }, + { 0x0040E2, "MESA RIDGE TECHNOLOGIES, INC." }, + { 0x0040E3, "QUIN SYSTEMS LTD" }, + { 0x0040E4, "E-M TECHNOLOGY, INC." }, + { 0x0040E5, "SYBUS CORPORATION" }, + { 0x0040E6, "C.A.E.N." }, + { 0x0040E7, "ARNOS INSTRUMENTS & COMPUTER" }, + { 0x0040E8, "CHARLES RIVER DATA SYSTEMS,INC" }, + { 0x0040E9, "ACCORD SYSTEMS, INC." }, + { 0x0040EA, "PLAIN TREE SYSTEMS INC" }, + { 0x0040EB, "MARTIN MARIETTA CORPORATION" }, + { 0x0040EC, "MIKASA SYSTEM ENGINEERING" }, + { 0x0040ED, "NETWORK CONTROLS INT'NATL INC." }, + { 0x0040EE, "OPTIMEM" }, + { 0x0040EF, "HYPERCOM, INC." }, + { 0x0040F0, "MICRO SYSTEMS, INC." }, + { 0x0040F1, "CHUO ELECTRONICS CO., LTD." }, + { 0x0040F2, "JANICH & KLASS COMPUTERTECHNIK" }, + { 0x0040F3, "NETCOR" }, + { 0x0040F4, "CAMEO COMMUNICATIONS, INC." }, + { 0x0040F5, "OEM ENGINES" }, + { 0x0040F6, "KATRON COMPUTERS INC." }, + { 0x0040F7, "POLAROID MEDICAL IMAGING SYS." }, + { 0x0040F8, "SYSTEMHAUS DISCOM" }, + { 0x0040F9, "COMBINET" }, + { 0x0040FA, "MICROBOARDS, INC." }, + { 0x0040FB, "CASCADE COMMUNICATIONS CORP." }, + { 0x0040FC, "IBR COMPUTER TECHNIK GMBH" }, + { 0x0040FD, "LXE" }, + { 0x0040FE, "SYMPLEX COMMUNICATIONS" }, + { 0x0040FF, "TELEBIT CORPORATION" }, + { 0x005000, "NEXO COMMUNICATIONS, INC." }, + { 0x005001, "YAMASHITA SYSTEMS CORP." }, + { 0x005002, "OMNISEC AG" }, + { 0x005003, "GRETAG MACBETH AG" }, + { 0x005004, "3COM CORPORATION" }, + { 0x005006, "TAC AB" }, + { 0x005007, "SIEMENS TELECOMMUNICATION" }, + { 0x005008, "TIVA MICROCOMPUTER CORP. (TMC)" }, + { 0x005009, "PHILIPS BROADBAND NETWORKS" }, + { 0x00500A, "IRIS TECHNOLOGIES, INC." }, + { 0x00500B, "CISCO SYSTEMS, INC." }, + { 0x00500C, "ETEK LABS, INC." }, + { 0x00500D, "SATORI ELECTORIC CO., LTD." }, + { 0x00500E, "CHROMATIS NETWORKS,INC." }, + { 0x00500F, "CISCO SYSTEMS, INC." }, + { 0x005010, "NOVANET LEARNING, INC." }, + { 0x005012, "CBL - GMBH" }, + { 0x005013, "Chaparral Technologies, Inc." }, + { 0x005014, "CISCO SYSTEMS, INC." }, + { 0x005015, "BRIGHT STAR ENGINEERING" }, + { 0x005016, "SST/WOODHEAD INDUSTRIES" }, + { 0x005017, "RSR S.R.L." }, + { 0x005018, "ADVANCED MULTIMEDIA INTERNET" }, + { 0x005019, "SPRING TIDE NETWORKS, INC." }, + { 0x00501A, "UISIQN" }, + { 0x00501B, "ABL CANADA, INC." }, + { 0x00501C, "JATOM SYSTEMS, INC." }, + { 0x00501E, "MIRANDA TECHNOLOGIES, INC." }, + { 0x00501F, "MRG SYSTEMS, LTD." }, + { 0x005020, "MEDIASTAR CO., LTD." }, + { 0x005021, "EIS INTERNATIONAL, INC." }, + { 0x005022, "ZONET TECHNOLOGY, INC." }, + { 0x005023, "PG DESIGN ELECTRONICS, INC." }, + { 0x005024, "NAVIC SYSTEMS, INC." }, + { 0x005026, "COSYSTEMS, INC." }, + { 0x005027, "GENICOM CORPORATION" }, + { 0x005028, "AVAL COMMUNICATIONS" }, + { 0x005029, "1394 PRINTER WORKING GROUP" }, + { 0x00502A, "CISCO SYSTEMS, INC." }, + { 0x00502B, "GENRAD LTD." }, + { 0x00502C, "SOYO COMPUTER, INC." }, + { 0x00502D, "ACCEL, INC." }, + { 0x00502E, "CAMBEX CORPORATION" }, + { 0x00502F, "TOLLBRIDGE TECHNOLOGIES, INC." }, + { 0x005030, "FUTURE PLUS SYSTEMS" }, + { 0x005031, "AEROFLEX LABORATORIES, INC." }, + { 0x005032, "PICAZO COMMUNICATIONS, INC." }, + { 0x005033, "MAYAN NETWORKS" }, + { 0x005036, "NETCAM, LTD." }, + { 0x005037, "KOGA ELECTRONICS CO." }, + { 0x005038, "DAIN TELECOM CO., LTD." }, + { 0x005039, "MARINER NETWORKS" }, + { 0x00503A, "DATONG ELECTRONICS LTD." }, + { 0x00503B, "MEDIAFIRE CORPORATION" }, + { 0x00503C, "TSINGHUA NOVEL ELECTRONICS" }, + { 0x00503E, "CISCO SYSTEMS, INC." }, + { 0x00503F, "ANCHOR GAMES" }, + { 0x005040, "EMWARE, INC." }, + { 0x005041, "CTX OPTO ELECTRONIC CORP." }, + { 0x005042, "SCI MANUFACTURING" }, + { 0x005043, "MARVELL SEMICONDUCTOR, INC." }, + { 0x005044, "ASACA CORPORATION" }, + { 0x005045, "RIOWORKS SOLUTIONS, INC." }, + { 0x005046, "MENICX INTERNATIONAL CO., LTD." }, + { 0x005048, "INFOLIBRIA" }, + { 0x005049, "ELLACOYA NETWORKS, INC." }, + { 0x00504A, "ELTECO A.S." }, + { 0x00504B, "BARCONET N.V." }, + { 0x00504C, "GALIL MOTION CONTROL, INC." }, + { 0x00504D, "TOKYO ELECTRON DEVICE LTD." }, + { 0x00504E, "SIERRA MONITOR CORP." }, + { 0x00504F, "OLENCOM ELECTRONICS" }, + { 0x005050, "CISCO SYSTEMS, INC." }, + { 0x005051, "IWATSU ELECTRIC CO., LTD." }, + { 0x005052, "TIARA NETWORKS, INC." }, + { 0x005053, "CISCO SYSTEMS, INC." }, + { 0x005054, "CISCO SYSTEMS, INC." }, + { 0x005055, "DOMS A/S" }, + { 0x005056, "VMWARE, INC." }, + { 0x005057, "BROADBAND ACCESS SYSTEMS" }, + { 0x005058, "VEGASTREAM LIMITED" }, + { 0x005059, "SUITE TECHNOLOGY SYSTEMS" }, + { 0x00505A, "NETWORK ALCHEMY, INC." }, + { 0x00505B, "KAWASAKI LSI U.S.A., INC." }, + { 0x00505C, "TUNDO CORPORATION" }, + { 0x00505E, "DIGITEK MICROLOGIC S.A." }, + { 0x00505F, "BRAND INNOVATORS" }, + { 0x005060, "TANDBERG TELECOM AS" }, + { 0x005062, "KOUWELL ELECTRONICS CORP. **" }, + { 0x005063, "OY COMSEL SYSTEM AB" }, + { 0x005064, "CAE ELECTRONICS" }, + { 0x005065, "DENSEI-LAMBAD Co., Ltd." }, + { 0x005066, "ATECOM GMBH ADVANCED" }, + { 0x005067, "AEROCOMM, INC." }, + { 0x005068, "ELECTRONIC INDUSTRIES" }, + { 0x005069, "PIXSTREAM INCORPORATED" }, + { 0x00506A, "EDEVA, INC." }, + { 0x00506B, "SPX-ATEG" }, + { 0x00506C, "G & L BEIJER ELECTRONICS AB" }, + { 0x00506D, "VIDEOJET SYSTEMS" }, + { 0x00506E, "CORDER ENGINEERING CORPORATION" }, + { 0x00506F, "G-CONNECT" }, + { 0x005070, "CHAINTECH COMPUTER CO., LTD." }, + { 0x005071, "AIWA CO., LTD." }, + { 0x005072, "CORVIS CORPORATION" }, + { 0x005073, "CISCO SYSTEMS, INC." }, + { 0x005074, "ADVANCED HI-TECH CORP." }, + { 0x005075, "KESTREL SOLUTIONS" }, + { 0x005076, "IBM" }, + { 0x005077, "PROLIFIC TECHNOLOGY, INC." }, + { 0x005078, "MEGATON HOUSE, LTD." }, + { 0x00507A, "XPEED, INC." }, + { 0x00507B, "MERLOT COMMUNICATIONS" }, + { 0x00507C, "VIDEOCON AG" }, + { 0x00507D, "IFP" }, + { 0x00507E, "NEWER TECHNOLOGY" }, + { 0x00507F, "DRAYTEK CORP." }, + { 0x005080, "CISCO SYSTEMS, INC." }, + { 0x005081, "MURATA MACHINERY, LTD." }, + { 0x005082, "FORESSON CORPORATION" }, + { 0x005083, "GILBARCO, INC." }, + { 0x005084, "ATL PRODUCTS" }, + { 0x005086, "TELKOM SA, LTD." }, + { 0x005087, "TERASAKI ELECTRIC CO., LTD." }, + { 0x005088, "AMANO CORPORATION" }, + { 0x005089, "SAFETY MANAGEMENT SYSTEMS" }, + { 0x00508B, "COMPAQ COMPUTER CORPORATION" }, + { 0x00508C, "RSI SYSTEMS" }, + { 0x00508D, "ABIT COMPUTER CORPORATION" }, + { 0x00508E, "OPTIMATION, INC." }, + { 0x00508F, "ASITA TECHNOLOGIES INT'L LTD." }, + { 0x005090, "DCTRI" }, + { 0x005091, "NETACCESS, INC." }, + { 0x005092, "RIGAKU INDUSTRIAL CORPORATION" }, + { 0x005093, "BOEING" }, + { 0x005094, "PACE MICRO TECHNOLOGY PLC" }, + { 0x005095, "PERACOM NETWORKS" }, + { 0x005096, "SALIX TECHNOLOGIES, INC." }, + { 0x005097, "MMC-EMBEDDED" }, + { 0x005098, "GLOBALOOP, LTD." }, + { 0x005099, "3COM EUROPE, LTD." }, + { 0x00509A, "TAG ELECTRONIC SYSTEMS" }, + { 0x00509B, "SWITCHCORE AB" }, + { 0x00509C, "BETA RESEARCH" }, + { 0x00509D, "THE INDUSTREE B.V." }, + { 0x00509E, "LES TECHNOLOGIES" }, + { 0x00509F, "HORIZON COMPUTER" }, + { 0x0050A0, "DELTA COMPUTER SYSTEMS, INC." }, + { 0x0050A1, "CARLO GAVAZZI, INC." }, + { 0x0050A2, "CISCO SYSTEMS, INC." }, + { 0x0050A3, "TRANSMEDIA COMMUNICATIONS, INC" }, + { 0x0050A4, "IO TECH, INC." }, + { 0x0050A5, "CAPITOL BUSINESS SYSTEMS, LTD." }, + { 0x0050A6, "OPTRONICS" }, + { 0x0050A7, "CISCO SYSTEMS, INC." }, + { 0x0050A8, "OPENCON SYSTEMS, INC." }, + { 0x0050A9, "MOLDAT WIRELESS TECHNOLGIES" }, + { 0x0050AA, "KONICA CORPORATION" }, + { 0x0050AB, "NALTEC, INC." }, + { 0x0050AC, "MAPLE COMPUTER CORPORATION" }, + { 0x0050AD, "COMMUNIQUE WIRELESS CORP." }, + { 0x0050AE, "IWAKI ELECTRONICS CO., LTD." }, + { 0x0050AF, "INTERGON, INC." }, + { 0x0050B0, "TECHNOLOGY ATLANTA CORPORATION" }, + { 0x0050B1, "GIDDINGS & LEWIS" }, + { 0x0050B2, "BRODEL AUTOMATION" }, + { 0x0050B3, "VOICEBOARD CORPORATION" }, + { 0x0050B4, "SATCHWELL CONTROL SYSTEMS, LTD" }, + { 0x0050B5, "FICHET-BAUCHE" }, + { 0x0050B6, "GOOD WAY IND. CO., LTD." }, + { 0x0050B7, "BOSER TECHNOLOGY CO., LTD." }, + { 0x0050B8, "INOVA COMPUTERS GMBH & CO. KG" }, + { 0x0050B9, "XITRON TECHNOLOGIES, INC." }, + { 0x0050BA, "D-LINK" }, + { 0x0050BB, "CMS TECHNOLOGIES" }, + { 0x0050BC, "HAMMER STORAGE SOLUTIONS" }, + { 0x0050BD, "CISCO SYSTEMS, INC." }, + { 0x0050BE, "FAST MULTIMEDIA AG" }, + { 0x0050BF, "MOTOTECH INC." }, + { 0x0050C0, "GATAN, INC." }, + { 0x0050C1, "GEMFLEX NETWORKS, LTD." }, + { 0x0050C2, "IEEE REGISTRATION AUTHORITY" }, + { 0x0050C4, "IMD" }, + { 0x0050C5, "ADS TECHNOLOGIES, INC." }, + { 0x0050C6, "LOOP TELECOMMUNICATION" }, + { 0x0050C8, "ADDONICS COMMUNICATIONS, INC." }, + { 0x0050C9, "MASPRO DENKOH CORP." }, + { 0x0050CA, "NET TO NET TECHNOLOGIES" }, + { 0x0050CB, "JETTER" }, + { 0x0050CC, "XYRATEX" }, + { 0x0050CD, "DIGIANSWER A/S" }, + { 0x0050CE, "LG INTERNATIONAL CORP." }, + { 0x0050CF, "VANLINK COMMUNICATION" }, + { 0x0050D0, "MINERVA SYSTEMS" }, + { 0x0050D1, "CISCO SYSTEMS, INC." }, + { 0x0050D2, "BAE Systems Canada, Inc." }, + { 0x0050D3, "DIGITAL AUDIO" }, + { 0x0050D4, "JOOHONG INFORMATION &" }, + { 0x0050D5, "AD SYSTEMS CORP." }, + { 0x0050D6, "ATLAS COPCO TOOLS AB" }, + { 0x0050D7, "TELSTRAT" }, + { 0x0050D8, "UNICORN COMPUTER CORP." }, + { 0x0050D9, "ENGETRON-ENGENHARIA ELETRONICA" }, + { 0x0050DA, "3COM CORPORATION" }, + { 0x0050DB, "CONTEMPORARY CONTROL" }, + { 0x0050DC, "TAS TELEFONBAU A. SCHWABE" }, + { 0x0050DD, "SERRA SOLDADURA, S.A." }, + { 0x0050DE, "SIGNUM SYSTEMS CORP." }, + { 0x0050DF, "AIRFIBER, INC." }, + { 0x0050E1, "NS TECH ELECTRONICS SDN BHD" }, + { 0x0050E2, "CISCO SYSTEMS, INC." }, + { 0x0050E3, "TELEGATE" }, + { 0x0050E4, "APPLE COMPUTER, INC." }, + { 0x0050E6, "HAKUSAN CORPORATION" }, + { 0x0050E7, "PARADISE INNOVATIONS (ASIA)" }, + { 0x0050E8, "NOMADIX INC." }, + { 0x0050EA, "XEL COMMUNICTIONS, INC." }, + { 0x0050EB, "ALPHA-TOP CORPORATION" }, + { 0x0050EC, "OLICOM A/S" }, + { 0x0050ED, "ANDA NETWORKS" }, + { 0x0050EE, "TEK DIGITEL CORPORATION" }, + { 0x0050EF, "SPE SYSTEMHAUS GMBH" }, + { 0x0050F0, "CISCO SYSTEMS, INC." }, + { 0x0050F1, "LIBIT SIGNAL PROCESSING, LTD." }, + { 0x0050F2, "MICROSOFT CORP." }, + { 0x0050F3, "GLOBAL NET INFORMATION CO.,LTD" }, + { 0x0050F4, "SIGMATEK GMBH & CO. KG" }, + { 0x0050F6, "PAN-INTERNATIONAL" }, + { 0x0050F7, "VENTURE MANUFACTURING" }, + { 0x0050F8, "ENTREGA TECHNOLOGIES, INC." }, + { 0x0050FA, "OXTEL, LTD." }, + { 0x0050FB, "VSK ELECTRONICS" }, + { 0x0050FC, "EDIMAX TECHNOLOGY CO., LTD." }, + { 0x0050FD, "ISIONCOMM CO., LTD." }, + { 0x0050FE, "PCTVNET ASA" }, + { 0x0050FF, "HAKKO ELECTRONICS CO., LTD." }, + { 0x006000, "XYCOM INC." }, + { 0x006001, "INNOSYS, INC." }, + { 0x006002, "SCREEN SUBTITLING SYSTEMS, LTD" }, + { 0x006003, "TERAOKA WEIGH SYSTEM PTE, LTD." }, + { 0x006004, "COMPUTADORES MODULARES SA" }, + { 0x006005, "FEEDBACK DATA LTD." }, + { 0x006006, "SOTEC CO., LTD" }, + { 0x006007, "ACRES GAMING, INC." }, + { 0x006008, "3COM CORPORATION" }, + { 0x006009, "CISCO SYSTEMS, INC." }, + { 0x00600A, "SORD COMPUTER CORPORATION" }, + { 0x00600B, "LOGWARE GMBH" }, + { 0x00600C, "APPLIED DATA SYSTEMS, INC." }, + { 0x00600D, "MICRODESIGN GMBH" }, + { 0x00600E, "WAVENET INTERNATIONAL, INC." }, + { 0x00600F, "WESTELL, INC." }, + { 0x006010, "NETWORK MACHINES, INC." }, + { 0x006011, "CRYSTAL SEMICONDUCTOR CORP." }, + { 0x006012, "POWER COMPUTING CORPORATION" }, + { 0x006013, "NETSTAL MASCHINEN AG" }, + { 0x006014, "EDEC CO., LTD." }, + { 0x006015, "NET2NET CORPORATION" }, + { 0x006016, "CLARIION" }, + { 0x006017, "TOKIMEC INC." }, + { 0x006018, "STELLAR ONE CORPORATION" }, + { 0x006019, "BOEHRINGER MANNHEIM CORP." }, + { 0x00601A, "KEITHLEY INSTRUMENTS" }, + { 0x00601B, "MESA ELECTRONICS" }, + { 0x00601C, "TELXON CORPORATION" }, + { 0x00601D, "LUCENT TECHNOLOGIES" }, + { 0x00601E, "SOFTLAB, INC." }, + { 0x00601F, "STALLION TECHNOLOGIES" }, + { 0x006020, "PIVOTAL NETWORKING, INC." }, + { 0x006021, "DSC CORPORATION" }, + { 0x006022, "VICOM SYSTEMS, INC." }, + { 0x006023, "PERICOM SEMICONDUCTOR CORP." }, + { 0x006024, "GRADIENT TECHNOLOGIES, INC." }, + { 0x006025, "ACTIVE IMAGING PLC" }, + { 0x006026, "VIKING COMPONENTS, INC." }, + { 0x006027, "Superior Modular Products" }, + { 0x006028, "MACROVISION CORPORATION" }, + { 0x006029, "CARY PERIPHERALS INC." }, + { 0x00602A, "SYMICRON COMPUTER" }, + { 0x00602B, "PEAK AUDIO" }, + { 0x00602C, "LINX DATA TERMINALS, INC." }, + { 0x00602D, "ALERTON TECHNOLOGIES, INC." }, + { 0x00602E, "CYCLADES CORPORATION" }, + { 0x00602F, "CISCO SYSTEMS, INC." }, + { 0x006030, "VILLAGE TRONIC" }, + { 0x006031, "HRK SYSTEMS" }, + { 0x006032, "I-CUBE, INC." }, + { 0x006033, "ACUITY IMAGING, INC." }, + { 0x006034, "ROBERT BOSCH GMBH" }, + { 0x006035, "DALLAS SEMICONDUCTOR, INC." }, + { 0x006036, "AUSTRIAN RESEARCH CENTER" }, + { 0x006037, "PHILIPS SEMICONDUCTORS" }, + { 0x006038, "Nortel Networks" }, + { 0x006039, "SANCOM TECHNOLOGY, INC." }, + { 0x00603A, "QUICK CONTROLS LTD." }, + { 0x00603B, "AMTEC SPA" }, + { 0x00603C, "HAGIWARA SYS-COM CO., LTD." }, + { 0x00603D, "3CX" }, + { 0x00603E, "CISCO SYSTEMS, INC." }, + { 0x00603F, "PATAPSCO DESIGNS" }, + { 0x006040, "NETRO CORP." }, + { 0x006041, "Yokogawa Electric Corporation" }, + { 0x006042, "TKS (USA), INC." }, + { 0x006043, "COMSOFT SYSTEMS, INC." }, + { 0x006044, "LITTON/POLY-SCIENTIFIC" }, + { 0x006045, "PATHLIGHT TECHNOLOGIES" }, + { 0x006046, "VMETRO, INC." }, + { 0x006047, "CISCO SYSTEMS, INC." }, + { 0x006048, "EMC CORPORATION" }, + { 0x006049, "VINA TECHNOLOGIES" }, + { 0x00604A, "SAIC IDEAS GROUP" }, + { 0x00604B, "BIODATA GMBH" }, + { 0x00604C, "SAT" }, + { 0x00604D, "MMC NETWORKS, INC." }, + { 0x00604E, "CYCLE COMPUTER CORPORATION, INC." }, + { 0x00604F, "SUZUKI MFG. CO., LTD." }, + { 0x006050, "INTERNIX INC." }, + { 0x006051, "QUALITY SEMICONDUCTOR" }, + { 0x006052, "PERIPHERALS ENTERPRISE CO., L." }, + { 0x006053, "TOYODA MACHINE WORKS, LTD." }, + { 0x006054, "CONTROLWARE GMBH" }, + { 0x006055, "CORNELL UNIVERSITY" }, + { 0x006056, "NETWORK TOOLS, INC." }, + { 0x006057, "MURATA MANUFACTURING CO., LTD." }, + { 0x006058, "COPPER MOUNTAIN" }, + { 0x006059, "TECHNICAL COMMUNICATIONS CORP." }, + { 0x00605A, "CELCORE, INC." }, + { 0x00605B, "INTRASERVER TECHNOLOGY INC." }, + { 0x00605C, "CISCO SYSTEMS, INC." }, + { 0x00605D, "SCANIVALVE CORP." }, + { 0x00605E, "LIBERTY TECHNOLOGY NETWORKING" }, + { 0x00605F, "NIPPON UNISOFT CORPORATION" }, + { 0x006060, "DAWNING TECHNOLOGIES, INC." }, + { 0x006061, "WHISTLE COMMUNICATIONS CORP." }, + { 0x006062, "TELESYNC, INC." }, + { 0x006063, "PSION DACOM PLC." }, + { 0x006064, "NETCOMM LIMITED" }, + { 0x006065, "BERNECKER & RAINER" }, + { 0x006066, "LACROIX TECHNOLGIE" }, + { 0x006067, "ACER NETXUS INC." }, + { 0x006068, "EICON TECHNOLOGY CORPORATION" }, + { 0x006069, "BROCADE COMMUNICATIONS SYSTEMS" }, + { 0x00606A, "MITSUBISHI WIRELESS COMM. INC." }, + { 0x00606B, "AICHI ELECTRONICS CO.,LTD." }, + { 0x00606C, "ARESCOM" }, + { 0x00606D, "DIGITAL EQUIPMENT CORP." }, + { 0x00606E, "DAVICOM SEMICONDUCTOR, INC." }, + { 0x00606F, "CLARION CORPORATION OF AMERICA" }, + { 0x006070, "CISCO SYSTEMS, INC." }, + { 0x006071, "MIDAS LAB, INC." }, + { 0x006072, "VXL INSTRUMENTS, LIMITED" }, + { 0x006073, "REDCREEK COMMUNICATIONS, INC." }, + { 0x006074, "QSC AUDIO PRODUCTS" }, + { 0x006075, "PENTEK, INC." }, + { 0x006076, "SCHLUMBERGER TECHNOLOGIES" }, + { 0x006077, "PRISA NETWORKS" }, + { 0x006078, "POWER MEASUREMENT LTD." }, + { 0x006079, "WAVEPHORE NETWORKS, INC." }, + { 0x00607A, "DVS GMBH" }, + { 0x00607B, "FORE SYSTEMS, INC." }, + { 0x00607C, "WAVEACCESS, LTD." }, + { 0x00607D, "SENTIENT NETWORKS INC." }, + { 0x00607E, "GIGALABS, INC." }, + { 0x00607F, "AURORA TECHNOLOGIES, INC." }, + { 0x006080, "MICROTRONIX DATACOM LTD." }, + { 0x006081, "TV/COM INTERNATIONAL" }, + { 0x006082, "NOVALINK TECHNOLOGIES, INC." }, + { 0x006083, "CISCO SYSTEMS, INC." }, + { 0x006084, "DIGITAL VIDEO" }, + { 0x006085, "STORAGE CONCEPTS" }, + { 0x006086, "LOGIC REPLACEMENT TECH. LTD." }, + { 0x006087, "KANSAI ELECTRIC CO., LTD." }, + { 0x006088, "WHITE MOUNTAIN DSP, INC." }, + { 0x006089, "XATA" }, + { 0x00608A, "CITADEL COMPUTER" }, + { 0x00608B, "CONFERTECH INTERNATIONAL" }, + { 0x00608C, "3COM CORPORATION" }, + { 0x00608D, "UNIPULSE CORP." }, + { 0x00608E, "HE ELECTRONICS, TECHNOLOGIE &" }, + { 0x00608F, "TEKRAM TECHNOLOGY CO., LTD." }, + { 0x006090, "ABLE COMMUNICATIONS, INC." }, + { 0x006091, "FIRST PACIFIC NETWORKS, INC." }, + { 0x006092, "MICRO/SYS, INC." }, + { 0x006093, "VARIAN" }, + { 0x006094, "IBM CORP." }, + { 0x006095, "ACCU-TIME SYSTEMS, INC." }, + { 0x006096, "T.S. MICROTECH INC." }, + { 0x006097, "3COM CORPORATION" }, + { 0x006098, "HT COMMUNICATIONS" }, + { 0x006099, "LAN MEDIA CORPORATION" }, + { 0x00609A, "NJK TECHNO CO." }, + { 0x00609B, "ASTRO-MED, INC." }, + { 0x00609C, "PERKIN-ELMER CORPORATION" }, + { 0x00609D, "PMI FOOD EQUIPMENT GROUP" }, + { 0x00609E, "X3 - INFORMATION TECHNOLOGY" }, + { 0x00609F, "PHAST CORPORATION" }, + { 0x0060A0, "SWITCHED NETWORK" }, + { 0x0060A1, "VPNET" }, + { 0x0060A2, "NIHON UNISYS LIMITED CO." }, + { 0x0060A3, "CONTINUUM TECHNOLOGY CORP." }, + { 0x0060A4, "GRINAKER SYSTEM TECHNOLOGIES" }, + { 0x0060A5, "PERFORMANCE TELECOM CORP." }, + { 0x0060A6, "PARTICLE MEASURING SYSTEMS" }, + { 0x0060A7, "MICROSENS GMBH & CO. KG" }, + { 0x0060A8, "TIDOMAT AB" }, + { 0x0060A9, "GESYTEC MBH" }, + { 0x0060AA, "INTELLIGENT DEVICES INC. (IDI)" }, + { 0x0060AB, "LARSCOM INCORPORATED" }, + { 0x0060AC, "RESILIENCE CORPORATION" }, + { 0x0060AD, "MEGACHIPS CORPORATION" }, + { 0x0060AE, "TRIO INFORMATION SYSTEMS AB" }, + { 0x0060AF, "PACIFIC MICRO DATA, INC." }, + { 0x0060B0, "HEWLETT-PACKARD CO." }, + { 0x0060B1, "INPUT/OUTPUT, INC." }, + { 0x0060B2, "PROCESS CONTROL CORP." }, + { 0x0060B3, "Z-COM, INC." }, + { 0x0060B4, "GLENAYRE R&D INC." }, + { 0x0060B5, "KEBA GMBH" }, + { 0x0060B6, "LAND COMPUTER CO., LTD." }, + { 0x0060B7, "CHANNELMATIC, INC." }, + { 0x0060B8, "CORELIS INC." }, + { 0x0060B9, "NITSUKO CORPORATION" }, + { 0x0060BA, "SAHARA NETWORKS, INC." }, + { 0x0060BB, "CABLETRON - NETLINK, INC." }, + { 0x0060BC, "KEUNYOUNG ELECTRONICS &" }, + { 0x0060BD, "HUBBELL-PULSECOM" }, + { 0x0060BE, "WEBTRONICS" }, + { 0x0060BF, "MACRAIGOR SYSTEMS, INC." }, + { 0x0060C0, "NERA AS" }, + { 0x0060C1, "WAVESPAN CORPORATION" }, + { 0x0060C2, "MPL AG" }, + { 0x0060C3, "NETVISION CORPORATION" }, + { 0x0060C4, "SOLITON SYSTEMS K.K." }, + { 0x0060C5, "ANCOT CORP." }, + { 0x0060C6, "DCS AG" }, + { 0x0060C7, "AMATI COMMUNICATIONS CORP." }, + { 0x0060C8, "KUKA WELDING SYSTEMS & ROBOTS" }, + { 0x0060C9, "CONTROLNET, INC." }, + { 0x0060CA, "HARMONIC SYSTEMS INCORPORATED" }, + { 0x0060CB, "HITACHI ZOSEN CORPORATION" }, + { 0x0060CC, "EMTRAK, INCORPORATED" }, + { 0x0060CD, "VIDEOSERVER, INC." }, + { 0x0060CE, "ACCLAIM COMMUNICATIONS" }, + { 0x0060CF, "ALTEON NETWORKS, INC." }, + { 0x0060D0, "SNMP RESEARCH INCORPORATED" }, + { 0x0060D1, "CASCADE COMMUNICATIONS" }, + { 0x0060D2, "LUCENT TECHNOLOGIES TAIWAN" }, + { 0x0060D3, "AT&T" }, + { 0x0060D4, "ELDAT COMMUNICATION LTD." }, + { 0x0060D5, "MIYACHI TECHNOS CORP." }, + { 0x0060D6, "NOVATEL WIRELESS TECHNOLOGIES" }, + { 0x0060D7, "ECOLE POLYTECHNIQUE FEDERALE" }, + { 0x0060D8, "ELMIC SYSTEMS, INC." }, + { 0x0060D9, "TRANSYS NETWORKS INC." }, + { 0x0060DA, "JBM ELECTRONICS CO." }, + { 0x0060DB, "NTP ELEKTRONIK A/S" }, + { 0x0060DC, "TOYO COMMUNICATION EQUIPMENT" }, + { 0x0060DD, "MYRICOM, INC." }, + { 0x0060DE, "KAYSER-THREDE GMBH" }, + { 0x0060DF, "INRANGE TECHNOLOGIES CORP." }, + { 0x0060E0, "AXIOM TECHNOLOGY CO., LTD." }, + { 0x0060E1, "ORCKIT COMMUNICATIONS LTD." }, + { 0x0060E2, "QUEST ENGINEERING & DEV." }, + { 0x0060E3, "ARBIN INSTRUMENTS" }, + { 0x0060E4, "COMPUSERVE, INC." }, + { 0x0060E5, "FUJI AUTOMATION CO., LTD." }, + { 0x0060E6, "SHOMITI SYSTEMS INCORPORATED" }, + { 0x0060E7, "RANDATA" }, + { 0x0060E8, "HITACHI COMPUTER PRODUCTS" }, + { 0x0060E9, "ATOP TECHNOLOGIES, INC." }, + { 0x0060EA, "STREAMLOGIC" }, + { 0x0060EB, "FOURTHTRACK SYSTEMS" }, + { 0x0060EC, "HERMARY OPTO ELECTRONICS INC." }, + { 0x0060ED, "RICARDO TEST AUTOMATION LTD." }, + { 0x0060EE, "APOLLO" }, + { 0x0060EF, "FLYTECH TECHNOLOGY CO., LTD." }, + { 0x0060F0, "JOHNSON & JOHNSON MEDICAL, INC" }, + { 0x0060F1, "EXP COMPUTER, INC." }, + { 0x0060F2, "LASERGRAPHICS, INC." }, + { 0x0060F3, "NETCOM SYSTEMS, INC." }, + { 0x0060F4, "ADVANCED COMPUTER SOLUTIONS," }, + { 0x0060F5, "ICON WEST, INC." }, + { 0x0060F6, "NEXTEST COMMUNICATION" }, + { 0x0060F7, "DATAFUSION SYSTEMS" }, + { 0x0060F8, "LORAN INTERNATIONAL TECHN. INC" }, + { 0x0060F9, "DIAMOND LANE COMMUNICATIONS" }, + { 0x0060FA, "EDUCATIONAL TECHNOLOGY" }, + { 0x0060FB, "PACKETEER, INC." }, + { 0x0060FC, "CONSERVATION THROUGH" }, + { 0x0060FD, "NETICS, INC." }, + { 0x0060FE, "LYNX SYSTEM DEVELOPERS, INC." }, + { 0x0060FF, "QUVIS, INC." }, + { 0x0070B0, "M/A-COM INC. COMPANIES" }, + { 0x0070B3, "DATA RECALL LTD." }, + { 0x008000, "MULTITECH SYSTEMS, INC." }, + { 0x008001, "PERIPHONICS CORPORATION" }, + { 0x008002, "SATELCOM (UK) LTD" }, + { 0x008003, "HYTEC ELECTRONICS LTD." }, + { 0x008004, "ANTLOW COMMUNICATIONS, LTD." }, + { 0x008005, "CACTUS COMPUTER INC." }, + { 0x008006, "COMPUADD CORPORATION" }, + { 0x008007, "DLOG NC-SYSTEME" }, + { 0x008008, "DYNATECH COMPUTER SYSTEMS" }, + { 0x008009, "JUPITER SYSTEMS, INC." }, + { 0x00800A, "JAPAN COMPUTER CORP." }, + { 0x00800B, "CSK CORPORATION" }, + { 0x00800C, "VIDECOM LIMITED" }, + { 0x00800D, "VOSSWINKEL F.U." }, + { 0x00800E, "ATLANTIX CORPORATION" }, + { 0x00800F, "STANDARD MICROSYSTEMS" }, + { 0x008010, "COMMODORE INTERNATIONAL" }, + { 0x008011, "DIGITAL SYSTEMS INT'L. INC." }, + { 0x008012, "INTEGRATED MEASUREMENT SYSTEMS" }, + { 0x008013, "THOMAS-CONRAD CORPORATION" }, + { 0x008014, "ESPRIT SYSTEMS" }, + { 0x008015, "SEIKO SYSTEMS, INC." }, + { 0x008016, "WANDEL AND GOLTERMANN" }, + { 0x008017, "PFU LIMITED" }, + { 0x008018, "KOBE STEEL, LTD." }, + { 0x008019, "DAYNA COMMUNICATIONS, INC." }, + { 0x00801A, "BELL ATLANTIC" }, + { 0x00801B, "KODIAK TECHNOLOGY" }, + { 0x00801C, "NEWPORT SYSTEMS SOLUTIONS" }, + { 0x00801D, "INTEGRATED INFERENCE MACHINES" }, + { 0x00801E, "XINETRON, INC." }, + { 0x00801F, "KRUPP ATLAS ELECTRONIK GMBH" }, + { 0x008020, "NETWORK PRODUCTS" }, + { 0x008021, "NEWBRIDGE RESEARCH CORP." }, + { 0x008022, "SCAN-OPTICS" }, + { 0x008023, "INTEGRATED BUSINESS NETWORKS" }, + { 0x008024, "KALPANA, INC." }, + { 0x008025, "STOLLMANN GMBH" }, + { 0x008026, "NETWORK PRODUCTS CORPORATION" }, + { 0x008027, "ADAPTIVE SYSTEMS, INC." }, + { 0x008028, "TRADPOST (HK) LTD" }, + { 0x008029, "EAGLE TECHNOLOGY, INC." }, + { 0x00802A, "TEST SYSTEMS & SIMULATIONS INC" }, + { 0x00802B, "INTEGRATED MARKETING CO" }, + { 0x00802C, "THE SAGE GROUP PLC" }, + { 0x00802D, "XYLOGICS INC" }, + { 0x00802E, "CASTLE ROCK COMPUTING" }, + { 0x00802F, "NATIONAL INSTRUMENTS CORP." }, + { 0x008030, "NEXUS ELECTRONICS" }, + { 0x008031, "BASYS, CORP." }, + { 0x008032, "ACCESS CO., LTD." }, + { 0x008033, "FORMATION, INC." }, + { 0x008034, "SMT GOUPIL" }, + { 0x008035, "TECHNOLOGY WORKS, INC." }, + { 0x008036, "REFLEX MANUFACTURING SYSTEMS" }, + { 0x008037, "Ericsson Group" }, + { 0x008038, "DATA RESEARCH & APPLICATIONS" }, + { 0x008039, "ALCATEL STC AUSTRALIA" }, + { 0x00803A, "VARITYPER, INC." }, + { 0x00803B, "APT COMMUNICATIONS, INC." }, + { 0x00803C, "TVS ELECTRONICS LTD" }, + { 0x00803D, "SURIGIKEN CO., LTD." }, + { 0x00803E, "SYNERNETICS" }, + { 0x00803F, "TATUNG COMPANY" }, + { 0x008040, "JOHN FLUKE MANUFACTURING CO." }, + { 0x008041, "VEB KOMBINAT ROBOTRON" }, + { 0x008042, "FORCE COMPUTERS" }, + { 0x008043, "NETWORLD, INC." }, + { 0x008044, "SYSTECH COMPUTER CORP." }, + { 0x008045, "MATSUSHITA ELECTRIC IND. CO" }, + { 0x008046, "UNIVERSITY OF TORONTO" }, + { 0x008047, "IN-NET CORP." }, + { 0x008048, "COMPEX INCORPORATED" }, + { 0x008049, "NISSIN ELECTRIC CO., LTD." }, + { 0x00804A, "PRO-LOG" }, + { 0x00804B, "EAGLE TECHNOLOGIES PTY.LTD." }, + { 0x00804C, "CONTEC CO., LTD." }, + { 0x00804D, "CYCLONE MICROSYSTEMS, INC." }, + { 0x00804E, "APEX COMPUTER COMPANY" }, + { 0x00804F, "DAIKIN INDUSTRIES, LTD." }, + { 0x008050, "ZIATECH CORPORATION" }, + { 0x008051, "FIBERMUX" }, + { 0x008052, "TECHNICALLY ELITE CONCEPTS" }, + { 0x008053, "INTELLICOM, INC." }, + { 0x008054, "FRONTIER TECHNOLOGIES CORP." }, + { 0x008055, "FERMILAB" }, + { 0x008056, "SPHINX ELEKTRONIK GMBH" }, + { 0x008057, "ADSOFT, LTD." }, + { 0x008058, "PRINTER SYSTEMS CORPORATION" }, + { 0x008059, "STANLEY ELECTRIC CO., LTD" }, + { 0x00805A, "TULIP COMPUTERS INTERNAT'L B.V" }, + { 0x00805B, "CONDOR SYSTEMS, INC." }, + { 0x00805C, "AGILIS CORPORATION" }, + { 0x00805D, "CANSTAR" }, + { 0x00805E, "LSI LOGIC CORPORATION" }, + { 0x00805F, "COMPAQ COMPUTER CORPORATION" }, + { 0x008060, "NETWORK INTERFACE CORPORATION" }, + { 0x008061, "LITTON SYSTEMS, INC." }, + { 0x008062, "INTERFACE CO." }, + { 0x008063, "RICHARD HIRSCHMANN GMBH & CO." }, + { 0x008064, "WYSE TECHNOLOGY" }, + { 0x008065, "CYBERGRAPHIC SYSTEMS PTY LTD." }, + { 0x008066, "ARCOM CONTROL SYSTEMS, LTD." }, + { 0x008067, "SQUARE D COMPANY" }, + { 0x008068, "YAMATECH SCIENTIFIC LTD." }, + { 0x008069, "COMPUTONE SYSTEMS" }, + { 0x00806A, "ERI (EMPAC RESEARCH INC.)" }, + { 0x00806B, "SCHMID TELECOMMUNICATION" }, + { 0x00806C, "CEGELEC PROJECTS LTD" }, + { 0x00806D, "CENTURY SYSTEMS CORP." }, + { 0x00806E, "NIPPON STEEL CORPORATION" }, + { 0x00806F, "ONELAN LTD." }, + { 0x008070, "COMPUTADORAS MICRON" }, + { 0x008071, "SAI TECHNOLOGY" }, + { 0x008072, "MICROPLEX SYSTEMS LTD." }, + { 0x008073, "DWB ASSOCIATES" }, + { 0x008074, "FISHER CONTROLS" }, + { 0x008075, "PARSYTEC GMBH" }, + { 0x008076, "MCNC" }, + { 0x008077, "BROTHER INDUSTRIES, LTD." }, + { 0x008078, "PRACTICAL PERIPHERALS, INC." }, + { 0x008079, "MICROBUS DESIGNS LTD." }, + { 0x00807A, "AITECH SYSTEMS LTD." }, + { 0x00807B, "ARTEL COMMUNICATIONS CORP." }, + { 0x00807C, "FIBERCOM, INC." }, + { 0x00807D, "EQUINOX SYSTEMS INC." }, + { 0x00807E, "SOUTHERN PACIFIC LTD." }, + { 0x00807F, "DY-4 INCORPORATED" }, + { 0x008080, "DATAMEDIA CORPORATION" }, + { 0x008081, "KENDALL SQUARE RESEARCH CORP." }, + { 0x008082, "PEP MODULAR COMPUTERS GMBH" }, + { 0x008083, "AMDAHL" }, + { 0x008084, "THE CLOUD INC." }, + { 0x008085, "H-THREE SYSTEMS CORPORATION" }, + { 0x008086, "COMPUTER GENERATION INC." }, + { 0x008087, "OKI ELECTRIC INDUSTRY CO., LTD" }, + { 0x008088, "VICTOR COMPANY OF JAPAN, LTD." }, + { 0x008089, "TECNETICS (PTY) LTD." }, + { 0x00808A, "SUMMIT MICROSYSTEMS CORP." }, + { 0x00808B, "DACOLL LIMITED" }, + { 0x00808C, "NetScout Systems, Inc." }, + { 0x00808D, "WESTCOAST TECHNOLOGY B.V." }, + { 0x00808E, "RADSTONE TECHNOLOGY" }, + { 0x00808F, "C. ITOH ELECTRONICS, INC." }, + { 0x008090, "MICROTEK INTERNATIONAL, INC." }, + { 0x008091, "TOKYO ELECTRIC CO.,LTD" }, + { 0x008092, "JAPAN COMPUTER INDUSTRY, INC." }, + { 0x008093, "XYRON CORPORATION" }, + { 0x008094, "ALFA LAVAL AUTOMATION AB" }, + { 0x008095, "BASIC MERTON HANDELSGES.M.B.H." }, + { 0x008096, "HUMAN DESIGNED SYSTEMS, INC." }, + { 0x008097, "CENTRALP AUTOMATISMES" }, + { 0x008098, "TDK CORPORATION" }, + { 0x008099, "KLOCKNER MOELLER IPC" }, + { 0x00809A, "NOVUS NETWORKS LTD" }, + { 0x00809B, "JUSTSYSTEM CORPORATION" }, + { 0x00809C, "LUXCOM, INC." }, + { 0x00809D, "Commscraft Ltd." }, + { 0x00809E, "DATUS GMBH" }, + { 0x00809F, "ALCATEL BUSINESS SYSTEMS" }, + { 0x0080A0, "EDISA HEWLETT PACKARD S/A" }, + { 0x0080A1, "MICROTEST, INC." }, + { 0x0080A2, "CREATIVE ELECTRONIC SYSTEMS" }, + { 0x0080A3, "LANTRONIX" }, + { 0x0080A4, "LIBERTY ELECTRONICS" }, + { 0x0080A5, "SPEED INTERNATIONAL" }, + { 0x0080A6, "REPUBLIC TECHNOLOGY, INC." }, + { 0x0080A7, "MEASUREX CORP." }, + { 0x0080A8, "VITACOM CORPORATION" }, + { 0x0080A9, "CLEARPOINT RESEARCH" }, + { 0x0080AA, "MAXPEED" }, + { 0x0080AB, "DUKANE NETWORK INTEGRATION" }, + { 0x0080AC, "IMLOGIX, DIVISION OF GENESYS" }, + { 0x0080AD, "CNET TECHNOLOGY, INC." }, + { 0x0080AE, "HUGHES NETWORK SYSTEMS" }, + { 0x0080AF, "ALLUMER CO., LTD." }, + { 0x0080B0, "ADVANCED INFORMATION" }, + { 0x0080B1, "SOFTCOM A/S" }, + { 0x0080B2, "NETWORK EQUIPMENT TECHNOLOGIES" }, + { 0x0080B3, "AVAL DATA CORPORATION" }, + { 0x0080B4, "SOPHIA SYSTEMS" }, + { 0x0080B5, "UNITED NETWORKS INC." }, + { 0x0080B6, "THEMIS COMPUTER" }, + { 0x0080B7, "STELLAR COMPUTER" }, + { 0x0080B8, "BUG, INCORPORATED" }, + { 0x0080B9, "ARCHE TECHNOLIGIES INC." }, + { 0x0080BA, "SPECIALIX (ASIA) PTE, LTD" }, + { 0x0080BB, "HUGHES LAN SYSTEMS" }, + { 0x0080BC, "HITACHI ENGINEERING CO., LTD" }, + { 0x0080BD, "THE FURUKAWA ELECTRIC CO., LTD" }, + { 0x0080BE, "ARIES RESEARCH" }, + { 0x0080BF, "TAKAOKA ELECTRIC MFG. CO. LTD." }, + { 0x0080C0, "PENRIL DATACOMM" }, + { 0x0080C1, "LANEX CORPORATION" }, + { 0x0080C2, "IEEE 802 COMMITTEE" }, + { 0x0080C3, "BICC INFORMATION SYSTEMS & SVC" }, + { 0x0080C4, "DOCUMENT TECHNOLOGIES, INC." }, + { 0x0080C5, "NOVELLCO DE MEXICO" }, + { 0x0080C6, "NATIONAL DATACOMM CORPORATION" }, + { 0x0080C7, "XIRCOM" }, + { 0x0080C8, "D-LINK SYSTEMS, INC." }, + { 0x0080C9, "ALBERTA MICROELECTRONIC CENTRE" }, + { 0x0080CA, "NETCOM RESEARCH INCORPORATED" }, + { 0x0080CB, "FALCO DATA PRODUCTS" }, + { 0x0080CC, "MICROWAVE BYPASS SYSTEMS" }, + { 0x0080CD, "MICRONICS COMPUTER, INC." }, + { 0x0080CE, "BROADCAST TELEVISION SYSTEMS" }, + { 0x0080CF, "EMBEDDED PERFORMANCE INC." }, + { 0x0080D0, "COMPUTER PERIPHERALS, INC." }, + { 0x0080D1, "KIMTRON CORPORATION" }, + { 0x0080D2, "SHINNIHONDENKO CO., LTD." }, + { 0x0080D3, "SHIVA CORP." }, + { 0x0080D4, "CHASE RESEARCH LTD." }, + { 0x0080D5, "CADRE TECHNOLOGIES" }, + { 0x0080D6, "NUVOTECH, INC." }, + { 0x0080D7, "FANTUM ENGINEERING, INC." }, + { 0x0080D8, "NETWORK PERIPHERALS INC." }, + { 0x0080D9, "EMK ELEKTRONIK" }, + { 0x0080DA, "BRUEL & KJAER" }, + { 0x0080DB, "GRAPHON CORPORATION" }, + { 0x0080DC, "PICKER INTERNATIONAL" }, + { 0x0080DD, "GMX INC/GIMIX" }, + { 0x0080DE, "GIPSI S.A." }, + { 0x0080DF, "ADC CODENOLL TECHNOLOGY CORP." }, + { 0x0080E0, "XTP SYSTEMS, INC." }, + { 0x0080E1, "STMICROELECTRONICS" }, + { 0x0080E2, "T.D.I. CO., LTD." }, + { 0x0080E3, "CORAL NETWORK CORPORATION" }, + { 0x0080E4, "NORTHWEST DIGITAL SYSTEMS, INC" }, + { 0x0080E5, "MYLEX CORPORATION" }, + { 0x0080E6, "PEER NETWORKS, INC." }, + { 0x0080E7, "LYNWOOD SCIENTIFIC DEV. LTD." }, + { 0x0080E8, "CUMULUS CORPORATIION" }, + { 0x0080E9, "MADGE NETWORKS" }, + { 0x0080EA, "ADVA Optical Networking Ltd." }, + { 0x0080EB, "COMPCONTROL B.V." }, + { 0x0080EC, "SUPERCOMPUTING SOLUTIONS, INC." }, + { 0x0080ED, "IQ TECHNOLOGIES, INC." }, + { 0x0080EE, "THOMSON CSF" }, + { 0x0080EF, "RATIONAL" }, + { 0x0080F0, "KYUSHU MATSUSHITA ELECTRIC CO." }, + { 0x0080F1, "OPUS SYSTEMS" }, + { 0x0080F2, "RAYCOM SYSTEMS INC" }, + { 0x0080F3, "SUN ELECTRONICS CORP." }, + { 0x0080F4, "TELEMECANIQUE ELECTRIQUE" }, + { 0x0080F5, "QUANTEL LTD" }, + { 0x0080F6, "SYNERGY MICROSYSTEMS" }, + { 0x0080F7, "ZENITH ELECTRONICS" }, + { 0x0080F8, "MIZAR, INC." }, + { 0x0080F9, "HEURIKON CORPORATION" }, + { 0x0080FA, "RWT GMBH" }, + { 0x0080FB, "BVM LIMITED" }, + { 0x0080FC, "AVATAR CORPORATION" }, + { 0x0080FD, "EXSCEED CORPRATION" }, + { 0x0080FE, "AZURE TECHNOLOGIES, INC." }, + { 0x0080FF, "SOC. DE TELEINFORMATIQUE RTC" }, + { 0x009000, "DIAMOND MULTIMEDIA" }, + { 0x009001, "NISHIMU ELCTRONICS INDUSTRIES" }, + { 0x009002, "ALLGON AB" }, + { 0x009003, "APLIO" }, + { 0x009004, "3COM EUROPE LTD." }, + { 0x009005, "PROTECH SYSTEMS CO., LTD." }, + { 0x009006, "HAMAMATSU PHOTONICS K.K." }, + { 0x009007, "DOMEX TECHNOLOGY CORP." }, + { 0x009008, "HAN A SYSTEMS, INC." }, + { 0x009009, "i Controls, Inc." }, + { 0x00900A, "PROTON ELECTRONIC INDUSTRIAL" }, + { 0x00900B, "LANNER ELECTRONICS, INC." }, + { 0x00900C, "CISCO SYSTEMS, INC." }, + { 0x00900D, "OVERLAND DATA INC." }, + { 0x00900E, "HANDLINK TECHNOLOGIES, INC." }, + { 0x00900F, "KAWASAKI HEAVY INDUSTRIES, LTD" }, + { 0x009010, "SIMULATION LABORATORIES, INC." }, + { 0x009011, "WAVTRACE, INC." }, + { 0x009012, "GLOBESPAN SEMICONDUCTOR, INC." }, + { 0x009013, "SAMSAN CORP." }, + { 0x009014, "ROTORK INSTRUMENTS, LTD." }, + { 0x009015, "CENTIGRAM COMMUNICATIONS CORP." }, + { 0x009016, "ZAC" }, + { 0x009017, "ZYPCOM, INC." }, + { 0x009018, "ITO ELECTRIC INDUSTRY CO, LTD." }, + { 0x009019, "HERMES ELECTRONICS CO., LTD." }, + { 0x00901A, "UNISPHERE SOLUTIONS" }, + { 0x00901B, "DIGITAL CONTROLS" }, + { 0x00901C, "MPS SOFTWARE GMBH" }, + { 0x00901D, "PEC (NZ) LTD." }, + { 0x00901E, "SELESTA INGEGNE RIA S.P.A." }, + { 0x00901F, "ADTEC PRODUCTIONS, INC." }, + { 0x009020, "PHILIPS ANALYTICAL X-RAY B.V." }, + { 0x009021, "CISCO SYSTEMS, INC." }, + { 0x009022, "IVEX" }, + { 0x009023, "ZILOG INC." }, + { 0x009024, "PIPELINKS, INC." }, + { 0x009025, "VISION SYSTEMS LTD. PTY" }, + { 0x009026, "ADVANCED SWITCHING" }, + { 0x009027, "INTEL CORPORATION" }, + { 0x009028, "NIPPON SIGNAL CO., LTD." }, + { 0x009029, "CRYPTO AG" }, + { 0x00902A, "COMMUNICATION DEVICES, INC." }, + { 0x00902B, "CISCO SYSTEMS, INC." }, + { 0x00902C, "DATA & CONTROL EQUIPMENT LTD." }, + { 0x00902D, "DATA ELECTRONICS" }, + { 0x00902E, "NAMCO LIMITED" }, + { 0x00902F, "NETCORE SYSTEMS, INC." }, + { 0x009030, "HONEYWELL-DATING" }, + { 0x009031, "MYSTICOM, LTD." }, + { 0x009032, "PELCOMBE GROUP LTD." }, + { 0x009033, "INNOVAPHONE GMBH" }, + { 0x009034, "IMAGIC, INC." }, + { 0x009035, "ALPHA TELECOM, INC." }, + { 0x009036, "ENS, INC." }, + { 0x009037, "ACUCOMM, INC." }, + { 0x009038, "FOUNTAIN TECHNOLOGIES, INC." }, + { 0x009039, "SHASTA NETWORKS" }, + { 0x00903A, "NIHON MEDIA TOOL INC." }, + { 0x00903B, "TRIEMS RESEARCH LAB, INC." }, + { 0x00903C, "ATLANTIC NETWORK SYSTEMS" }, + { 0x00903D, "BIOPAC SYSTEMS, INC." }, + { 0x00903E, "N.V. PHILIPS INDUSTRIAL" }, + { 0x00903F, "AZTEC RADIOMEDIA" }, + { 0x009040, "CASTLE NETWORKS, INC." }, + { 0x009041, "APPLIED DIGITAL ACCESS" }, + { 0x009042, "ECCS" }, + { 0x009043, "NICHIBEI DENSHI CO., LTD." }, + { 0x009044, "ASSURED DIGITAL, INC." }, + { 0x009045, "MARIPOSA TECHNOLOGY" }, + { 0x009046, "DEXDYNE, LTD." }, + { 0x009047, "GIGA FAST E. LTD." }, + { 0x009048, "ZEAL CORPORATION" }, + { 0x009049, "ENTRIDIA CORPORATION" }, + { 0x00904A, "CONCUR SYSTEM TECHNOLOGIES" }, + { 0x00904B, "GEMTEK TECHNOLOGY CO., LTD." }, + { 0x00904C, "EPIGRAM, INC." }, + { 0x00904D, "SPEC S.A." }, + { 0x00904E, "DELEM BV" }, + { 0x00904F, "ABB POWER T&D COMPANY, INC." }, + { 0x009050, "TELESTE OY" }, + { 0x009051, "ULTIMATE TECHNOLOGY CORP." }, + { 0x009052, "SELCOM ELETTRONICA S.R.L." }, + { 0x009053, "DAEWOO ELECTRONICS CO., LTD." }, + { 0x009054, "INNOVATIVE SEMICONDUCTORS, INC" }, + { 0x009055, "PARKER HANNIFIN CORPORATION" }, + { 0x009056, "TELESTREAM, INC." }, + { 0x009057, "AANETCOM, INC." }, + { 0x009058, "ULTRA ELECTRONICS LTD." }, + { 0x009059, "TELECOM DEVICE K.K." }, + { 0x00905A, "DEARBORN GROUP, INC." }, + { 0x00905B, "RAYMOND AND LAE ENGINEERING" }, + { 0x00905C, "EDMI" }, + { 0x00905D, "NETCOM SICHERHEITSTECHNIK GMBH" }, + { 0x00905E, "RAULAND-BORG CORPORATION" }, + { 0x00905F, "CISCO SYSTEMS, INC." }, + { 0x009060, "SYSTEM CREATE CORP." }, + { 0x009061, "PACIFIC RESEARCH & ENGINEERING" }, + { 0x009062, "ICP VORTEX COMPUTERSYSTEME" }, + { 0x009063, "COHERENT COMMUNICATIONS" }, + { 0x009064, "THOMSON BROADCAST SYSTEMS" }, + { 0x009065, "FINISAR CORPORATION" }, + { 0x009066, "Troika Networks, Inc." }, + { 0x009067, "WALKABOUT COMPUTERS, INC." }, + { 0x009068, "DVT CORP." }, + { 0x009069, "JUNIPER NETWORKS, INC." }, + { 0x00906A, "TURNSTONE SYSTEMS, INC." }, + { 0x00906B, "APPLIED RESOURCES, INC." }, + { 0x00906C, "GWT GLOBAL WEIGHING" }, + { 0x00906D, "CISCO SYSTEMS, INC." }, + { 0x00906E, "PRAXON, INC." }, + { 0x00906F, "CISCO SYSTEMS, INC." }, + { 0x009070, "NEO NETWORKS, INC." }, + { 0x009071, "BADGER TECHNOLOGY, INC." }, + { 0x009072, "SIMRAD AS" }, + { 0x009073, "GAIO TECHNOLOGY" }, + { 0x009074, "ARGON NETWORKS, INC." }, + { 0x009075, "NEC DO BRASIL S.A." }, + { 0x009076, "FMT AIRCRAFT GATE SUPPORT" }, + { 0x009077, "ADVANCED FIBRE COMMUNICATIONS" }, + { 0x009078, "MER TELEMANAGEMENT" }, + { 0x009079, "CLEARONE INC." }, + { 0x00907A, "SPECTRALINK CORP." }, + { 0x00907B, "E-TECH, INC." }, + { 0x00907C, "DIGITALCAST, INC." }, + { 0x00907D, "HOME WIRELESS NETWORKS" }, + { 0x00907E, "VETRONIX CORP." }, + { 0x00907F, "WATCHGUARD TECHNOLOGIES, INC." }, + { 0x009080, "NOT LIMITED, INC." }, + { 0x009081, "ALOHA NETWORKS, INC." }, + { 0x009082, "FORCE INSTITUTE" }, + { 0x009083, "TURBO COMMUNICATION, INC." }, + { 0x009084, "ATECH SYSTEM" }, + { 0x009085, "GOLDEN ENTERPRISES, INC." }, + { 0x009086, "CISCO SYSTEMS, INC." }, + { 0x009087, "ITIS" }, + { 0x009088, "BAXALL SECURITY LTD." }, + { 0x009089, "SOFTCOM MICROSYSTEMS, INC." }, + { 0x00908A, "BAYLY COMMUNICATIONS, INC." }, + { 0x00908B, "CELL COMPUTING, INC." }, + { 0x00908C, "ETREND ELECTRONICS, INC." }, + { 0x00908D, "VICKERS ELECTRONICS SYSTEMS" }, + { 0x00908E, "Nortel Networks Broadband Access" }, + { 0x00908F, "AUDIOCODES LTD." }, + { 0x009090, "I-BUS" }, + { 0x009091, "DIGITALSCAPE, INC." }, + { 0x009092, "CISCO SYSTEMS, INC." }, + { 0x009093, "NANAO CORPORATION" }, + { 0x009094, "OSPREY TECHNOLOGIES, INC." }, + { 0x009095, "UNIVERSAL AVIONICS" }, + { 0x009096, "ASKEY COMPUTER CORP." }, + { 0x009097, "SYCAMORE NETWORKS" }, + { 0x009098, "SBC DESIGNS, INC." }, + { 0x009099, "ALLIED TELESIS,K.K." }, + { 0x00909A, "ONE WORLD SYSTEMS, INC." }, + { 0x00909B, "MARKPOINT AB" }, + { 0x00909C, "COMBOX, LTD." }, + { 0x00909D, "GSE SYSTEMS, INC." }, + { 0x00909E, "DELPHI ENGINEERING GROUP" }, + { 0x00909F, "DIGI-DATA CORPORATION" }, + { 0x0090A0, "8X8 INC." }, + { 0x0090A1, "FLYING PIG SYSTEMS, LTD." }, + { 0x0090A2, "CYBERTAN TECHNOLOGY, INC." }, + { 0x0090A3, "MEDIALINCS CO., LTD." }, + { 0x0090A4, "ALTIGA NETWORKS" }, + { 0x0090A5, "SPECTRA LOGIC" }, + { 0x0090A6, "CISCO SYSTEMS, INC." }, + { 0x0090A7, "CLIENTEC CORPORATION" }, + { 0x0090A8, "NINETILES NETWORKS LTD." }, + { 0x0090A9, "WESTERN DIGITAL" }, + { 0x0090AA, "INDIGO ACTIVE VISION" }, + { 0x0090AB, "CISCO SYSTEMS, INC." }, + { 0x0090AC, "OPTIVISION, INC." }, + { 0x0090AD, "ASPECT ELECTRONICS, INC." }, + { 0x0090AE, "ITALTEL SPA" }, + { 0x0090AF, "J. MORITA MFG. CORP." }, + { 0x0090B0, "VADEM" }, + { 0x0090B1, "CISCO SYSTEMS, INC." }, + { 0x0090B2, "AVICI SYSTEMS INC." }, + { 0x0090B3, "AGRANAT SYSTEMS" }, + { 0x0090B4, "WILLOWBROOK TECHNOLOGIES" }, + { 0x0090B5, "NIKON CORPORATION" }, + { 0x0090B6, "FIBEX SYSTEMS" }, + { 0x0090B7, "DIGITAL LIGHTWAVE, INC." }, + { 0x0090B8, "ROHDE & SCHWARZ GMBH & CO. KG" }, + { 0x0090B9, "BERAN INSTRUMENTS LTD." }, + { 0x0090BA, "VALID NETWORKS, INC." }, + { 0x0090BB, "TAINET COMMUNICATION SYSTEM" }, + { 0x0090BC, "TELEMANN CO., LTD." }, + { 0x0090BD, "OMNIA COMMUNICATIONS, INC." }, + { 0x0090BE, "IBC/INTEGRATED BUSINESS" }, + { 0x0090BF, "CISCO SYSTEMS, INC." }, + { 0x0090C0, "K.J. LAW ENGINEERS, INC." }, + { 0x0090C1, "EDA INDUSTRIES" }, + { 0x0090C2, "JK MICROSYSTEMS, INC." }, + { 0x0090C3, "TOPIC SEMICONDUCTOR CORP." }, + { 0x0090C4, "JAVELIN SYSTEMS, INC." }, + { 0x0090C5, "INTERNET MAGIC, INC." }, + { 0x0090C6, "OPTIM SYSTEMS, INC." }, + { 0x0090C7, "ICOM INC." }, + { 0x0090C8, "WAVERIDER COMMUNICATIONS" }, + { 0x0090C9, "PRODUCTIVITY ENHANCEMENT" }, + { 0x0090CA, "ACCORD VIDEO" }, + { 0x0090CB, "WIRELESS ONLINE, INC." }, + { 0x0090CC, "PLANEX COMMUNICATIONS, INC." }, + { 0x0090CD, "ENT-EMPRESA NACIONAL" }, + { 0x0090CE, "TETRA GMBH" }, + { 0x0090CF, "NORTEL" }, + { 0x0090D0, "ALCATEL BELL" }, + { 0x0090D1, "LEICHU ENTERPRISE CO., LTD." }, + { 0x0090D2, "ARTEL VIDEO SYSTEMS" }, + { 0x0090D3, "GIESECKE & DEVRIENT GMBH" }, + { 0x0090D4, "BINDVIEW DEVELOPMENT CORP." }, + { 0x0090D5, "EUPHONIX, INC." }, + { 0x0090D6, "CRYSTAL GROUP" }, + { 0x0090D7, "NETBOOST CORP." }, + { 0x0090D8, "WHITECROSS SYSTEMS" }, + { 0x0090D9, "CISCO SYSTEMS, INC." }, + { 0x0090DA, "DYNARC, INC." }, + { 0x0090DB, "NEXT LEVEL COMMUNICATIONS" }, + { 0x0090DC, "TECO INFORMATION SYSTEMS" }, + { 0x0090DD, "THE MIHARU COMMUNICATIONS" }, + { 0x0090DE, "CARDKEY SYSTEMS, INC." }, + { 0x0090DF, "MITSUBISHI CHEMICAL" }, + { 0x0090E0, "SYSTRAN CORP." }, + { 0x0090E1, "TELENA S.P.A." }, + { 0x0090E2, "DISTRIBUTED PROCESSING" }, + { 0x0090E3, "AVEX ELECTRONICS INC." }, + { 0x0090E4, "NEC AMERICA, INC." }, + { 0x0090E5, "TEKNEMA, INC." }, + { 0x0090E6, "ACER LABORATORIES, INC." }, + { 0x0090E7, "HORSCH ELEKTRONIK AG" }, + { 0x0090E8, "MOXA TECHNOLOGIES CORP., LTD." }, + { 0x0090E9, "JANZ COMPUTER AG" }, + { 0x0090EA, "ALPHA TECHNOLOGIES, INC." }, + { 0x0090EB, "SENTRY TELECOM SYSTEMS" }, + { 0x0090EC, "PYRESCOM" }, + { 0x0090ED, "CENTRAL SYSTEM RESEARCH" }, + { 0x0090EE, "PERSONAL COMMUNICATIONS" }, + { 0x0090EF, "INTEGRIX, INC." }, + { 0x0090F0, "HARMONIC LIGHTWAVES, LTD." }, + { 0x0090F1, "DOT HILL SYSTEMS CORPORATION" }, + { 0x0090F2, "CISCO SYSTEMS, INC." }, + { 0x0090F3, "ASPECT COMMUNICATIONS" }, + { 0x0090F4, "LIGHTNING INSTRUMENTATION" }, + { 0x0090F5, "CLEVO CO." }, + { 0x0090F6, "ESCALATE NETWORKS, INC." }, + { 0x0090F7, "NBASE COMMUNICATIONS LTD." }, + { 0x0090F8, "MEDIATRIX TELECOM" }, + { 0x0090F9, "LEITCH" }, + { 0x0090FA, "GIGANET, INC." }, + { 0x0090FB, "PORTWELL, INC." }, + { 0x0090FC, "NETWORK COMPUTING DEVICES" }, + { 0x0090FD, "COPPERCOM, INC." }, + { 0x0090FE, "ELECOM CO., LTD. (LANEED DIV." }, + { 0x0090FF, "TELLUS TECHNOLOGY INC." }, + { 0x009D8E, "CARDIAC RECORDERS, INC." }, + { 0x00A000, "CENTILLION NETWORKS, INC." }, + { 0x00A001, "WATKINS-JOHNSON COMPANY" }, + { 0x00A002, "LEEDS & NORTHRUP AUSTRALIA" }, + { 0x00A003, "STAEFA CONTROL SYSTEM" }, + { 0x00A004, "NETPOWER, INC." }, + { 0x00A005, "DANIEL INSTRUMENTS, LTD." }, + { 0x00A006, "IMAGE DATA PROCESSING" }, + { 0x00A007, "APEXX TECHNOLOGY, INC." }, + { 0x00A008, "NETCORP" }, + { 0x00A009, "WHITETREE NETWORK" }, + { 0x00A00A, "R.D.C. COMMUNICATION" }, + { 0x00A00B, "COMPUTEX CO., LTD." }, + { 0x00A00C, "KINGMAX TECHNOLOGY, INC." }, + { 0x00A00D, "THE PANDA PROJECT" }, + { 0x00A00E, "VISUAL NETWORKS, INC." }, + { 0x00A00F, "Broadband Technologies" }, + { 0x00A010, "SYSLOGIC DATENTECHNIK AG" }, + { 0x00A011, "MUTOH INDUSTRIES LTD." }, + { 0x00A012, "B.A.T.M. ADVANCED TECHNOLOGIES" }, + { 0x00A013, "TELTREND LTD." }, + { 0x00A014, "CSIR" }, + { 0x00A015, "WYLE" }, + { 0x00A016, "MICROPOLIS CORP." }, + { 0x00A017, "J B M CORPORATION" }, + { 0x00A018, "CREATIVE CONTROLLERS, INC." }, + { 0x00A019, "NEBULA CONSULTANTS, INC." }, + { 0x00A01A, "BINAR ELEKTRONIK AB" }, + { 0x00A01B, "PREMISYS COMMUNICATIONS, INC." }, + { 0x00A01C, "NASCENT NETWORKS CORPORATION" }, + { 0x00A01D, "SIXNET" }, + { 0x00A01E, "EST CORPORATION" }, + { 0x00A01F, "TRICORD SYSTEMS, INC." }, + { 0x00A020, "CITICORP/TTI" }, + { 0x00A021, "GENERAL DYNAMICS-" }, + { 0x00A022, "CENTRE FOR DEVELOPMENT OF" }, + { 0x00A023, "APPLIED CREATIVE TECHNOLOGY," }, + { 0x00A024, "3COM CORPORATION" }, + { 0x00A025, "REDCOM LABS INC." }, + { 0x00A026, "TELDAT, S.A." }, + { 0x00A027, "FIREPOWER SYSTEMS, INC." }, + { 0x00A028, "CONNER PERIPHERALS" }, + { 0x00A029, "COULTER CORPORATION" }, + { 0x00A02A, "TRANCELL SYSTEMS" }, + { 0x00A02B, "TRANSITIONS RESEARCH CORP." }, + { 0x00A02C, "INTERWAVE COMMUNICATIONS" }, + { 0x00A02D, "1394 Trade Association" }, + { 0x00A02E, "BRAND COMMUNICATIONS, LTD." }, + { 0x00A02F, "PIRELLI CAVI" }, + { 0x00A030, "CAPTOR NV/SA" }, + { 0x00A031, "HAZELTINE CORPORATION, MS 1-17" }, + { 0x00A032, "GES SINGAPORE PTE. LTD." }, + { 0x00A033, "IMC MESS-SYSTEME GMBH" }, + { 0x00A034, "AXEL" }, + { 0x00A035, "CYLINK CORPORATION" }, + { 0x00A036, "APPLIED NETWORK TECHNOLOGY" }, + { 0x00A037, "DATASCOPE CORPORATION" }, + { 0x00A038, "EMAIL ELECTRONICS" }, + { 0x00A039, "ROSS TECHNOLOGY, INC." }, + { 0x00A03A, "KUBOTEK CORPORATION" }, + { 0x00A03B, "TOSHIN ELECTRIC CO., LTD." }, + { 0x00A03C, "EG&G NUCLEAR INSTRUMENTS" }, + { 0x00A03D, "OPTO - 22" }, + { 0x00A03E, "ATM FORUM" }, + { 0x00A03F, "COMPUTER SOCIETY MICROPROCES'R" }, + { 0x00A040, "APPLE COMPUTER" }, + { 0x00A041, "LEYBOLD-INFICON" }, + { 0x00A042, "SPUR PRODUCTS CORP." }, + { 0x00A043, "AMERICAN TECHNOLOGY LABS, INC." }, + { 0x00A044, "NTT INTELLIGENT TECHNOLOGY" }, + { 0x00A045, "PHOENIX CONTACT GMBH & CO." }, + { 0x00A046, "SCITEX CORP. LTD." }, + { 0x00A047, "INTEGRATED FITNESS CORP." }, + { 0x00A048, "QUESTECH, LTD." }, + { 0x00A049, "DIGITECH INDUSTRIES, INC." }, + { 0x00A04A, "NISSHIN ELECTRIC CO., LTD." }, + { 0x00A04B, "TFL LAN INC." }, + { 0x00A04C, "INNOVATIVE SYSTEMS & TECH. INC" }, + { 0x00A04D, "EDA INSTRUMENTS, INC." }, + { 0x00A04E, "VOELKER TECHNOLOGIES, INC." }, + { 0x00A04F, "AMERITEC CORP." }, + { 0x00A050, "CYPRESS SEMICONDUCTOR" }, + { 0x00A051, "ANGIA COMMUNICATIONS. INC." }, + { 0x00A052, "STANILITE ELECTRONICS PTY. LTD" }, + { 0x00A053, "COMPACT DEVICES, INC." }, + { 0x00A055, "LINKTECH, INC." }, + { 0x00A056, "MICROPROSS" }, + { 0x00A057, "ELSA AG" }, + { 0x00A058, "GLORY, LTD." }, + { 0x00A059, "HAMILTON HALLMARK" }, + { 0x00A05A, "KOFAX IMAGE PRODUCTS" }, + { 0x00A05B, "MARQUIP, INC." }, + { 0x00A05C, "INVENTORY CONVERSION, INC./" }, + { 0x00A05D, "CS COMPUTER SYSTEME GMBH" }, + { 0x00A05E, "MYRIAD LOGIC INC." }, + { 0x00A05F, "BTG ENGINEERING BV" }, + { 0x00A060, "ACER PERIPHERALS, INC." }, + { 0x00A061, "PURITAN BENNETT" }, + { 0x00A062, "AES PRODATA" }, + { 0x00A063, "JRL SYSTEMS, INC." }, + { 0x00A064, "KVB/ANALECT" }, + { 0x00A065, "NEXLAND, INC." }, + { 0x00A066, "ISA CO., LTD." }, + { 0x00A067, "NETWORK SERVICES GROUP" }, + { 0x00A068, "BHP LIMITED" }, + { 0x00A069, "TrueTime" }, + { 0x00A06A, "VERILINK CORP." }, + { 0x00A06B, "DMS DORSCH MIKROSYSTEM GMBH" }, + { 0x00A06C, "SHINDENGEN ELECTRIC MFG." }, + { 0x00A06D, "MANNESMANN TALLY CORPORATION" }, + { 0x00A06E, "AUSTRON, INC." }, + { 0x00A06F, "THE APPCON GROUP, INC." }, + { 0x00A070, "COASTCOM" }, + { 0x00A071, "VIDEO LOTTERY TECHNOLOGIES,INC" }, + { 0x00A072, "OVATION SYSTEMS LTD." }, + { 0x00A073, "COM21, INC." }, + { 0x00A074, "PERCEPTION TECHNOLOGY" }, + { 0x00A075, "MICRON TECHNOLOGY, INC." }, + { 0x00A076, "CARDWARE LAB, INC." }, + { 0x00A077, "FUJITSU NEXION, INC." }, + { 0x00A078, "Marconi Communications" }, + { 0x00A079, "ALPS ELECTRIC (USA), INC." }, + { 0x00A07A, "ADVANCED PERIPHERALS" }, + { 0x00A07B, "DAWN COMPUTER INCORPORATION" }, + { 0x00A07C, "TONYANG NYLON CO., LTD." }, + { 0x00A07D, "SEEQ TECHNOLOGY, INC." }, + { 0x00A07E, "AVID TECHNOLOGY, INC." }, + { 0x00A07F, "GSM-SYNTEL, LTD." }, + { 0x00A080, "ANTARES MICROSYSTEMS" }, + { 0x00A081, "ALCATEL DATA NETWORKS" }, + { 0x00A082, "NKT ELEKTRONIK A/S" }, + { 0x00A083, "ASIMMPHONY TURKEY" }, + { 0x00A084, "DATAPLEX PTY. LTD." }, + { 0x00A086, "AMBER WAVE SYSTEMS, INC." }, + { 0x00A087, "MITEL SEMICONDUCTOR, LTD." }, + { 0x00A088, "ESSENTIAL COMMUNICATIONS" }, + { 0x00A089, "XPOINT TECHNOLOGIES, INC." }, + { 0x00A08A, "BROOKTROUT TECHNOLOGY, INC." }, + { 0x00A08B, "ASTON ELECTRONIC DESIGNS LTD." }, + { 0x00A08C, "MULTIMEDIA LANS, INC." }, + { 0x00A08D, "JACOMO CORPORATION" }, + { 0x00A08E, "Nokia Internet Communications" }, + { 0x00A08F, "DESKNET SYSTEMS, INC." }, + { 0x00A090, "TIMESTEP CORPORATION" }, + { 0x00A091, "APPLICOM INTERNATIONAL" }, + { 0x00A092, "H. BOLLMANN MANUFACTURERS, LTD" }, + { 0x00A093, "B/E AEROSPACE" }, + { 0x00A094, "COMSAT CORPORATION" }, + { 0x00A095, "ACACIA NETWORKS, INC." }, + { 0x00A096, "MITSUMI ELECTRIC CO., LTD." }, + { 0x00A097, "JC INFORMATION SYSTEMS" }, + { 0x00A098, "NETWORK APPLIANCE CORP." }, + { 0x00A099, "K-NET LTD." }, + { 0x00A09A, "NIHON KOHDEN AMERICA" }, + { 0x00A09B, "QPSX COMMUNICATIONS, LTD." }, + { 0x00A09C, "XYPLEX, INC." }, + { 0x00A09D, "JOHNATHON FREEMAN TECHNOLOGIES" }, + { 0x00A09E, "ICTV" }, + { 0x00A09F, "COMMVISION CORP." }, + { 0x00A0A0, "COMPACT DATA, LTD." }, + { 0x00A0A1, "EPIC DATA INC." }, + { 0x00A0A2, "DIGICOM S.P.A." }, + { 0x00A0A3, "RELIABLE POWER METERS" }, + { 0x00A0A4, "MICROS SYSTEMS, INC." }, + { 0x00A0A5, "TEKNOR MICROSYSTEME, INC." }, + { 0x00A0A6, "M.I. SYSTEMS, K.K." }, + { 0x00A0A7, "VORAX CORPORATION" }, + { 0x00A0A8, "RENEX CORPORATION" }, + { 0x00A0A9, "GN NETTEST (CANADA) INC." }, + { 0x00A0AA, "SPACELABS MEDICAL" }, + { 0x00A0AB, "NETCS INFORMATIONSTECHNIK GMBH" }, + { 0x00A0AC, "GILAT SATELLITE NETWORKS, LTD." }, + { 0x00A0AD, "MARCONI SPA" }, + { 0x00A0AE, "NUCOM SYSTEMS, INC." }, + { 0x00A0AF, "WMS INDUSTRIES" }, + { 0x00A0B0, "I-O DATA DEVICE, INC." }, + { 0x00A0B1, "FIRST VIRTUAL CORPORATION" }, + { 0x00A0B2, "SHIMA SEIKI" }, + { 0x00A0B3, "ZYKRONIX" }, + { 0x00A0B4, "TEXAS MICROSYSTEMS, INC." }, + { 0x00A0B5, "3H TECHNOLOGY" }, + { 0x00A0B6, "SANRITZ AUTOMATION CO., LTD." }, + { 0x00A0B7, "CORDANT, INC." }, + { 0x00A0B8, "SYMBIOS LOGIC INC." }, + { 0x00A0B9, "EAGLE TECHNOLOGY, INC." }, + { 0x00A0BA, "PATTON ELECTRONICS CO." }, + { 0x00A0BB, "HILAN GMBH" }, + { 0x00A0BC, "VIASAT, INCORPORATED" }, + { 0x00A0BD, "I-TECH CORP." }, + { 0x00A0BE, "INTEGRATED CIRCUIT SYSTEMS,INC" }, + { 0x00A0BF, "WIRELESS DATA GROUP MOTOROLA" }, + { 0x00A0C0, "DIGITAL LINK CORP." }, + { 0x00A0C1, "ORTIVUS MEDICAL AB" }, + { 0x00A0C2, "R.A. SYSTEMS CO., LTD." }, + { 0x00A0C3, "UNICOMPUTER GMBH" }, + { 0x00A0C4, "CRISTIE ELECTRONICS LTD." }, + { 0x00A0C5, "ZYXEL COMMUNICATION" }, + { 0x00A0C6, "QUALCOMM INCORPORATED" }, + { 0x00A0C7, "TADIRAN TELECOMMUNICATIONS" }, + { 0x00A0C8, "ADTRAN INC." }, + { 0x00A0C9, "INTEL CORPORATION - HF1-06" }, + { 0x00A0CA, "FUJITSU DENSO LTD." }, + { 0x00A0CB, "ARK TELECOMMUNICATIONS, INC." }, + { 0x00A0CC, "LITE-ON COMMUNICATIONS, INC." }, + { 0x00A0CD, "DR. JOHANNES HEIDENHAIN GMBH" }, + { 0x00A0CE, "ASTROCOM CORPORATION" }, + { 0x00A0CF, "SOTAS, INC." }, + { 0x00A0D0, "TEN X TECHNOLOGY, INC." }, + { 0x00A0D1, "INVENTEC CORPORATION" }, + { 0x00A0D2, "ALLIED TELESIS INTERNATIONAL" }, + { 0x00A0D3, "INSTEM COMPUTER SYSTEMS, LTD." }, + { 0x00A0D4, "RADIOLAN, INC." }, + { 0x00A0D5, "SIERRA WIRELESS INC." }, + { 0x00A0D6, "SBE, INC." }, + { 0x00A0D7, "KASTEN CHASE APPLIED RESEARCH" }, + { 0x00A0D8, "SPECTRA - TEK" }, + { 0x00A0D9, "CONVEX COMPUTER CORPORATION" }, + { 0x00A0DA, "INTEGRATED SYSTEMS" }, + { 0x00A0DB, "FISHER & PAYKEL PRODUCTION" }, + { 0x00A0DC, "O.N. ELECTRONIC CO., LTD." }, + { 0x00A0DD, "AZONIX CORPORATION" }, + { 0x00A0DE, "YAMAHA CORPORATION" }, + { 0x00A0DF, "STS TECHNOLOGIES, INC." }, + { 0x00A0E0, "TENNYSON TECHNOLOGIES PTY LTD" }, + { 0x00A0E1, "WESTPORT RESEARCH" }, + { 0x00A0E2, "KEISOKU GIKEN CORP." }, + { 0x00A0E3, "XKL SYSTEMS CORP." }, + { 0x00A0E4, "OPTIQUEST" }, + { 0x00A0E5, "NHC COMMUNICATIONS" }, + { 0x00A0E6, "DIALOGIC CORPORATION" }, + { 0x00A0E7, "CENTRAL DATA CORPORATION" }, + { 0x00A0E8, "REUTERS HOLDINGS PLC" }, + { 0x00A0E9, "ELECTRONIC RETAILING SYSTEMS" }, + { 0x00A0EA, "ETHERCOM CORP." }, + { 0x00A0EB, "FASTCOMM COMMUNICATIONS CORP." }, + { 0x00A0EC, "TRANSMITTON LTD." }, + { 0x00A0ED, "PRI AUTOMATION" }, + { 0x00A0EE, "NASHOBA NETWORKS" }, + { 0x00A0EF, "LUCIDATA LTD." }, + { 0x00A0F0, "TORONTO MICROELECTRONICS INC." }, + { 0x00A0F1, "MTI" }, + { 0x00A0F2, "INFOTEK COMMUNICATIONS, INC." }, + { 0x00A0F3, "STAUBLI" }, + { 0x00A0F4, "GE" }, + { 0x00A0F5, "RADGUARD LTD." }, + { 0x00A0F6, "AUTOGAS SYSTEMS, INC." }, + { 0x00A0F7, "V.I COMPUTER CORP." }, + { 0x00A0F8, "SYMBOL TECHNOLOGIES, INC." }, + { 0x00A0F9, "BINTEC COMMUNICATIONS GMBH" }, + { 0x00A0FA, "Marconi Communication GmbH" }, + { 0x00A0FB, "TORAY ENGINEERING CO., LTD." }, + { 0x00A0FC, "IMAGE SCIENCES, INC." }, + { 0x00A0FD, "SCITEX DIGITAL PRINTING, INC." }, + { 0x00A0FE, "BOSTON TECHNOLOGY, INC." }, + { 0x00A0FF, "TELLABS OPERATIONS, INC." }, + { 0x00AA00, "INTEL CORPORATION" }, + { 0x00AA01, "INTEL CORPORATION" }, + { 0x00AA02, "INTEL CORPORATION" }, + { 0x00AA3C, "OLIVETTI TELECOM SPA (OLTECO)" }, + { 0x00B009, "Grass Valley Group" }, + { 0x00B017, "InfoGear Technology Corp." }, + { 0x00B019, "Casi-Rusco" }, + { 0x00B01C, "Westport Technologies" }, + { 0x00B01E, "Rantic Labs, Inc." }, + { 0x00B02A, "ORSYS GmbH" }, + { 0x00B02D, "ViaGate Technologies, Inc." }, + { 0x00B03B, "HiQ Networks" }, + { 0x00B048, "Marconi Communications Inc." }, + { 0x00B04A, "Cisco Systems, Inc." }, + { 0x00B052, "Intellon Corporation" }, + { 0x00B064, "Cisco Systems, Inc." }, + { 0x00B069, "Honewell Oy" }, + { 0x00B06D, "Jones Futurex Inc." }, + { 0x00B080, "Mannesmann Ipulsys B.V." }, + { 0x00B086, "LocSoft Limited" }, + { 0x00B08E, "Cisco Systems, Inc." }, + { 0x00B091, "Transmeta Corp." }, + { 0x00B094, "Alaris, Inc." }, + { 0x00B09A, "Morrow Technologies Corp." }, + { 0x00B09D, "Point Grey Research Inc." }, + { 0x00B0AC, "SIAE-Microelettronica S.p.A." }, + { 0x00B0AE, "Symmetricom" }, + { 0x00B0B3, "Xstreamis PLC" }, + { 0x00B0C2, "Cisco Systems, Inc." }, + { 0x00B0C7, "Tellabs Operations, Inc." }, + { 0x00B0CE, "TECHNOLOGY RESCUE" }, + { 0x00B0D0, "Dell Computer Corp." }, + { 0x00B0DB, "Nextcell, Inc." }, + { 0x00B0DF, "Reliable Data Technology, Inc." }, + { 0x00B0E7, "British Federal Ltd." }, + { 0x00B0EC, "EACEM" }, + { 0x00B0EE, "Ajile Systems, Inc." }, + { 0x00B0F0, "CALY NETWORKS" }, + { 0x00B0F5, "NetWorth Technologies, Inc." }, + { 0x00BB01, "OCTOTHORPE CORP." }, + { 0x00BBF0, "UNGERMANN-BASS INC." }, + { 0x00C000, "LANOPTICS, LTD." }, + { 0x00C001, "DIATEK PATIENT MANAGMENT" }, + { 0x00C002, "SERCOMM CORPORATION" }, + { 0x00C003, "GLOBALNET COMMUNICATIONS" }, + { 0x00C004, "JAPAN BUSINESS COMPUTER CO.LTD" }, + { 0x00C005, "LIVINGSTON ENTERPRISES, INC." }, + { 0x00C006, "NIPPON AVIONICS CO., LTD." }, + { 0x00C007, "PINNACLE DATA SYSTEMS, INC." }, + { 0x00C008, "SECO SRL" }, + { 0x00C009, "KT TECHNOLOGY (S) PTE LTD" }, + { 0x00C00A, "MICRO CRAFT" }, + { 0x00C00B, "NORCONTROL A.S." }, + { 0x00C00C, "RELIA TECHNOLGIES" }, + { 0x00C00D, "ADVANCED LOGIC RESEARCH, INC." }, + { 0x00C00E, "PSITECH, INC." }, + { 0x00C00F, "QUANTUM SOFTWARE SYSTEMS LTD." }, + { 0x00C010, "HIRAKAWA HEWTECH CORP." }, + { 0x00C011, "INTERACTIVE COMPUTING DEVICES" }, + { 0x00C012, "NETSPAN CORPORATION" }, + { 0x00C013, "NETRIX" }, + { 0x00C014, "TELEMATICS CALABASAS INT'L,INC" }, + { 0x00C015, "NEW MEDIA CORPORATION" }, + { 0x00C016, "ELECTRONIC THEATRE CONTROLS" }, + { 0x00C017, "FORTE NETWORKS" }, + { 0x00C018, "LANART CORPORATION" }, + { 0x00C019, "LEAP TECHNOLOGY, INC." }, + { 0x00C01A, "COROMETRICS MEDICAL SYSTEMS" }, + { 0x00C01B, "SOCKET COMMUNICATIONS, INC." }, + { 0x00C01C, "INTERLINK COMMUNICATIONS LTD." }, + { 0x00C01D, "GRAND JUNCTION NETWORKS, INC." }, + { 0x00C01E, "LA FRANCAISE DES JEUX" }, + { 0x00C01F, "S.E.R.C.E.L." }, + { 0x00C020, "ARCO ELECTRONIC, CONTROL LTD." }, + { 0x00C021, "NETEXPRESS" }, + { 0x00C022, "LASERMASTER TECHNOLOGIES, INC." }, + { 0x00C023, "TUTANKHAMON ELECTRONICS" }, + { 0x00C024, "EDEN SISTEMAS DE COMPUTACAO SA" }, + { 0x00C025, "DATAPRODUCTS CORPORATION" }, + { 0x00C026, "LANS TECHNOLOGY CO., LTD." }, + { 0x00C027, "CIPHER SYSTEMS, INC." }, + { 0x00C028, "JASCO CORPORATION" }, + { 0x00C029, "KABEL RHEYDT AG" }, + { 0x00C02A, "OHKURA ELECTRIC CO., LTD." }, + { 0x00C02B, "GERLOFF GESELLSCHAFT FUR" }, + { 0x00C02C, "CENTRUM COMMUNICATIONS, INC." }, + { 0x00C02D, "FUJI PHOTO FILM CO., LTD." }, + { 0x00C02E, "NETWIZ" }, + { 0x00C02F, "OKUMA CORPORATION" }, + { 0x00C030, "INTEGRATED ENGINEERING B. V." }, + { 0x00C031, "DESIGN RESEARCH SYSTEMS, INC." }, + { 0x00C032, "I-CUBED LIMITED" }, + { 0x00C033, "TELEBIT COMMUNICATIONS APS" }, + { 0x00C034, "TRANSACTION NETWORK" }, + { 0x00C035, "QUINTAR COMPANY" }, + { 0x00C036, "RAYTECH ELECTRONIC CORP." }, + { 0x00C037, "DYNATEM" }, + { 0x00C038, "RASTER IMAGE PROCESSING SYSTEM" }, + { 0x00C039, "TDK SEMICONDUCTOR CORPORATION" }, + { 0x00C03A, "MEN-MIKRO ELEKTRONIK GMBH" }, + { 0x00C03B, "MULTIACCESS COMPUTING CORP." }, + { 0x00C03C, "TOWER TECH S.R.L." }, + { 0x00C03D, "WIESEMANN & THEIS GMBH" }, + { 0x00C03E, "FA. GEBR. HELLER GMBH" }, + { 0x00C03F, "STORES AUTOMATED SYSTEMS, INC." }, + { 0x00C040, "ECCI" }, + { 0x00C041, "DIGITAL TRANSMISSION SYSTEMS" }, + { 0x00C042, "DATALUX CORP." }, + { 0x00C043, "STRATACOM" }, + { 0x00C044, "EMCOM CORPORATION" }, + { 0x00C045, "ISOLATION SYSTEMS, LTD." }, + { 0x00C046, "KEMITRON LTD." }, + { 0x00C047, "UNIMICRO SYSTEMS, INC." }, + { 0x00C048, "BAY TECHNICAL ASSOCIATES" }, + { 0x00C049, "U.S. ROBOTICS, INC." }, + { 0x00C04A, "GROUP 2000 AG" }, + { 0x00C04B, "CREATIVE MICROSYSTEMS" }, + { 0x00C04C, "DEPARTMENT OF FOREIGN AFFAIRS" }, + { 0x00C04D, "MITEC, INC." }, + { 0x00C04E, "COMTROL CORPORATION" }, + { 0x00C04F, "DELL COMPUTER CORPORATION" }, + { 0x00C050, "TOYO DENKI SEIZO K.K." }, + { 0x00C051, "ADVANCED INTEGRATION RESEARCH" }, + { 0x00C052, "BURR-BROWN" }, + { 0x00C053, "DAVOX CORPORATION" }, + { 0x00C054, "NETWORK PERIPHERALS, LTD." }, + { 0x00C055, "MODULAR COMPUTING TECHNOLOGIES" }, + { 0x00C056, "SOMELEC" }, + { 0x00C057, "MYCO ELECTRONICS" }, + { 0x00C058, "DATAEXPERT CORP." }, + { 0x00C059, "NIPPON DENSO CO., LTD." }, + { 0x00C05A, "SEMAPHORE COMMUNICATIONS CORP." }, + { 0x00C05B, "NETWORKS NORTHWEST, INC." }, + { 0x00C05C, "ELONEX PLC" }, + { 0x00C05D, "L&N TECHNOLOGIES" }, + { 0x00C05E, "VARI-LITE, INC." }, + { 0x00C05F, "FINE-PAL COMPANY LIMITED" }, + { 0x00C060, "ID SCANDINAVIA AS" }, + { 0x00C061, "SOLECTEK CORPORATION" }, + { 0x00C062, "IMPULSE TECHNOLOGY" }, + { 0x00C063, "MORNING STAR TECHNOLOGIES, INC" }, + { 0x00C064, "GENERAL DATACOMM IND. INC." }, + { 0x00C065, "SCOPE COMMUNICATIONS, INC." }, + { 0x00C066, "DOCUPOINT, INC." }, + { 0x00C067, "UNITED BARCODE INDUSTRIES" }, + { 0x00C068, "PHILIP DRAKE ELECTRONICS LTD." }, + { 0x00C069, "ADAPTIVE BROADBAND CORPORATION" }, + { 0x00C06A, "ZAHNER-ELEKTRIK GMBH & CO. KG" }, + { 0x00C06B, "OSI PLUS CORPORATION" }, + { 0x00C06C, "SVEC COMPUTER CORP." }, + { 0x00C06D, "BOCA RESEARCH, INC." }, + { 0x00C06E, "HAFT TECHNOLOGY, INC." }, + { 0x00C06F, "KOMATSU LTD." }, + { 0x00C070, "SECTRA SECURE-TRANSMISSION AB" }, + { 0x00C071, "AREANEX COMMUNICATIONS, INC." }, + { 0x00C072, "KNX LTD." }, + { 0x00C073, "XEDIA CORPORATION" }, + { 0x00C074, "TOYODA AUTOMATIC LOOM" }, + { 0x00C075, "XANTE CORPORATION" }, + { 0x00C076, "I-DATA INTERNATIONAL A-S" }, + { 0x00C077, "DAEWOO TELECOM LTD." }, + { 0x00C078, "COMPUTER SYSTEMS ENGINEERING" }, + { 0x00C079, "FONSYS CO.,LTD." }, + { 0x00C07A, "PRIVA B.V." }, + { 0x00C07B, "ASCEND COMMUNICATIONS, INC." }, + { 0x00C07C, "HIGHTECH INFORMATION" }, + { 0x00C07D, "RISC DEVELOPMENTS LTD." }, + { 0x00C07E, "KUBOTA CORPORATION ELECTRONIC" }, + { 0x00C07F, "NUPON COMPUTING CORP." }, + { 0x00C080, "NETSTAR, INC." }, + { 0x00C081, "METRODATA LTD." }, + { 0x00C082, "MOORE PRODUCTS CO." }, + { 0x00C083, "TRACE MOUNTAIN PRODUCTS, INC." }, + { 0x00C084, "DATA LINK CORP. LTD." }, + { 0x00C085, "ELECTRONICS FOR IMAGING, INC." }, + { 0x00C086, "THE LYNK CORPORATION" }, + { 0x00C087, "UUNET TECHNOLOGIES, INC." }, + { 0x00C088, "EKF ELEKTRONIK GMBH" }, + { 0x00C089, "TELINDUS DISTRIBUTION" }, + { 0x00C08A, "LAUTERBACH DATENTECHNIK GMBH" }, + { 0x00C08B, "RISQ MODULAR SYSTEMS, INC." }, + { 0x00C08C, "PERFORMANCE TECHNOLOGIES, INC." }, + { 0x00C08D, "TRONIX PRODUCT DEVELOPMENT" }, + { 0x00C08E, "NETWORK INFORMATION TECHNOLOGY" }, + { 0x00C08F, "MATSUSHITA ELECTRIC WORKS, LTD" }, + { 0x00C090, "PRAIM S.R.L." }, + { 0x00C091, "JABIL CIRCUIT, INC." }, + { 0x00C092, "MENNEN MEDICAL INC." }, + { 0x00C093, "ALTA RESEARCH CORP." }, + { 0x00C094, "VMX INC." }, + { 0x00C095, "ZNYX" }, + { 0x00C096, "TAMURA CORPORATION" }, + { 0x00C097, "ARCHIPEL SA" }, + { 0x00C098, "CHUNTEX ELECTRONIC CO., LTD." }, + { 0x00C099, "YOSHIKI INDUSTRIAL CO.,LTD." }, + { 0x00C09A, "PHOTONICS CORPORATION" }, + { 0x00C09B, "RELIANCE COMM/TEC, R-TEC" }, + { 0x00C09C, "TOA ELECTRONIC LTD." }, + { 0x00C09D, "DISTRIBUTED SYSTEMS INT'L, INC" }, + { 0x00C09E, "CACHE COMPUTERS, INC." }, + { 0x00C09F, "QUANTA COMPUTER, INC." }, + { 0x00C0A0, "ADVANCE MICRO RESEARCH, INC." }, + { 0x00C0A1, "TOKYO DENSHI SEKEI CO." }, + { 0x00C0A2, "INTERMEDIUM A/S" }, + { 0x00C0A3, "DUAL ENTERPRISES CORPORATION" }, + { 0x00C0A4, "UNIGRAF OY" }, + { 0x00C0A5, "DICKENS DATA SYSTEMS" }, + { 0x00C0A6, "EXICOM AUSTRALIA PTY. LTD" }, + { 0x00C0A7, "SEEL LTD." }, + { 0x00C0A8, "GVC CORPORATION" }, + { 0x00C0A9, "BARRON MCCANN LTD." }, + { 0x00C0AA, "SILICON VALLEY COMPUTER" }, + { 0x00C0AB, "Telco Systems, Inc." }, + { 0x00C0AC, "GAMBIT COMPUTER COMMUNICATIONS" }, + { 0x00C0AD, "MARBEN COMMUNICATION SYSTEMS" }, + { 0x00C0AE, "TOWERCOM CO. INC. DBA PC HOUSE" }, + { 0x00C0AF, "TEKLOGIX INC." }, + { 0x00C0B0, "GCC TECHNOLOGIES,INC." }, + { 0x00C0B1, "GENIUS NET CO." }, + { 0x00C0B2, "NORAND CORPORATION" }, + { 0x00C0B3, "COMSTAT DATACOMM CORPORATION" }, + { 0x00C0B4, "MYSON TECHNOLOGY, INC." }, + { 0x00C0B5, "CORPORATE NETWORK SYSTEMS,INC." }, + { 0x00C0B6, "MERIDIAN DATA, INC." }, + { 0x00C0B7, "AMERICAN POWER CONVERSION CORP" }, + { 0x00C0B8, "FRASER'S HILL LTD." }, + { 0x00C0B9, "FUNK SOFTWARE, INC." }, + { 0x00C0BA, "NETVANTAGE" }, + { 0x00C0BB, "FORVAL CREATIVE, INC." }, + { 0x00C0BC, "TELECOM AUSTRALIA/CSSC" }, + { 0x00C0BD, "INEX TECHNOLOGIES, INC." }, + { 0x00C0BE, "ALCATEL - SEL" }, + { 0x00C0BF, "TECHNOLOGY CONCEPTS, LTD." }, + { 0x00C0C0, "SHORE MICROSYSTEMS, INC." }, + { 0x00C0C1, "QUAD/GRAPHICS, INC." }, + { 0x00C0C2, "INFINITE NETWORKS LTD." }, + { 0x00C0C3, "ACUSON COMPUTED SONOGRAPHY" }, + { 0x00C0C4, "COMPUTER OPERATIONAL" }, + { 0x00C0C5, "SID INFORMATICA" }, + { 0x00C0C6, "PERSONAL MEDIA CORP." }, + { 0x00C0C7, "SPARKTRUM MICROSYSTEMS, INC." }, + { 0x00C0C8, "MICRO BYTE PTY. LTD." }, + { 0x00C0C9, "ELSAG BAILEY PROCESS" }, + { 0x00C0CA, "ALFA, INC." }, + { 0x00C0CB, "CONTROL TECHNOLOGY CORPORATION" }, + { 0x00C0CC, "TELESCIENCES CO SYSTEMS, INC." }, + { 0x00C0CD, "COMELTA, S.A." }, + { 0x00C0CE, "CEI SYSTEMS & ENGINEERING PTE" }, + { 0x00C0CF, "IMATRAN VOIMA OY" }, + { 0x00C0D0, "RATOC SYSTEM INC." }, + { 0x00C0D1, "COMTREE TECHNOLOGY CORPORATION" }, + { 0x00C0D2, "SYNTELLECT, INC." }, + { 0x00C0D3, "OLYMPUS IMAGE SYSTEMS, INC." }, + { 0x00C0D4, "AXON NETWORKS, INC." }, + { 0x00C0D5, "QUANCOM ELECTRONIC GMBH" }, + { 0x00C0D6, "J1 SYSTEMS, INC." }, + { 0x00C0D7, "TAIWAN TRADING CENTER DBA" }, + { 0x00C0D8, "UNIVERSAL DATA SYSTEMS" }, + { 0x00C0D9, "QUINTE NETWORK CONFIDENTIALITY" }, + { 0x00C0DA, "NICE SYSTEMS LTD." }, + { 0x00C0DB, "IPC CORPORATION (PTE) LTD." }, + { 0x00C0DC, "EOS TECHNOLOGIES, INC." }, + { 0x00C0DD, "QLogic Corporation" }, + { 0x00C0DE, "ZCOMM, INC." }, + { 0x00C0DF, "KYE SYSTEMS CORP." }, + { 0x00C0E0, "DSC COMMUNICATION CORP." }, + { 0x00C0E1, "SONIC SOLUTIONS" }, + { 0x00C0E2, "CALCOMP, INC." }, + { 0x00C0E3, "OSITECH COMMUNICATIONS, INC." }, + { 0x00C0E4, "SIEMENS BUILDING" }, + { 0x00C0E5, "GESPAC, S.A." }, + { 0x00C0E6, "Verilink Corporation" }, + { 0x00C0E7, "FIBERDATA AB" }, + { 0x00C0E8, "PLEXCOM, INC." }, + { 0x00C0E9, "OAK SOLUTIONS, LTD." }, + { 0x00C0EA, "ARRAY TECHNOLOGY LTD." }, + { 0x00C0EB, "SEH COMPUTERTECHNIK GMBH" }, + { 0x00C0EC, "DAUPHIN TECHNOLOGY" }, + { 0x00C0ED, "US ARMY ELECTRONIC" }, + { 0x00C0EE, "KYOCERA CORPORATION" }, + { 0x00C0EF, "ABIT CORPORATION" }, + { 0x00C0F0, "KINGSTON TECHNOLOGY CORP." }, + { 0x00C0F1, "SHINKO ELECTRIC CO., LTD." }, + { 0x00C0F2, "TRANSITION NETWORKS" }, + { 0x00C0F3, "NETWORK COMMUNICATIONS CORP." }, + { 0x00C0F4, "INTERLINK SYSTEM CO., LTD." }, + { 0x00C0F5, "METACOMP, INC." }, + { 0x00C0F6, "CELAN TECHNOLOGY INC." }, + { 0x00C0F7, "ENGAGE COMMUNICATION, INC." }, + { 0x00C0F8, "ABOUT COMPUTING INC." }, + { 0x00C0F9, "HARRIS AND JEFFRIES, INC." }, + { 0x00C0FA, "CANARY COMMUNICATIONS, INC." }, + { 0x00C0FB, "ADVANCED TECHNOLOGY LABS" }, + { 0x00C0FC, "ELASTIC REALITY, INC." }, + { 0x00C0FD, "PROSUM" }, + { 0x00C0FE, "APTEC COMPUTER SYSTEMS, INC." }, + { 0x00C0FF, "DOT HILL SYSTEMS CORPORATION" }, + { 0x00CBBD, "Cambridge Broadband Ltd." }, + { 0x00CF1C, "COMMUNICATION MACHINERY CORP." }, + { 0x00D000, "FERRAN SCIENTIFIC, INC." }, + { 0x00D001, "VST TECHNOLOGIES, INC." }, + { 0x00D002, "DITECH CORPORATION" }, + { 0x00D003, "COMDA ENTERPRISES CORP." }, + { 0x00D004, "PENTACOM LTD." }, + { 0x00D005, "ZHS ZEITMANAGEMENTSYSTEME" }, + { 0x00D006, "CISCO SYSTEMS, INC." }, + { 0x00D007, "MIC ASSOCIATES, INC." }, + { 0x00D008, "MACTELL CORPORATION" }, + { 0x00D009, "HSING TECH. ENTERPRISE CO. LTD" }, + { 0x00D00A, "LANACCESS TELECOM S.A." }, + { 0x00D00B, "RHK TECHNOLOGY, INC." }, + { 0x00D00C, "SNIJDER MICRO SYSTEMS" }, + { 0x00D00D, "MICROMERITICS INSTRUMENT" }, + { 0x00D00E, "PLURIS, INC." }, + { 0x00D00F, "SPEECH DESIGN GMBH" }, + { 0x00D010, "CONVERGENT NETWORKS, INC." }, + { 0x00D011, "PRISM VIDEO, INC." }, + { 0x00D012, "GATEWORKS CORP." }, + { 0x00D013, "PRIMEX AEROSPACE COMPANY" }, + { 0x00D014, "ROOT, INC." }, + { 0x00D015, "UNIVEX MICROTECHNOLOGY CORP." }, + { 0x00D016, "SCM MICROSYSTEMS, INC." }, + { 0x00D017, "SYNTECH INFORMATION CO., LTD." }, + { 0x00D018, "QWES. COM, INC." }, + { 0x00D019, "DAINIPPON SCREEN CORPORATE" }, + { 0x00D01A, "URMET SUD S.P.A." }, + { 0x00D01B, "MIMAKI ENGINEERING CO., LTD." }, + { 0x00D01C, "SBS TECHNOLOGIES," }, + { 0x00D01D, "FURUNO ELECTRIC CO., LTD." }, + { 0x00D01E, "PINGTEL CORP." }, + { 0x00D01F, "CTAM PTY. LTD." }, + { 0x00D020, "AIM SYSTEM, INC." }, + { 0x00D021, "REGENT ELECTRONICS CORP." }, + { 0x00D022, "INCREDIBLE TECHNOLOGIES, INC." }, + { 0x00D023, "INFORTREND TECHNOLOGY, INC." }, + { 0x00D024, "Cognex Corporation" }, + { 0x00D025, "XROSSTECH, INC." }, + { 0x00D026, "HIRSCHMANN AUSTRIA GMBH" }, + { 0x00D027, "APPLIED AUTOMATION, INC." }, + { 0x00D028, "OMNEON VIDEO NETWORKS" }, + { 0x00D029, "WAKEFERN FOOD CORPORATION" }, + { 0x00D02A, "FLEXION SYSTEMS" }, + { 0x00D02B, "JETCELL, INC." }, + { 0x00D02C, "CAMPBELL SCIENTIFIC, INC." }, + { 0x00D02D, "ADEMCO" }, + { 0x00D02E, "COMMUNICATION AUTOMATION CORP." }, + { 0x00D02F, "VLSI TECHNOLOGY INC." }, + { 0x00D030, "SAFETRAN SYSTEMS CORP." }, + { 0x00D031, "INDUSTRIAL LOGIC CORPORATION" }, + { 0x00D032, "YANO ELECTRIC CO., LTD." }, + { 0x00D033, "DALIAN DAXIAN NETWORK" }, + { 0x00D034, "ORMEC SYSTEMS CORP." }, + { 0x00D035, "BEHAVIOR TECH. COMPUTER CORP." }, + { 0x00D036, "TECHNOLOGY ATLANTA CORP." }, + { 0x00D037, "PHILIPS-DVS-LO BDR" }, + { 0x00D038, "FIVEMERE, LTD." }, + { 0x00D039, "UTILICOM, INC." }, + { 0x00D03A, "ZONEWORX, INC." }, + { 0x00D03B, "VISION PRODUCTS PTY. LTD." }, + { 0x00D03C, "Vieo, Inc." }, + { 0x00D03E, "ROCKETCHIPS, INC." }, + { 0x00D03F, "AMERICAN COMMUNICATION" }, + { 0x00D040, "SYSMATE CO., LTD." }, + { 0x00D041, "AMIGO TECHNOLOGY CO., LTD." }, + { 0x00D042, "MAHLO GMBH & CO. UG" }, + { 0x00D043, "ZONAL RETAIL DATA SYSTEMS" }, + { 0x00D044, "ALIDIAN NETWORKS, INC." }, + { 0x00D045, "KVASER AB" }, + { 0x00D046, "DOLBY LABORATORIES, INC." }, + { 0x00D047, "XN TECHNOLOGIES" }, + { 0x00D048, "ECTON, INC." }, + { 0x00D049, "IMPRESSTEK CO., LTD." }, + { 0x00D04A, "PRESENCE TECHNOLOGY GMBH" }, + { 0x00D04B, "LA CIE GROUP S.A." }, + { 0x00D04C, "EUROTEL TELECOM LTD." }, + { 0x00D04D, "DIV OF RESEARCH & STATISTICS" }, + { 0x00D04E, "LOGIBAG" }, + { 0x00D04F, "BITRONICS, INC." }, + { 0x00D050, "ISKRATEL" }, + { 0x00D051, "O2 MICRO, INC." }, + { 0x00D052, "ASCEND COMMUNICATIONS, INC." }, + { 0x00D053, "CONNECTED SYSTEMS" }, + { 0x00D054, "SAS INSTITUTE INC." }, + { 0x00D055, "KATHREIN-WERKE KG" }, + { 0x00D056, "SOMAT CORPORATION" }, + { 0x00D057, "ULTRAK, INC." }, + { 0x00D058, "CISCO SYSTEMS, INC." }, + { 0x00D059, "AMBIT MICROSYSTEMS CORP." }, + { 0x00D05A, "SYMBIONICS, LTD." }, + { 0x00D05B, "ACROLOOP MOTION CONTROL" }, + { 0x00D05C, "TECHNOTREND SYSTEMTECHNIK GMBH" }, + { 0x00D05D, "INTELLIWORXX, INC." }, + { 0x00D05E, "STRATABEAM TECHNOLOGY, INC." }, + { 0x00D05F, "VALCOM, INC." }, + { 0x00D060, "PANASONIC EUROPEAN" }, + { 0x00D061, "TREMON ENTERPRISES CO., LTD." }, + { 0x00D062, "DIGIGRAM" }, + { 0x00D063, "CISCO SYSTEMS, INC." }, + { 0x00D064, "MULTITEL" }, + { 0x00D065, "TOKO ELECTRIC" }, + { 0x00D066, "WINTRISS ENGINEERING CORP." }, + { 0x00D067, "CAMPIO COMMUNICATIONS" }, + { 0x00D068, "IWILL CORPORATION" }, + { 0x00D069, "TECHNOLOGIC SYSTEMS" }, + { 0x00D06A, "LINKUP SYSTEMS CORPORATION" }, + { 0x00D06B, "SR TELECOM INC." }, + { 0x00D06C, "SHAREWAVE, INC." }, + { 0x00D06D, "ACRISON, INC." }, + { 0x00D06E, "TRENDVIEW RECORDERS LTD." }, + { 0x00D06F, "KMC CONTROLS" }, + { 0x00D070, "LONG WELL ELECTRONICS CORP." }, + { 0x00D071, "ECHELON CORP." }, + { 0x00D072, "BROADLOGIC" }, + { 0x00D073, "ACN ADVANCED COMMUNICATIONS" }, + { 0x00D074, "TAQUA SYSTEMS, INC." }, + { 0x00D075, "ALARIS MEDICAL SYSTEMS, INC." }, + { 0x00D076, "MERRILL LYNCH & CO., INC." }, + { 0x00D077, "LUCENT TECHNOLOGIES" }, + { 0x00D078, "ELTEX OF SWEDEN AB" }, + { 0x00D079, "CISCO SYSTEMS, INC." }, + { 0x00D07A, "AMAQUEST COMPUTER CORP." }, + { 0x00D07B, "COMCAM INTERNATIONAL LTD." }, + { 0x00D07C, "KOYO ELECTRONICS INC. CO.,LTD." }, + { 0x00D07D, "COSINE COMMUNICATIONS" }, + { 0x00D07E, "KEYCORP LTD." }, + { 0x00D07F, "STRATEGY & TECHNOLOGY, LIMITED" }, + { 0x00D080, "EXABYTE CORPORATION" }, + { 0x00D081, "REAL TIME DEVICES USA, INC." }, + { 0x00D082, "IOWAVE INC." }, + { 0x00D083, "INVERTEX, INC." }, + { 0x00D084, "NEXCOMM SYSTEMS, INC." }, + { 0x00D085, "OTIS ELEVATOR COMPANY" }, + { 0x00D086, "FOVEON, INC." }, + { 0x00D087, "MICROFIRST INC." }, + { 0x00D088, "MAINSAIL NETWORKS, INC." }, + { 0x00D089, "DYNACOLOR, INC." }, + { 0x00D08A, "PHOTRON USA" }, + { 0x00D08B, "ADVA Limited" }, + { 0x00D08C, "GENOA TECHNOLOGY, INC." }, + { 0x00D08D, "PHOENIX GROUP, INC." }, + { 0x00D08E, "NVISION INC." }, + { 0x00D08F, "ARDENT TECHNOLOGIES, INC." }, + { 0x00D090, "CISCO SYSTEMS, INC." }, + { 0x00D091, "SMARTSAN SYSTEMS, INC." }, + { 0x00D092, "GLENAYRE WESTERN MULTIPLEX" }, + { 0x00D093, "TQ - COMPONENTS GMBH" }, + { 0x00D094, "TIMELINE VISTA, INC." }, + { 0x00D095, "XYLAN CORPORATION" }, + { 0x00D096, "3COM EUROPE LTD." }, + { 0x00D097, "CISCO SYSTEMS, INC." }, + { 0x00D098, "IPS AUTOMATION" }, + { 0x00D099, "ELCARD OY" }, + { 0x00D09A, "FILANET CORPORATION" }, + { 0x00D09B, "SPECTEL LTD." }, + { 0x00D09C, "KAPADIA COMMUNICATIONS" }, + { 0x00D09D, "VERIS INDUSTRIES" }, + { 0x00D09E, "2WIRE, INC." }, + { 0x00D09F, "NOVTEK TEST SYSTEMS" }, + { 0x00D0A0, "MIPS DENMARK" }, + { 0x00D0A1, "OSKAR VIERLING GMBH + CO. KG" }, + { 0x00D0A2, "INTEGRATED DEVICE" }, + { 0x00D0A3, "VOCAL DATA, INC." }, + { 0x00D0A4, "ALANTRO COMMUNICATIONS" }, + { 0x00D0A5, "AMERICAN ARIUM" }, + { 0x00D0A6, "LANBIRD TECHNOLOGY CO., LTD." }, + { 0x00D0A7, "TOKYO SOKKI KENKYUJO CO., LTD." }, + { 0x00D0A8, "NETWORK ENGINES, INC." }, + { 0x00D0A9, "SHINANO KENSHI CO., LTD." }, + { 0x00D0AA, "CHASE COMMUNICATIONS" }, + { 0x00D0AB, "DELTAKABEL TELECOM CV" }, + { 0x00D0AC, "GRAYSON WIRELESS" }, + { 0x00D0AD, "TL INDUSTRIES" }, + { 0x00D0AE, "ORESIS COMMUNICATIONS, INC." }, + { 0x00D0AF, "CUTLER-HAMMER, INC." }, + { 0x00D0B0, "BITSWITCH LTD." }, + { 0x00D0B1, "OMEGA ELECTRONICS SA" }, + { 0x00D0B2, "XIOTECH CORPORATION" }, + { 0x00D0B3, "DRS FLIGHT SAFETY AND" }, + { 0x00D0B4, "KATSUJIMA CO., LTD." }, + { 0x00D0B5, "DOTCOM" }, + { 0x00D0B6, "CRESCENT NETWORKS, INC." }, + { 0x00D0B7, "INTEL CORPOTATION" }, + { 0x00D0B8, "IOMEGA CORP." }, + { 0x00D0B9, "MICROTEK INTERNATIONAL, INC." }, + { 0x00D0BA, "CISCO SYSTEMS, INC." }, + { 0x00D0BB, "CISCO SYSTEMS, INC." }, + { 0x00D0BC, "CISCO SYSTEMS, INC." }, + { 0x00D0BD, "SICAN GMBH" }, + { 0x00D0BE, "EMUTEC INC." }, + { 0x00D0BF, "PIVOTAL TECHNOLOGIES" }, + { 0x00D0C0, "CISCO SYSTEMS, INC." }, + { 0x00D0C1, "HARMONIC DATA SYSTEMS, LTD." }, + { 0x00D0C2, "BALTHAZAR TECHNOLOGY AB" }, + { 0x00D0C3, "VIVID TECHNOLOGY PTE, LTD." }, + { 0x00D0C4, "TERATECH CORPORATION" }, + { 0x00D0C5, "COMPUTATIONAL SYSTEMS, INC." }, + { 0x00D0C6, "THOMAS & BETTS CORP." }, + { 0x00D0C7, "PATHWAY, INC." }, + { 0x00D0C8, "I/O CONSULTING A/S" }, + { 0x00D0C9, "ADVANTECH CO., LTD." }, + { 0x00D0CA, "INTRINSYC SOFTWARE INC." }, + { 0x00D0CB, "DASAN CO., LTD." }, + { 0x00D0CC, "TECHNOLOGIES LYRE INC." }, + { 0x00D0CD, "ATAN TECHNOLOGY INC." }, + { 0x00D0CE, "ASYST ELECTRONIC" }, + { 0x00D0CF, "MORETON BAY" }, + { 0x00D0D0, "ZHONGXING TELECOM LTD." }, + { 0x00D0D1, "SIROCCO SYSTEMS, INC." }, + { 0x00D0D2, "EPILOG CORPORATION" }, + { 0x00D0D3, "CISCO SYSTEMS, INC." }, + { 0x00D0D4, "V-BITS, INC." }, + { 0x00D0D5, "GRUNDIG AG" }, + { 0x00D0D6, "AETHRA TELECOMUNICAZIONI" }, + { 0x00D0D7, "B2C2, INC." }, + { 0x00D0D8, "3Com Corporation" }, + { 0x00D0D9, "DEDICATED MICROCOMPUTERS" }, + { 0x00D0DA, "TAICOM DATA SYSTEMS CO., LTD." }, + { 0x00D0DB, "MCQUAY INTERNATIONAL" }, + { 0x00D0DC, "MODULAR MINING SYSTEMS, INC." }, + { 0x00D0DD, "SUNRISE TELECOM, INC." }, + { 0x00D0DE, "PHILIPS MULTIMEDIA NETWORK" }, + { 0x00D0DF, "KUZUMI ELECTRONICS, INC." }, + { 0x00D0E0, "DOOIN ELECTRONICS CO." }, + { 0x00D0E1, "AVIONITEK ISRAEL INC." }, + { 0x00D0E2, "MRT MICRO, INC." }, + { 0x00D0E3, "ELE-CHEM ENGINEERING CO., LTD." }, + { 0x00D0E4, "CISCO SYSTEMS, INC." }, + { 0x00D0E5, "SOLIDUM SYSTEMS CORP." }, + { 0x00D0E6, "IBOND INC." }, + { 0x00D0E7, "VCON TELECOMMUNICATION LTD." }, + { 0x00D0E8, "MAC SYSTEM CO., LTD." }, + { 0x00D0E9, "ADVANTAGE CENTURY" }, + { 0x00D0EA, "NEXTONE COMMUNICATIONS, INC." }, + { 0x00D0EB, "LIGHTERA NETWORKS, INC." }, + { 0x00D0EC, "NAKAYO TELECOMMUNICATIONS, INC" }, + { 0x00D0ED, "XIOX" }, + { 0x00D0EE, "DICTAPHONE CORPORATION" }, + { 0x00D0EF, "IGT" }, + { 0x00D0F0, "CONVISION TECHNOLOGY GMBH" }, + { 0x00D0F1, "SEGA ENTERPRISES, LTD." }, + { 0x00D0F2, "MONTEREY NETWORKS" }, + { 0x00D0F3, "SOLARI DI UDINE SPA" }, + { 0x00D0F4, "CARINTHIAN TECH INSTITUTE" }, + { 0x00D0F5, "ORANGE MICRO, INC." }, + { 0x00D0F6, "NORTHCHURCH COMMUNICATIONS INC" }, + { 0x00D0F7, "NEXT NETS CORPORATION" }, + { 0x00D0F8, "FUJIAN STAR TERMINAL" }, + { 0x00D0F9, "ACUTE COMMUNICATIONS CORP." }, + { 0x00D0FA, "RACAL GUARDATA" }, + { 0x00D0FB, "TEK MICROSYSTEMS, INCORPORATED" }, + { 0x00D0FC, "GRANITE MICROSYSTEMS" }, + { 0x00D0FD, "OPTIMA TELE.COM, INC." }, + { 0x00D0FE, "ASTRAL POINT" }, + { 0x00D0FF, "CISCO SYSTEMS, INC." }, + { 0x00DD00, "UNGERMANN-BASS INC." }, + { 0x00DD01, "UNGERMANN-BASS INC." }, + { 0x00DD02, "UNGERMANN-BASS INC." }, + { 0x00DD03, "UNGERMANN-BASS INC." }, + { 0x00DD04, "UNGERMANN-BASS INC." }, + { 0x00DD05, "UNGERMANN-BASS INC." }, + { 0x00DD06, "UNGERMANN-BASS INC." }, + { 0x00DD07, "UNGERMANN-BASS INC." }, + { 0x00DD08, "UNGERMANN-BASS INC." }, + { 0x00DD09, "UNGERMANN-BASS INC." }, + { 0x00DD0A, "UNGERMANN-BASS INC." }, + { 0x00DD0B, "UNGERMANN-BASS INC." }, + { 0x00DD0C, "UNGERMANN-BASS INC." }, + { 0x00DD0D, "UNGERMANN-BASS INC." }, + { 0x00DD0E, "UNGERMANN-BASS INC." }, + { 0x00DD0F, "UNGERMANN-BASS INC." }, + { 0x00E000, "FUJITSU, LTD" }, + { 0x00E001, "STRAND LIGHTING LIMITED" }, + { 0x00E002, "CROSSROADS SYSTEMS, INC." }, + { 0x00E003, "NOKIA WIRELESS BUSINESS COMMUN" }, + { 0x00E004, "PMC-SIERRA, INC." }, + { 0x00E005, "TECHNICAL CORP." }, + { 0x00E006, "SILICON INTEGRATED SYS. CORP." }, + { 0x00E007, "NETWORK ALCHEMY LTD." }, + { 0x00E008, "AMAZING CONTROLS! INC." }, + { 0x00E009, "MARATHON TECHNOLOGIES CORP." }, + { 0x00E00A, "DIBA, INC." }, + { 0x00E00B, "ROOFTOP COMMUNICATIONS CORP." }, + { 0x00E00C, "MOTOROLA" }, + { 0x00E00D, "RADIANT SYSTEMS" }, + { 0x00E00E, "AVALON IMAGING SYSTEMS, INC." }, + { 0x00E00F, "SHANGHAI BAUD DATA" }, + { 0x00E010, "HESS SB-AUTOMATENBAU GMBH" }, + { 0x00E011, "UNIDEN SAN DIEGO" }, + { 0x00E012, "PLUTO TECHNOLOGIES" }, + { 0x00E013, "EASTERN ELECTRONIC CO., LTD." }, + { 0x00E014, "CISCO SYSTEMS, INC." }, + { 0x00E015, "HEIWA CORPORATION" }, + { 0x00E016, "RAPID CITY COMMUNICATIONS" }, + { 0x00E017, "EXXACT GMBH" }, + { 0x00E018, "ASUSTEK COMPUTER INC." }, + { 0x00E019, "ING. GIORDANO ELETTRONICA" }, + { 0x00E01A, "COMTEC SYSTEMS. CO., LTD." }, + { 0x00E01B, "SPHERE COMMUNICATIONS, INC." }, + { 0x00E01C, "MOBILITY ELECTRONICSY" }, + { 0x00E01D, "WEBTV NETWORKS, INC." }, + { 0x00E01E, "CISCO SYSTEMS, INC." }, + { 0x00E01F, "AVIDIA SYSTEMS, INC." }, + { 0x00E020, "TECNOMEN OY" }, + { 0x00E021, "FREEGATE CORP." }, + { 0x00E022, "MEDIALIGHT INC." }, + { 0x00E023, "TELRAD" }, + { 0x00E024, "GADZOOX NETWORKS" }, + { 0x00E025, "DIT CO., LTD." }, + { 0x00E026, "EASTMAN KODAK CO." }, + { 0x00E027, "DUX, INC." }, + { 0x00E028, "APTIX CORPORATION" }, + { 0x00E029, "STANDARD MICROSYSTEMS CORP." }, + { 0x00E02A, "TANDBERG TELEVISION AS" }, + { 0x00E02B, "EXTREME NETWORKS" }, + { 0x00E02C, "AST COMPUTER" }, + { 0x00E02D, "INNOMEDIALOGIC, INC." }, + { 0x00E02E, "SPC ELECTRONICS CORPORATION" }, + { 0x00E02F, "MCNS HOLDINGS, L.P." }, + { 0x00E030, "MELITA INTERNATIONAL CORP." }, + { 0x00E031, "HAGIWARA ELECTRIC CO., LTD." }, + { 0x00E032, "MISYS FINANCIAL SYSTEMS, LTD." }, + { 0x00E033, "E.E.P.D. GMBH" }, + { 0x00E034, "CISCO SYSTEMS, INC." }, + { 0x00E035, "LOUGHBOROUGH SOUND IMAGES, PLC" }, + { 0x00E036, "PIONEER CORPORATION" }, + { 0x00E037, "CENTURY CORPORATION" }, + { 0x00E038, "PROXIMA CORPORATION" }, + { 0x00E039, "PARADYNE CORP." }, + { 0x00E03A, "CABLETRON SYSTEMS, INC." }, + { 0x00E03B, "PROMINET CORPORATION" }, + { 0x00E03C, "ADVANSYS" }, + { 0x00E03D, "FOCON ELECTRONIC SYSTEMS A/S" }, + { 0x00E03E, "ALFATECH, INC." }, + { 0x00E03F, "JATON CORPORATION" }, + { 0x00E040, "DESKSTATION TECHNOLOGY, INC." }, + { 0x00E041, "CSPI" }, + { 0x00E042, "PACOM DATA LTD." }, + { 0x00E043, "VITALCOM" }, + { 0x00E044, "LSICS CORPORATION" }, + { 0x00E045, "TOUCHWAVE, INC." }, + { 0x00E046, "BENTLY NEVADA CORP." }, + { 0x00E047, "INFOCUS SYSTEMS" }, + { 0x00E048, "SDL COMMUNICATIONS, INC." }, + { 0x00E049, "MICROWI ELECTRONIC GMBH" }, + { 0x00E04A, "ENHANCED MESSAGING SYSTEMS,INC" }, + { 0x00E04B, "JUMP INDUSTRIELLE" }, + { 0x00E04C, "REALTEK SEMICONDUCTOR CORP." }, + { 0x00E04D, "INTERNET INITIATIVE JAPAN, INC" }, + { 0x00E04E, "SANYO DENKI CO., LTD." }, + { 0x00E04F, "CISCO SYSTEMS, INC." }, + { 0x00E050, "EXECUTONE INFORMATION" }, + { 0x00E051, "TALX CORPORATION" }, + { 0x00E052, "FOUNDRY NETWORKS, INC." }, + { 0x00E053, "CELLPORT LABS, INC." }, + { 0x00E054, "KODAI HITEC CO., LTD." }, + { 0x00E055, "INGENIERIA ELECTRONICA" }, + { 0x00E056, "HOLONTECH CORPORATION" }, + { 0x00E057, "HAN MICROTELECOM. CO., LTD." }, + { 0x00E058, "PHASE ONE DENMARK A/S" }, + { 0x00E059, "CONTROLLED ENVIRONMENTS, LTD." }, + { 0x00E05A, "GALEA NETWORK SECURITY" }, + { 0x00E05B, "WEST END SYSTEMS CORP." }, + { 0x00E05C, "MATSUSHITA KOTOBUKI" }, + { 0x00E05D, "UNITEC CO., LTD." }, + { 0x00E05E, "JAPAN AVIATION ELECTRONICS" }, + { 0x00E05F, "E-NET, INC." }, + { 0x00E060, "SHERWOOD" }, + { 0x00E061, "EDGEPOINT NETWORKS, INC." }, + { 0x00E062, "HOST ENGINEERING" }, + { 0x00E063, "CABLETRON - YAGO SYSTEMS, INC." }, + { 0x00E064, "SAMSUNG ELECTRONICS" }, + { 0x00E065, "OPTICAL ACCESS INTERNATIONAL" }, + { 0x00E066, "PROMAX SYSTEMS, INC." }, + { 0x00E067, "EAC AUTOMATION-CONSULTING GMBH" }, + { 0x00E068, "MERRIMAC SYSTEMS INC." }, + { 0x00E069, "JAYCOR NETWORKS, INC." }, + { 0x00E06A, "KAPSCH AG" }, + { 0x00E06B, "W&G SPECIAL PRODUCTS" }, + { 0x00E06C, "BALTIMORE TECHNOLOGIES, LTD." }, + { 0x00E06D, "COMPUWARE CORPORATION" }, + { 0x00E06E, "FAR SYSTEMS SPA" }, + { 0x00E06F, "TERAYON CORP." }, + { 0x00E070, "DH TECHNOLOGY" }, + { 0x00E071, "EPIS MICROCOMPUTER" }, + { 0x00E072, "LYNK" }, + { 0x00E073, "NATIONAL AMUSEMENT" }, + { 0x00E074, "TIERNAN COMMUNICATIONS, INC." }, + { 0x00E075, "ATLAS COMPUTER EQUIPMENT, INC." }, + { 0x00E076, "DEVELOPMENT CONCEPTS, INC." }, + { 0x00E077, "WEBGEAR, INC." }, + { 0x00E078, "BERKELEY NETWORKS" }, + { 0x00E079, "A.T.N.R." }, + { 0x00E07A, "MIKRODIDAKT AB" }, + { 0x00E07B, "BAY NETWORKS" }, + { 0x00E07C, "METTLER-TOLEDO, INC." }, + { 0x00E07D, "NETRONIX, INC." }, + { 0x00E07E, "WALT DISNEY IMAGINEERING" }, + { 0x00E07F, "LOGISTISTEM SRL" }, + { 0x00E080, "CONTROL RESOURCES CORPORATION" }, + { 0x00E081, "TYAN COMPUTER CORP." }, + { 0x00E082, "ANERMA" }, + { 0x00E083, "JATO TECHNOLOGIES, INC." }, + { 0x00E084, "COMPULITE R&D" }, + { 0x00E085, "GLOBAL MAINTECH, INC." }, + { 0x00E086, "CYBEX COMPUTER PRODUCTS" }, + { 0x00E087, "LECROY" }, + { 0x00E088, "LTX CORPORATION" }, + { 0x00E089, "ION Networks, Inc." }, + { 0x00E08A, "GEC AVERY, LTD." }, + { 0x00E08B, "QLOGIC CORP." }, + { 0x00E08C, "NEOPARADIGM LABS, INC." }, + { 0x00E08D, "PRESSURE SYSTEMS, INC." }, + { 0x00E08E, "UTSTARCOM" }, + { 0x00E08F, "CISCO SYSTEMS, INC." }, + { 0x00E090, "BECKMAN LAB. AUTOMATION DIV." }, + { 0x00E091, "LG ELECTRONICS, INC." }, + { 0x00E092, "ADMTEK INCORPORATED" }, + { 0x00E093, "ACKFIN NETWORKS" }, + { 0x00E094, "OSAI SRL" }, + { 0x00E095, "ADVANCED-VISION TECHNOLGIES" }, + { 0x00E096, "SHIMADZU CORPORATION" }, + { 0x00E097, "CARRIER ACCESS CORPORATION" }, + { 0x00E098, "ABOCOM SYSTEMS, INC." }, + { 0x00E099, "SAMSON AG" }, + { 0x00E09A, "POSITRON INDUSTRIES, INC." }, + { 0x00E09B, "ENGAGE NETWORKS, INC." }, + { 0x00E09C, "MII" }, + { 0x00E09D, "SARNOFF CORPORATION" }, + { 0x00E09E, "QUANTUM CORPORATION" }, + { 0x00E09F, "PIXEL VISION" }, + { 0x00E0A0, "WILTRON CO." }, + { 0x00E0A1, "HIMA PAUL HILDEBRANDT" }, + { 0x00E0A2, "MICROSLATE INC." }, + { 0x00E0A3, "CISCO SYSTEMS, INC." }, + { 0x00E0A4, "ESAOTE S.P.A." }, + { 0x00E0A5, "COMCORE SEMICONDUCTOR, INC." }, + { 0x00E0A6, "TELOGY NETWORKS, INC." }, + { 0x00E0A7, "IPC INFORMATION SYSTEMS, INC." }, + { 0x00E0A8, "SAT GMBH&CO" }, + { 0x00E0A9, "FUNAI ELECTRIC CO., LTD." }, + { 0x00E0AA, "ELECTROSONIC LTD." }, + { 0x00E0AB, "DIMAT S.A." }, + { 0x00E0AC, "MIDSCO, INC." }, + { 0x00E0AD, "EES TECHNOLOGY, LTD." }, + { 0x00E0AE, "XAQTI CORPORATION" }, + { 0x00E0AF, "GENERAL DYNAMICS INFORMATION" }, + { 0x00E0B0, "CISCO SYSTEMS, INC." }, + { 0x00E0B1, "PACKET ENGINES, INC." }, + { 0x00E0B2, "TELMAX COMMUNICATIONS CORP." }, + { 0x00E0B3, "ETHERWAN SYSTEMS, INC." }, + { 0x00E0B4, "TECHNO SCOPE CO., LTD." }, + { 0x00E0B5, "ARDENT COMMUNICATIONS CORP." }, + { 0x00E0B6, "Entrada Networks" }, + { 0x00E0B7, "PI GROUP, LTD." }, + { 0x00E0B8, "GATEWAY 2000" }, + { 0x00E0B9, "BYAS SYSTEMS" }, + { 0x00E0BA, "BERGHOF AUTOMATIONSTECHNIK" }, + { 0x00E0BB, "NBX CORPORATION" }, + { 0x00E0BC, "SYMON COMMUNICATIONS, INC." }, + { 0x00E0BD, "INTERFACE SYSTEMS, INC." }, + { 0x00E0BE, "GENROCO INTERNATIONAL, INC." }, + { 0x00E0BF, "TORRENT NETWORKING" }, + { 0x00E0C0, "SEIWA ERECTRIC MFG. CO., LTD." }, + { 0x00E0C1, "MEMOREX TELEX JAPAN, LTD." }, + { 0x00E0C2, "NECSY SPA" }, + { 0x00E0C3, "SAKAI SYSTEM DEVELOPMENT CORP." }, + { 0x00E0C4, "HORNER ELECTRIC, INC." }, + { 0x00E0C5, "BCOM ELECTRONICS INC." }, + { 0x00E0C6, "LINK2IT, L.L.C." }, + { 0x00E0C7, "EUROTECH SRL" }, + { 0x00E0C8, "VIRTUAL ACCESS, LTD." }, + { 0x00E0C9, "AUTOMATEDLOGIC CORPORATION" }, + { 0x00E0CA, "BEST DATA PRODUCTS" }, + { 0x00E0CB, "RESON, INC." }, + { 0x00E0CC, "HERO SYSTEMS, LTD." }, + { 0x00E0CD, "SENSIS CORPORATION" }, + { 0x00E0CE, "ARN" }, + { 0x00E0CF, "INTEGRATED DEVICE" }, + { 0x00E0D0, "NETSPEED, INC." }, + { 0x00E0D1, "TELSIS LIMITED" }, + { 0x00E0D2, "VERSANET COMMUNICATIONS, INC." }, + { 0x00E0D3, "DATENTECHNIK GMBH" }, + { 0x00E0D4, "EXCELLENT COMPUTER" }, + { 0x00E0D5, "ARCXEL TECHNOLOGIES, INC." }, + { 0x00E0D6, "COMPUTER & COMMUNICATION" }, + { 0x00E0D7, "SUNSHINE ELECTRONICS, INC." }, + { 0x00E0D8, "LANBIT COMPUTER, INC." }, + { 0x00E0D9, "TAZMO CO., LTD." }, + { 0x00E0DA, "ASSURED ACCESS" }, + { 0x00E0DB, "VIAVIDEO COMMUNICATIONS" }, + { 0x00E0DC, "NEXWARE CORP." }, + { 0x00E0DD, "ZENITH ELECTRONICS CORPORATION" }, + { 0x00E0DE, "DATAX NV" }, + { 0x00E0DF, "KE KOMMUNIKATIONS-ELECTRONIK" }, + { 0x00E0E0, "SI ELECTRONICS, LTD." }, + { 0x00E0E1, "G2 NETWORKS, ILNC." }, + { 0x00E0E2, "INNOVA CORP." }, + { 0x00E0E3, "SK-ELEKTRONIK GMBH" }, + { 0x00E0E4, "FANUC ROBOTICS NORTH AMERICA," }, + { 0x00E0E5, "CINCO NETWORKS, INC." }, + { 0x00E0E6, "INCAA DATACOM B.V." }, + { 0x00E0E7, "RAYTHEON E-SYSTEMS, INC." }, + { 0x00E0E8, "GRETACODER DATA SYSTEMS AG" }, + { 0x00E0E9, "DATA LABS, INC." }, + { 0x00E0EA, "INNOVAT COMMUNICATIONS, INC." }, + { 0x00E0EB, "DIGICOM SYSTEMS, INCORPORATED" }, + { 0x00E0EC, "CELESTICA INC." }, + { 0x00E0ED, "SILICOM, LTD." }, + { 0x00E0EE, "MAREL HF" }, + { 0x00E0EF, "DIONEX" }, + { 0x00E0F0, "ABLER TECHNOLOGY, INC." }, + { 0x00E0F1, "THAT CORPORATION" }, + { 0x00E0F2, "ARLOTTO COMNET, INC." }, + { 0x00E0F3, "WEBSPRINT COMMUNICATIONS, INC." }, + { 0x00E0F4, "INSIDE TECHNOLOGY A/S" }, + { 0x00E0F5, "TELES AG" }, + { 0x00E0F6, "DECISION EUROPE" }, + { 0x00E0F7, "CISCO SYSTEMS, INC." }, + { 0x00E0F8, "DIANA CONTROL AB" }, + { 0x00E0F9, "CISCO SYSTEMS, INC." }, + { 0x00E0FA, "TRL TECHNOLOGY, LTD." }, + { 0x00E0FB, "LEIGHTRONIX, INC." }, + { 0x00E0FC, "HUAWEI TECHNOLOGIES CO., LTD." }, + { 0x00E0FD, "A-TREND TECHNOLOGY CO., LTD." }, + { 0x00E0FE, "CISCO SYSTEMS, INC." }, + { 0x00E0FF, "SECURITY DYNAMICS TECHNOLOGIES" }, + { 0x00E6D3, "NIXDORF COMPUTER CORP." }, + { 0x020701, "RACAL-DATACOM" }, + { 0x021C7C, "PERQ SYSTEMS CORPORATION" }, + { 0x026086, "LOGIC REPLACEMENT TECH. LTD." }, + { 0x02608C, "3COM CORPORATION" }, + { 0x027001, "RACAL-DATACOM" }, + { 0x0270B0, "M/A-COM INC. COMPANIES" }, + { 0x0270B3, "DATA RECALL LTD" }, + { 0x029D8E, "CARDIAC RECORDERS INC." }, + { 0x02AA3C, "OLIVETTI TELECOMM SPA (OLTECO)" }, + { 0x02BB01, "OCTOTHORPE CORP." }, + { 0x02C08C, "3COM CORPORATION" }, + { 0x02CF1C, "COMMUNICATION MACHINERY CORP." }, + { 0x02E6D3, "NIXDORF COMPUTER CORPORATION" }, + { 0x040AE0, "XMIT AG COMPUTER NETWORKS" }, + { 0x04E0C4, "TRIUMPH-ADLER AG" }, + { 0x080001, "COMPUTERVISION CORPORATION" }, + { 0x080002, "BRIDGE COMMUNICATIONS INC." }, + { 0x080003, "ADVANCED COMPUTER COMM." }, + { 0x080004, "CROMEMCO INCORPORATED" }, + { 0x080005, "SYMBOLICS INC." }, + { 0x080006, "SIEMENS AG" }, + { 0x080007, "APPLE COMPUTER INC." }, + { 0x080008, "BOLT BERANEK AND NEWMAN INC." }, + { 0x080009, "HEWLETT PACKARD" }, + { 0x08000A, "NESTAR SYSTEMS INCORPORATED" }, + { 0x08000B, "UNISYS CORPORATION" }, + { 0x08000C, "MIKLYN DEVELOPMENT CO." }, + { 0x08000D, "INTERNATIONAL COMPUTERS LTD." }, + { 0x08000E, "NCR CORPORATION" }, + { 0x08000F, "MITEL CORPORATION" }, + { 0x080011, "TEKTRONIX INC." }, + { 0x080012, "BELL ATLANTIC INTEGRATED SYST." }, + { 0x080013, "EXXON" }, + { 0x080014, "EXCELAN" }, + { 0x080015, "STC BUSINESS SYSTEMS" }, + { 0x080016, "BARRISTER INFO SYS CORP" }, + { 0x080017, "NATIONAL SEMICONDUCTOR" }, + { 0x080018, "PIRELLI FOCOM NETWORKS" }, + { 0x080019, "GENERAL ELECTRIC CORPORATION" }, + { 0x08001A, "TIARA/ 10NET" }, + { 0x08001B, "DATA GENERAL" }, + { 0x08001C, "KDD-KOKUSAI DEBNSIN DENWA CO." }, + { 0x08001D, "ABLE COMMUNICATIONS INC." }, + { 0x08001E, "APOLLO COMPUTER INC." }, + { 0x08001F, "SHARP CORPORATION" }, + { 0x080020, "SUN MICROSYSTEMS INC." }, + { 0x080021, "3M COMPANY" }, + { 0x080022, "NBI INC." }, + { 0x080023, "MATSUHITA GRAPHIC COMM SYS INC" }, + { 0x080024, "10NET COMMUNICATIONS/DCA" }, + { 0x080025, "CONTROL DATA" }, + { 0x080026, "NORSK DATA A.S." }, + { 0x080027, "CADMUS COMPUTER SYSTEMS" }, + { 0x080028, "TEXAS INSTRUMENTS" }, + { 0x080029, "MEGATEK CORPORATION" }, + { 0x08002A, "MOSAIC TECHNOLOGIES INC." }, + { 0x08002B, "DIGITAL EQUIPMENT CORPORATION" }, + { 0x08002C, "BRITTON LEE INC." }, + { 0x08002D, "LAN-TEC INC." }, + { 0x08002E, "METAPHOR COMPUTER SYSTEMS" }, + { 0x08002F, "PRIME COMPUTER INC." }, + { 0x080030, "NETWORK RESEARCH CORPORATION" }, + { 0x080030, "CERN" }, + { 0x080030, "ROYAL MELBOURNE INST OF TECH" }, + { 0x080031, "LITTLE MACHINES INC." }, + { 0x080032, "TIGAN INCORPORATED" }, + { 0x080033, "BAUSCH & LOMB" }, + { 0x080034, "FILENET CORPORATION" }, + { 0x080035, "MICROFIVE CORPORATION" }, + { 0x080036, "INTERGRAPH CORPORATION" }, + { 0x080037, "FUJI-XEROX CO. LTD." }, + { 0x080038, "CII HONEYWELL BULL" }, + { 0x080039, "SPIDER SYSTEMS LIMITED" }, + { 0x08003A, "ORCATECH INC." }, + { 0x08003B, "TORUS SYSTEMS LIMITED" }, + { 0x08003C, "SCHLUMBERGER WELL SERVICES" }, + { 0x08003D, "CADNETIX CORPORATIONS" }, + { 0x08003E, "CODEX CORPORATION" }, + { 0x08003F, "FRED KOSCHARA ENTERPRISES" }, + { 0x080040, "FERRANTI COMPUTER SYS. LIMITED" }, + { 0x080041, "RACAL-MILGO INFORMATION SYS.." }, + { 0x080042, "JAPAN MACNICS CORP." }, + { 0x080043, "PIXEL COMPUTER INC." }, + { 0x080044, "DAVID SYSTEMS INC." }, + { 0x080045, "CONCURRENT COMPUTER CORP." }, + { 0x080046, "SONY CORPORATION LTD." }, + { 0x080047, "SEQUENT COMPUTER SYSTEMS INC." }, + { 0x080048, "EUROTHERM GAUGING SYSTEMS" }, + { 0x080049, "UNIVATION" }, + { 0x08004A, "BANYAN SYSTEMS INC." }, + { 0x08004B, "PLANNING RESEARCH CORP." }, + { 0x08004C, "HYDRA COMPUTER SYSTEMS INC." }, + { 0x08004D, "CORVUS SYSTEMS INC." }, + { 0x08004E, "3COM EUROPE LTD." }, + { 0x08004F, "CYGNET SYSTEMS" }, + { 0x080050, "DAISY SYSTEMS CORP." }, + { 0x080051, "EXPERDATA" }, + { 0x080052, "INSYSTEC" }, + { 0x080053, "MIDDLE EAST TECH. UNIVERSITY" }, + { 0x080055, "STANFORD TELECOMM. INC." }, + { 0x080056, "STANFORD LINEAR ACCEL. CENTER" }, + { 0x080057, "EVANS & SUTHERLAND" }, + { 0x080058, "SYSTEMS CONCEPTS" }, + { 0x080059, "A/S MYCRON" }, + { 0x08005A, "IBM CORPORATION" }, + { 0x08005B, "VTA TECHNOLOGIES INC." }, + { 0x08005C, "FOUR PHASE SYSTEMS" }, + { 0x08005D, "GOULD INC." }, + { 0x08005E, "COUNTERPOINT COMPUTER INC." }, + { 0x08005F, "SABER TECHNOLOGY CORP." }, + { 0x080060, "INDUSTRIAL NETWORKING INC." }, + { 0x080061, "JAROGATE LTD." }, + { 0x080062, "GENERAL DYNAMICS" }, + { 0x080063, "PLESSEY" }, + { 0x080064, "AUTOPHON AG" }, + { 0x080065, "GENRAD INC." }, + { 0x080066, "AGFA CORPORATION" }, + { 0x080067, "COMDESIGN" }, + { 0x080068, "RIDGE COMPUTERS" }, + { 0x080069, "SILICON GRAPHICS INC." }, + { 0x08006A, "ATT BELL LABORATORIES" }, + { 0x08006B, "ACCEL TECHNOLOGIES INC." }, + { 0x08006C, "SUNTEK TECHNOLOGY INT'L" }, + { 0x08006D, "WHITECHAPEL COMPUTER WORKS" }, + { 0x08006E, "MASSCOMP" }, + { 0x08006F, "PHILIPS APELDOORN B.V." }, + { 0x080070, "MITSUBISHI ELECTRIC CORP." }, + { 0x080071, "MATRA (DSIE)" }, + { 0x080072, "XEROX CORP UNIV GRANT PROGRAM" }, + { 0x080073, "TECMAR INC." }, + { 0x080074, "CASIO COMPUTER CO. LTD." }, + { 0x080075, "DANSK DATA ELECTRONIK" }, + { 0x080076, "PC LAN TECHNOLOGIES" }, + { 0x080077, "TSL COMMUNICATIONS LTD." }, + { 0x080078, "ACCELL CORPORATION" }, + { 0x080079, "THE DROID WORKS" }, + { 0x08007A, "INDATA" }, + { 0x08007B, "SANYO ELECTRIC CO. LTD." }, + { 0x08007C, "VITALINK COMMUNICATIONS CORP." }, + { 0x08007E, "AMALGAMATED WIRELESS(AUS) LTD" }, + { 0x08007F, "CARNEGIE-MELLON UNIVERSITY" }, + { 0x080080, "AES DATA INC." }, + { 0x080081, ",ASTECH INC." }, + { 0x080082, "VERITAS SOFTWARE" }, + { 0x080083, "SEIKO INSTRUM. AND ELECTRONICS" }, + { 0x080084, "TOMEN ELECTRONICS CORP." }, + { 0x080085, "ELXSI" }, + { 0x080086, "IMAGEN CORPORATION" }, + { 0x080087, "XYPLEX" }, + { 0x080088, "MCDATA CORPORATION" }, + { 0x080089, "KINETICS" }, + { 0x08008A, "PERFORMANCE TECHNOLOGY" }, + { 0x08008B, "PYRAMID TECHNOLOGY CORP." }, + { 0x08008C, "NETWORK RESEARCH CORPORATION" }, + { 0x08008D, "XYVISION INC." }, + { 0x08008E, "TANDEM COMPUTERS" }, + { 0x08008F, "CHIPCOM CORPORATION" }, + { 0x080090, "SONOMA SYSTEMS" }, + { 0x08BBCC, "AK-NORD EDV VERTRIEBSGES. MBH" }, + { 0x10005A, "IBM CORPORATION" }, + { 0x1000E8, "NATIONAL SEMICONDUCTOR" }, + { 0x800010, "ATT BELL LABORATORIES" }, + { 0xA06A00, "Verilink Corporation" }, + { 0xAA0000, "DIGITAL EQUIPMENT CORPORATION" }, + { 0xAA0001, "DIGITAL EQUIPMENT CORPORATION" }, + { 0xAA0002, "DIGITAL EQUIPMENT CORPORATION" }, + { 0xAA0003, "DIGITAL EQUIPMENT CORPORATION" }, + { 0xAA0004, "DIGITAL EQUIPMENT CORPORATION" }, +}; + +#endif /* CONFIG_IEEE1394_OUI_DB */ diff -urN linux-2.4.21-rc1.orig/drivers/sound/Config.in linux/drivers/sound/Config.in --- linux-2.4.21-rc1.orig/drivers/sound/Config.in 2003-04-29 04:47:21.000000000 -0600 +++ linux/drivers/sound/Config.in 2003-04-29 04:50:47.000000000 -0600 @@ -6,6 +6,10 @@ # Prompt user for primary drivers. +dep_bool ' Old non-ALSA PCI sound drivers' CONFIG_SOUND_OLD_PCI_DRIVERS $CONFIG_SOUND + +if [ "$CONFIG_SOUND_OLD_PCI_DRIVERS" = "y" ]; then + dep_tristate ' ALi5455 audio support' CONFIG_SOUND_ALI5455 $CONFIG_SOUND $CONFIG_PCI dep_tristate ' BT878 audio dma' CONFIG_SOUND_BT878 $CONFIG_SOUND $CONFIG_PCI dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI @@ -125,6 +129,8 @@ dep_tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_PCI dep_mbool ' VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_VIA82CXXX +fi + dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then diff -urN linux-2.4.21-rc1.orig/drivers/sound/Makefile linux/drivers/sound/Makefile --- linux-2.4.21-rc1.orig/drivers/sound/Makefile 2003-04-29 04:47:21.000000000 -0600 +++ linux/drivers/sound/Makefile 2003-04-29 23:28:18.000000000 -0600 @@ -14,7 +14,9 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_SOUND) += soundcore.o +ifeq ($(CONFIG_SND),n) + obj-$(CONFIG_SOUND) += soundcore.o +endif obj-$(CONFIG_SOUND_OSS) += sound.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o diff -urN linux-2.4.21-rc1.orig/include/sound/ac97_codec.h linux/include/sound/ac97_codec.h --- linux-2.4.21-rc1.orig/include/sound/ac97_codec.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ac97_codec.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,332 @@ +#ifndef __SOUND_AC97_CODEC_H +#define __SOUND_AC97_CODEC_H + +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "control.h" +#include "info.h" + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_RESERVED 0x24 /* Reserved */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ +#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ +#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ +#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ +#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ +#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ +#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ +#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ +#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ +#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ +#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ +#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ +#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ +#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ + +/* basic capabilities (reset register) */ +#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ +#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ +#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ +#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ +#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ +#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ +#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ +#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ +#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ +#define AC97_BC_DAC_MASK 0x00c0 +#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ +#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ +#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ +#define AC97_BC_ADC_MASK 0x0300 + +/* extended audio ID bit defines */ +#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ +#define AC97_EI_DRA 0x0002 /* Double rate supported */ +#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ +#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ +#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ +#define AC97_EI_DACS_SLOT_SHIFT 4 +#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ +#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ +#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ +#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ +#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ +#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ +#define AC97_EI_REV_SHIFT 10 +#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_EI_ADDR_SHIFT 14 + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_SLOT_SHIFT 4 +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_CC_SHIFT 4 +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_SHIFT 12 +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* extended modem ID bit defines */ +#define AC97_MEI_LINE1 0x0001 /* Line1 present */ +#define AC97_MEI_LINE2 0x0002 /* Line2 present */ +#define AC97_MEI_HANDSET 0x0004 /* Handset present */ +#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ +#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ +#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_MEI_ADDR_SHIFT 14 + +/* extended modem status and control bit defines */ +#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ +#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ +#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ +#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ +#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ +#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ +#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ +#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ +#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ +#define AC97_MEA_PRB 0x0200 /* reserved */ +#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ +#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ +#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ +#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ +#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ +#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ + +/* specific - SigmaTel */ +#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ +#define AC97_SIGMATEL_DAC2INVERT 0x6e +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 + +/* specific - Analog Devices */ +#define AC97_AD_TEST 0x5a /* test register */ +#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */ +#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */ +#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ +#define AC97_AD_MISC 0x76 /* Misc Control Bits */ + +/* specific - Cirrus Logic */ +#define AC97_CSR_ACMODE 0x5e /* AC Mode Register */ +#define AC97_CSR_MISC_CRYSTAL 0x60 /* Misc Crystal Control */ +#define AC97_CSR_SPDIF 0x68 /* S/PDIF Register */ +#define AC97_CSR_SERIAL 0x6a /* Serial Port Control */ +#define AC97_CSR_SPECF_ADDR 0x6c /* Special Feature Address */ +#define AC97_CSR_SPECF_DATA 0x6e /* Special Feature Data */ +#define AC97_CSR_BDI_STATUS 0x7a /* BDI Status */ + +/* specific - Conexant */ +#define AC97_CXR_AUDIO_MISC 0x5c +#define AC97_CXR_SPDIFEN (1<<3) +#define AC97_CXR_COPYRGT (1<<2) +#define AC97_CXR_SPDIF_MASK (3<<0) +#define AC97_CXR_SPDIF_PCM 0x0 +#define AC97_CXR_SPDIF_AC3 0x2 + +/* specific - ALC */ +#define AC97_ALC650_SURR_DAC_VOL 0x64 +#define AC97_ALC650_LFE_DAC_VOL 0x66 +#define AC97_ALC650_MULTICH 0x6a +#define AC97_ALC650_CLOCK 0x7a + +/* specific - Yamaha YMF753 */ +#define AC97_YMF753_DIT_CTRL2 0x66 /* DIT Control 2 */ +#define AC97_YMF753_3D_MODE_SEL 0x68 /* 3D Mode Select */ + +/* ac97->scaps */ +#define AC97_SCAP_AUDIO (1<<0) /* audio AC'97 codec */ +#define AC97_SCAP_MODEM (1<<1) /* modem AC'97 codec */ +#define AC97_SCAP_SURROUND_DAC (1<<2) /* surround L&R DACs are present */ +#define AC97_SCAP_CENTER_LFE_DAC (1<<3) /* center and LFE DACs are present */ + +/* ac97->flags */ +#define AC97_HAS_PC_BEEP (1<<0) /* force PC Speaker usage */ +#define AC97_AD_MULTI (1<<1) /* Analog Devices - multi codecs */ +#define AC97_CS_SPDIF (1<<2) /* Cirrus Logic uses funky SPDIF */ +#define AC97_CX_SPDIF (1<<3) /* Conexant's spdif interface */ + +/* rates indexes */ +#define AC97_RATES_FRONT_DAC 0 +#define AC97_RATES_SURR_DAC 1 +#define AC97_RATES_LFE_DAC 2 +#define AC97_RATES_ADC 3 +#define AC97_RATES_MIC_ADC 4 +#define AC97_RATES_SPDIF 5 + +/* + * + */ + +typedef struct _snd_ac97 ac97_t; + +struct _snd_ac97 { + void (*reset) (ac97_t *ac97); + void (*write) (ac97_t *ac97, unsigned short reg, unsigned short val); + unsigned short (*read) (ac97_t *ac97, unsigned short reg); + void (*wait) (ac97_t *ac97); + void (*init) (ac97_t *ac97); + void *private_data; + void (*private_free) (ac97_t *ac97); + /* --- */ + snd_card_t *card; + spinlock_t reg_lock; + unsigned short num; /* number of codec: 0 = primary, 1 = secondary */ + unsigned short addr; /* physical address of codec [0-3] */ + unsigned int id; /* identification of codec */ + unsigned short caps; /* capabilities (register 0) */ + unsigned short ext_id; /* extended feature identification (register 28) */ + unsigned short ext_mid; /* extended modem ID (register 3C) */ + unsigned int scaps; /* driver capabilities */ + unsigned int flags; /* specific code */ + unsigned int clock; /* AC'97 clock (usually 48000Hz) */ + unsigned int rates[6]; /* see AC97_RATES_* defines */ + unsigned int spdif_status; + unsigned short regs[0x80]; /* register cache */ + unsigned int limited_regs; /* allow limited registers only */ + DECLARE_BITMAP(reg_accessed, 0x80); /* bit flags */ + union { /* vendor specific code */ + struct { + unsigned short unchained[3]; // 0 = C34, 1 = C79, 2 = C69 + unsigned short chained[3]; // 0 = C34, 1 = C79, 2 = C69 + unsigned short id[3]; // codec IDs (lower 16-bit word) + unsigned short pcmreg[3]; // PCM registers + struct semaphore mutex; + } ad18xx; + } spec; +}; + +/* conditions */ +static inline int ac97_is_audio(ac97_t * ac97) +{ + return (ac97->scaps & AC97_SCAP_AUDIO); +} +static inline int ac97_is_modem(ac97_t * ac97) +{ + return (ac97->scaps & AC97_SCAP_MODEM); +} +static inline int ac97_is_rev22(ac97_t * ac97) +{ + return (ac97->ext_id & AC97_EI_REV_MASK) == AC97_EI_REV_22; +} +static inline int ac97_can_amap(ac97_t * ac97) +{ + return (ac97->ext_id & AC97_EI_AMAP) != 0; +} + +/* functions */ +int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97); /* create mixer controls */ +int snd_ac97_modem(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97); /* create modem controls */ + +void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value); +unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value); +int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value); +int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value); +int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate); +#ifdef CONFIG_PM +void snd_ac97_suspend(ac97_t *ac97); +void snd_ac97_resume(ac97_t *ac97); +#endif + +enum { AC97_TUNE_HP_ONLY, AC97_TUNE_SWAP_HP }; + +struct ac97_quirk { + unsigned short vendor; + unsigned short device; + const char *name; + int type; +}; + +int snd_ac97_tune_hardware(ac97_t *ac97, struct pci_dev *pci, struct ac97_quirk *quirk); + +#endif /* __SOUND_AC97_CODEC_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ad1816a.h linux/include/sound/ad1816a.h --- linux-2.4.21-rc1.orig/include/sound/ad1816a.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ad1816a.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,174 @@ +#ifndef __SOUND_AD1816A_H +#define __SOUND_AD1816A_H + +/* + ad1816a.h - definitions for ADI SoundPort AD1816A chip. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "control.h" +#include "pcm.h" +#include "timer.h" + +#define AD1816A_REG(r) (chip->port + r) + +#define AD1816A_CHIP_STATUS 0x00 +#define AD1816A_INDIR_ADDR 0x00 +#define AD1816A_INTERRUPT_STATUS 0x01 +#define AD1816A_INDIR_DATA_LOW 0x02 +#define AD1816A_INDIR_DATA_HIGH 0x03 +#define AD1816A_PIO_DEBUG 0x04 +#define AD1816A_PIO_STATUS 0x05 +#define AD1816A_PIO_DATA 0x06 +#define AD1816A_RESERVED_7 0x07 +#define AD1816A_PLAYBACK_CONFIG 0x08 +#define AD1816A_CAPTURE_CONFIG 0x09 +#define AD1816A_RESERVED_10 0x0a +#define AD1816A_RESERVED_11 0x0b +#define AD1816A_JOYSTICK_RAW_DATA 0x0c +#define AD1816A_JOYSTICK_CTRL 0x0d +#define AD1816A_JOY_POS_DATA_LOW 0x0e +#define AD1816A_JOY_POS_DATA_HIGH 0x0f + +#define AD1816A_LOW_BYTE_TMP 0x00 +#define AD1816A_INTERRUPT_ENABLE 0x01 +#define AD1816A_EXTERNAL_CTRL 0x01 +#define AD1816A_PLAYBACK_SAMPLE_RATE 0x02 +#define AD1816A_CAPTURE_SAMPLE_RATE 0x03 +#define AD1816A_VOICE_ATT 0x04 +#define AD1816A_FM_ATT 0x05 +#define AD1816A_I2S_1_ATT 0x06 +#define AD1816A_I2S_0_ATT 0x07 +#define AD1816A_PLAYBACK_BASE_COUNT 0x08 +#define AD1816A_PLAYBACK_CURR_COUNT 0x09 +#define AD1816A_CAPTURE_BASE_COUNT 0x0a +#define AD1816A_CAPTURE_CURR_COUNT 0x0b +#define AD1816A_TIMER_BASE_COUNT 0x0c +#define AD1816A_TIMER_CURR_COUNT 0x0d +#define AD1816A_MASTER_ATT 0x0e +#define AD1816A_CD_GAIN_ATT 0x0f +#define AD1816A_SYNTH_GAIN_ATT 0x10 +#define AD1816A_VID_GAIN_ATT 0x11 +#define AD1816A_LINE_GAIN_ATT 0x12 +#define AD1816A_MIC_GAIN_ATT 0x13 +#define AD1816A_PHONE_IN_GAIN_ATT 0x13 +#define AD1816A_ADC_SOURCE_SEL 0x14 +#define AD1816A_ADC_PGA 0x14 +#define AD1816A_CHIP_CONFIG 0x20 +#define AD1816A_DSP_CONFIG 0x21 +#define AD1816A_FM_SAMPLE_RATE 0x22 +#define AD1816A_I2S_1_SAMPLE_RATE 0x23 +#define AD1816A_I2S_0_SAMPLE_RATE 0x24 +#define AD1816A_RESERVED_37 0x25 +#define AD1816A_PROGRAM_CLOCK_RATE 0x26 +#define AD1816A_3D_PHAT_CTRL 0x27 +#define AD1816A_PHONE_OUT_ATT 0x27 +#define AD1816A_RESERVED_40 0x28 +#define AD1816A_HW_VOL_BUT 0x29 +#define AD1816A_DSP_MAILBOX_0 0x2a +#define AD1816A_DSP_MAILBOX_1 0x2b +#define AD1816A_POWERDOWN_CTRL 0x2c +#define AD1816A_TIMER_CTRL 0x2c +#define AD1816A_VERSION_ID 0x2d +#define AD1816A_RESERVED_46 0x2e + +#define AD1816A_READY 0x80 + +#define AD1816A_PLAYBACK_IRQ_PENDING 0x80 +#define AD1816A_CAPTURE_IRQ_PENDING 0x40 +#define AD1816A_TIMER_IRQ_PENDING 0x20 + +#define AD1816A_PLAYBACK_ENABLE 0x01 +#define AD1816A_PLAYBACK_PIO 0x02 +#define AD1816A_CAPTURE_ENABLE 0x01 +#define AD1816A_CAPTURE_PIO 0x02 + +#define AD1816A_FMT_LINEAR_8 0x00 +#define AD1816A_FMT_ULAW_8 0x08 +#define AD1816A_FMT_LINEAR_16_LIT 0x10 +#define AD1816A_FMT_ALAW_8 0x18 +#define AD1816A_FMT_LINEAR_16_BIG 0x30 +#define AD1816A_FMT_ALL 0x38 +#define AD1816A_FMT_STEREO 0x04 + +#define AD1816A_PLAYBACK_IRQ_ENABLE 0x8000 +#define AD1816A_CAPTURE_IRQ_ENABLE 0x4000 +#define AD1816A_TIMER_IRQ_ENABLE 0x2000 +#define AD1816A_TIMER_ENABLE 0x0080 + +#define AD1816A_SRC_LINE 0x00 +#define AD1816A_SRC_OUT 0x10 +#define AD1816A_SRC_CD 0x20 +#define AD1816A_SRC_SYNTH 0x30 +#define AD1816A_SRC_VIDEO 0x40 +#define AD1816A_SRC_MIC 0x50 +#define AD1816A_SRC_MONO 0x50 +#define AD1816A_SRC_PHONE_IN 0x60 +#define AD1816A_SRC_MASK 0x70 + +#define AD1816A_CAPTURE_NOT_EQUAL 0x1000 +#define AD1816A_WSS_ENABLE 0x8000 + +typedef struct _snd_ad1816a ad1816a_t; + +struct _snd_ad1816a { + unsigned long port; + struct resource *res_port; + int irq; + int dma1; + int dma2; + + unsigned short hardware; + unsigned short version; + + spinlock_t lock; + + unsigned short mode; + + snd_card_t *card; + snd_pcm_t *pcm; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; + + snd_timer_t *timer; +}; + + +#define AD1816A_HW_AUTO 0 +#define AD1816A_HW_AD1816A 1 +#define AD1816A_HW_AD1815 2 +#define AD1816A_HW_AD18MAX10 3 + +#define AD1816A_MODE_PLAYBACK 0x01 +#define AD1816A_MODE_CAPTURE 0x02 +#define AD1816A_MODE_TIMER 0x04 +#define AD1816A_MODE_OPEN (AD1816A_MODE_PLAYBACK | \ + AD1816A_MODE_CAPTURE | \ + AD1816A_MODE_TIMER) + + +extern int snd_ad1816a_create(snd_card_t *card, unsigned long port, + int irq, int dma1, int dma2, + ad1816a_t **chip); + +extern int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm); +extern int snd_ad1816a_mixer(ad1816a_t *chip); + +#endif /* __SOUND_AD1816A_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ad1848.h linux/include/sound/ad1848.h --- linux-2.4.21-rc1.orig/include/sound/ad1848.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ad1848.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,207 @@ +#ifndef __SOUND_AD1848_H +#define __SOUND_AD1848_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for AD1847/AD1848/CS4248 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" + +/* IO ports */ + +#define AD1848P( codec, x ) ( (chip) -> port + c_d_c_AD1848##x ) + +#define c_d_c_AD1848REGSEL 0 +#define c_d_c_AD1848REG 1 +#define c_d_c_AD1848STATUS 2 +#define c_d_c_AD1848PIO 3 + +/* codec registers */ + +#define AD1848_LEFT_INPUT 0x00 /* left input control */ +#define AD1848_RIGHT_INPUT 0x01 /* right input control */ +#define AD1848_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define AD1848_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define AD1848_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define AD1848_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define AD1848_LEFT_OUTPUT 0x06 /* left output control register */ +#define AD1848_RIGHT_OUTPUT 0x07 /* right output control register */ +#define AD1848_DATA_FORMAT 0x08 /* clock and data format - playback/capture - bits 7-0 MCE */ +#define AD1848_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define AD1848_PIN_CTRL 0x0a /* pin control */ +#define AD1848_TEST_INIT 0x0b /* test and initialization */ +#define AD1848_MISC_INFO 0x0c /* miscellaneaous information */ +#define AD1848_LOOPBACK 0x0d /* loopback control */ +#define AD1848_DATA_UPR_CNT 0x0e /* playback/capture upper base count */ +#define AD1848_DATA_LWR_CNT 0x0f /* playback/capture lower base count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define AD1848_INIT 0x80 /* CODEC is initializing */ +#define AD1848_MCE 0x40 /* mode change enable */ +#define AD1848_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define AD1848_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for AD1848_LEFT_INPUT and AD1848_RIGHT_INPUT registers */ + +#define AD1848_ENABLE_MIC_GAIN 0x20 + +#define AD1848_MIXS_LINE1 0x00 +#define AD1848_MIXS_AUX1 0x40 +#define AD1848_MIXS_LINE2 0x80 +#define AD1848_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - AD1848_PLAYBK_FORMAT */ + +#define AD1848_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define AD1848_ALAW_8 0x60 /* 8-bit A-law companded */ +#define AD1848_ULAW_8 0x20 /* 8-bit U-law companded */ +#define AD1848_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define AD1848_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define AD1848_XTAL1 0x00 /* 24.576 crystal */ +#define AD1848_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - AD1848_IFACE_CTRL */ + +#define AD1848_CAPTURE_PIO 0x80 /* capture PIO enable */ +#define AD1848_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define AD1848_CALIB_MODE 0x18 /* calibration mode bits */ +#define AD1848_AUTOCALIB 0x08 /* auto calibrate */ +#define AD1848_SINGLE_DMA 0x04 /* use single DMA channel */ +#define AD1848_CAPTURE_ENABLE 0x02 /* capture enable */ +#define AD1848_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - AD1848_PIN_CTRL */ + +#define AD1848_IRQ_ENABLE 0x02 /* enable IRQ */ +#define AD1848_XCTL1 0x40 /* external control #1 */ +#define AD1848_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - AD1848_TEST_INIT */ + +#define AD1848_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define AD1848_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* defines for codec.mode */ + +#define AD1848_MODE_NONE 0x0000 +#define AD1848_MODE_PLAY 0x0001 +#define AD1848_MODE_CAPTURE 0x0002 +#define AD1848_MODE_TIMER 0x0004 +#define AD1848_MODE_OPEN (AD1848_MODE_PLAY|AD1848_MODE_CAPTURE|AD1848_MODE_TIMER) +#define AD1848_MODE_RUNNING 0x0010 + +/* defines for codec.hardware */ + +#define AD1848_HW_DETECT 0x0000 /* let AD1848 driver detect chip */ +#define AD1848_HW_AD1847 0x0001 /* AD1847 chip */ +#define AD1848_HW_AD1848 0x0002 /* AD1848 chip */ +#define AD1848_HW_CS4248 0x0003 /* CS4248 chip */ +#define AD1848_HW_CMI8330 0x0004 /* CMI8330 chip */ + +struct _snd_ad1848 { + unsigned long port; /* i/o port */ + struct resource *res_port; + int irq; /* IRQ line */ + int dma; /* data DMA */ + unsigned short version; /* version of CODEC chip */ + unsigned short mode; /* see to AD1848_MODE_XXXX */ + unsigned short hardware; /* see to AD1848_HW_XXXX */ + unsigned short single_dma:1; /* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */ + + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_card_t *card; + + unsigned char image[32]; /* SGalaxy needs an access to extended registers */ + int mce_bit; + int calibrate_mute; + int dma_size; + + spinlock_t reg_lock; + struct semaphore open_mutex; +}; + +typedef struct _snd_ad1848 ad1848_t; + +/* exported functions */ + +void snd_ad1848_out(ad1848_t *chip, unsigned char reg, unsigned char value); +void snd_ad1848_dout(ad1848_t *chip, unsigned char reg, unsigned char value); +unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg); +void snd_ad1848_mce_up(ad1848_t *chip); +void snd_ad1848_mce_down(ad1848_t *chip); + +int snd_ad1848_create(snd_card_t * card, + unsigned long port, + int irq, int dma, + unsigned short hardware, + ad1848_t ** chip); + +int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm); +const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction); +int snd_ad1848_mixer(ad1848_t * chip); +void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* exported mixer stuffs */ +enum { AD1848_MIX_SINGLE, AD1848_MIX_DOUBLE, AD1848_MIX_CAPTURE }; + +#define AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) \ + ((reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24)) +#define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \ + ((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22)) + +int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, unsigned long value); + +/* for ease of use */ +struct ad1848_mix_elem { + const char *name; + int index; + int type; + unsigned long private_value; +}; + +#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .name = xname, \ + .index = xindex, \ + .type = AD1848_MIX_SINGLE, \ + .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) } + +#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .name = xname, \ + .index = xindex, \ + .type = AD1848_MIX_DOUBLE, \ + .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) } + +static inline int snd_ad1848_add_ctl_elem(ad1848_t *chip, const struct ad1848_mix_elem *c) +{ + return snd_ad1848_add_ctl(chip, c->name, c->index, c->type, c->private_value); +} + +#ifdef CONFIG_SND_DEBUG +void snd_ad1848_debug(ad1848_t *chip); +#endif + +#endif /* __SOUND_AD1848_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/adriver.h linux/include/sound/adriver.h --- linux-2.4.21-rc1.orig/include/sound/adriver.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/adriver.h 2003-04-29 06:06:49.000000000 -0600 @@ -0,0 +1,325 @@ +#ifndef __SOUND_LOCAL_DRIVER_H +#define __SOUND_LOCAL_DRIVER_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 3) +#error "This driver requires Linux 2.2.3 or higher." +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 1) +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) +# error "This code requires Linux 2.4.0-test1 and higher." +# endif +#define LINUX_2_4__donotuse +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 0) +#define LINUX_2_2 +#endif + +#ifdef ALSA_BUILD +#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__) +#define MODVERSIONS +#include +#include "sndversions.h" +#endif +#ifdef SNDRV_NO_MODVERS +#undef MODVERSIONS +#undef _set_ver +#endif +#endif /* ALSA_BUILD */ + +#include + +#ifdef CONFIG_PCI +#include +#endif + +#ifdef LINUX_2_2 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 18) +#include +#endif +#ifndef LINUX_2_4__donotuse +#include "compat_22.h" +#endif +#endif /* LINUX_2_2 */ + +#ifdef LINUX_2_4__donotuse +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3) +#define pci_set_dma_mask(pci, mask) pci->dma_mask = mask +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 7) +#define PCI_OLD_SUSPEND +#endif +#ifndef virt_to_page +#define virt_to_page(x) (&mem_map[MAP_NR(x)]) +#endif +#define snd_request_region request_region +#ifndef rwlock_init +#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) +#endif +#ifndef list_for_each_safe +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next) +#endif +#endif /* LINUX_2_4__donotuse */ + +#ifndef __devexit_p +#define __devexit_p(x) x +#endif + +#include +#ifndef major +#define major(x) MAJOR(x) +#endif +#ifndef minor +#define minor(x) MINOR(x) +#endif +#ifndef mk_kdev +#define mk_kdev(maj, min) MKDEV(maj, min) +#endif +#ifndef DECLARE_BITMAP +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[((bits)+BITS_PER_LONG-1)/BITS_PER_LONG] +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 3) +#define need_resched() (current->need_resched) +#endif +#include +#if !defined(isa_virt_to_bus) +#if defined(virt_to_bus) || defined(__alpha__) +#define isa_virt_to_bus virt_to_bus +#endif +#endif + +#if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#if (defined(CONFIG_ISAPNP_KERNEL) && defined(ALSA_BUILD)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 30) && !defined(ALSA_BUILD)) +#include +#define isapnp_dev pci_dev +#define isapnp_card pci_bus +#else +#include +#endif +#undef __ISAPNP__ +#define __ISAPNP__ +#endif + +#if !defined(CONFIG_ISA) && defined(CONFIG_SND_ISA) +#define CONFIG_ISA +#endif + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(license) +#endif + +/* no vsnprintf yet? */ +/* FIXME: the version number is not sure.. at least it exists already on 2.4.10 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 10) +#define snprintf(buf,size,fmt,args...) sprintf(buf,fmt,##args) +#define vsnprintf(buf,size,fmt,args) vsprintf(buf,fmt,args) +#endif + +#if defined(__alpha__) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14) +#include +#undef writeb +#define writeb(v, a) do { __writeb((v),(a)); mb(); } while(0) +#undef writew +#define writew(v, a) do { __writew((v),(a)); mb(); } while(0) +#undef writel +#define writel(v, a) do { __writel((v),(a)); mb(); } while(0) +#undef writeq +#define writeq(v, a) do { __writeq((v),(a)); mb(); } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 28) +#include +static inline void synchronize_irq_wrapper(unsigned int irq) { synchronize_irq(); } +#undef synchronize_irq +#define synchronize_irq(irq) synchronize_irq_wrapper(irq) +#endif /* LINUX_VERSION_CODE < 2.5.28 */ + +#ifndef min +/* + * copied from the include/linux/kernel.h file + * for compatibility with earlier kernels. + */ +#define min(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) +#define max(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#ifdef CONFIG_DEVFS_FS +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 29) +#include +#undef register_chrdev +#define register_chrdev devfs_register_chrdev +#undef unregister_chrdev +#define unregister_chrdev devfs_unregister_chrdev +#undef devfs_remove +#define devfs_remove snd_compat_devfs_remove +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) +static inline void devfs_find_and_unregister (devfs_handle_t dir, const char *name, + unsigned int major, unsigned int minor, + char type, int traverse_symlinks) +{ + devfs_handle_t master; + master = devfs_find_handle(dir, name, strlen(name), major, minor, type, traverse_symlinks); + devfs_unregister(master); +} +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +static inline void devfs_find_and_unregister (devfs_handle_t dir, const char *name, + unsigned int major, unsigned int minor, + char type, int traverse_symlinks) +{ + devfs_handle_t master; + master = devfs_find_handle(dir, name, major, minor, type, traverse_symlinks); + devfs_unregister(master); +} +#endif +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 29) +static inline void devfs_remove(const char *fmt, ...) { } +#endif +#endif /* CONFIG_DEVFS_FS */ + +/* workarounds for USB API */ +#if defined(SND_NEED_USB_WRAPPER) && (defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)) + +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) +inline static int usb_make_path(struct usb_device *dev, char *buf, size_t size) +{ + int actual; + actual = snprintf(buf, size, "%03d/%03d", dev->bus->busnum, dev->devnum); + return (actual >= (int)size) ? -1 : actual; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +inline static struct urb *usb_alloc_urb_wrapper(int iso_packets, int flags) +{ + return usb_alloc_urb(iso_packets); +} +inline static int usb_submit_urb_wrapper(struct urb *urb, int flags) +{ + return usb_submit_urb(urb); +} +#undef usb_alloc_urb +#undef usb_submit_urb +#define usb_alloc_urb(n,flags) usb_alloc_urb_wrapper(n,flags) +#define usb_submit_urb(p,flags) usb_submit_urb_wrapper(p,flags) +#define OLD_USB +#endif /* LINUX_VERSION_CODE < 2.5.0 */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 24) +int snd_hack_usb_set_interface(struct usb_device *dev, int interface, int alternate); +#undef usb_set_interface +#define usb_set_interface(dev,iface,alt) snd_hack_usb_set_interface(dev,iface,alt) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 45) +#define URB_ISO_ASAP USB_ISO_ASAP +#define URB_ASYNC_UNLINK USB_ASYNC_UNLINK +#define usb_fill_int_urb FILL_INT_URB +#define usb_fill_bulk_urb FILL_BULK_URB +#define usb_host_config usb_config_descriptor +#define usb_host_interface usb_interface_descriptor +#define usb_host_endpoint usb_endpoint_descriptor +#define get_iface_desc(iface) (iface) +#define get_endpoint(alt,ep) (&(alt)->endpoint[ep]) +#define get_ep_desc(ep) (ep) +#define get_cfg_desc(cfg) (cfg) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 45) +#define usb_pipe_needs_resubmit(pipe) (!usb_pipeint(pipe)) +#endif + +#endif /* SND_NEED_USB_WRAPPER && CONFIG_USB */ + +/* workqueue-alike; 2.5.45 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 45) && !defined(__WORK_INITIALIZER) +struct work_struct { + void (*func)(void *); + void *data; +}; +#define INIT_WORK(_work, _func, _data) \ + do { \ + (_work)->func = _func; \ + (_work)->data = _data; \ + } while (0) +#define __WORK_INITIALIZER(n, f, d) { \ + .func = (f), \ + .data = (d), \ + } +#define DECLARE_WORK(n, f, d) \ + struct work_struct n = __WORK_INITIALIZER(n, f, d) +int snd_compat_schedule_work(struct work_struct *work); +#define schedule_work(w) snd_compat_schedule_work(w) +#else +#include +#endif /* 2.5.45 */ + +/* 2.5 new modules */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define try_module_get(x) try_inc_mod_count(x) +static inline void module_put(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} +#endif /* 2.5.0 */ + +/* gameport - 2.4 has different defines */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#ifdef CONFIG_INPUT_GAMEPORT +#define CONFIG_GAMEPORT +#endif +#ifdef CONFIG_INPUT_GAMEPORT_MODULE +#define CONFIG_GAMEPORT_MODULE +#endif +#endif /* 2.5.0 */ + +/* vmalloc_to_page wrapper */ +#ifndef CONFIG_HAVE_VMALLOC_TO_PAGE +struct page *snd_compat_vmalloc_to_page(void *addr); +#define vmalloc_to_page(addr) snd_compat_vmalloc_to_page(addr) +#endif + +#include "amagic.h" + +#endif /* __SOUND_LOCAL_DRIVER_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ainstr_fm.h linux/include/sound/ainstr_fm.h --- linux-2.4.21-rc1.orig/include/sound/ainstr_fm.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ainstr_fm.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,132 @@ +/* + * Advanced Linux Sound Architecture + * + * FM (OPL2/3) Instrument Format + * Copyright (c) 2000 Uros Bizjak + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_FM_H +#define __SOUND_AINSTR_FM_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define FM_SHARE_FILE 0 + +/* + * FM operator + */ + +typedef struct fm_operator { + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char wave_select; +} fm_operator_t; + +/* + * Instrument + */ + +#define FM_PATCH_OPL2 0x01 /* OPL2 2 operators FM instrument */ +#define FM_PATCH_OPL3 0x02 /* OPL3 4 operators FM instrument */ + +typedef struct { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned char type; /* instrument type */ + + fm_operator_t op[4]; + unsigned char feedback_connection[2]; + + unsigned char echo_delay; + unsigned char echo_atten; + unsigned char chorus_spread; + unsigned char trnsps; + unsigned char fix_dur; + unsigned char modes; + unsigned char fix_key; +} fm_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * fm_xinstrument FM_STRU_INSTR + * + */ + +#define FM_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * FM operator + */ + +typedef struct fm_xoperator { + __u8 am_vib; + __u8 ksl_level; + __u8 attack_decay; + __u8 sustain_release; + __u8 wave_select; +} fm_xoperator_t; + +/* + * Instrument + */ + +typedef struct fm_xinstrument { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u8 type; /* instrument type */ + + fm_xoperator_t op[4]; /* fm operators */ + __u8 feedback_connection[2]; + + __u8 echo_delay; + __u8 echo_atten; + __u8 chorus_spread; + __u8 trnsps; + __u8 fix_dur; + __u8 modes; + __u8 fix_key; +} fm_xinstrument_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_fm_id; + +int snd_seq_fm_init(snd_seq_kinstr_ops_t * ops, + snd_seq_kinstr_ops_t * next); + +#endif + +#endif /* __SOUND_AINSTR_FM_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ainstr_gf1.h linux/include/sound/ainstr_gf1.h --- linux-2.4.21-rc1.orig/include/sound/ainstr_gf1.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ainstr_gf1.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,227 @@ +/* + * Advanced Linux Sound Architecture + * + * GF1 (GUS) Patch Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_GF1_H +#define __SOUND_AINSTR_GF1_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define GF1_SHARE_FILE 0 + +/* + * wave formats + */ + +#define GF1_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define GF1_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define GF1_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define GF1_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define GF1_WAVE_LOOP 0x0008 /* loop mode */ +#define GF1_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define GF1_WAVE_STEREO 0x0100 /* stereo mode */ +#define GF1_WAVE_ULAW 0x0200 /* uLaw compression mode */ + +/* + * Wavetable definitions + */ + +typedef struct gf1_wave { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this instrument */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + + unsigned char flags; /* GF1 patch flags */ + unsigned char pad; + unsigned int sample_rate; /* sample rate in Hz */ + unsigned int low_frequency; /* low frequency range */ + unsigned int high_frequency; /* high frequency range */ + unsigned int root_frequency; /* root frequency range */ + signed short tune; + unsigned char balance; + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + unsigned short scale_frequency; + unsigned short scale_factor; /* 0-2048 or 0-2 */ + + struct gf1_wave *next; +} gf1_wave_t; + +/* + * Instrument + */ + +#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */ +#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */ +#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define IWFFFF_EFFECT_NONE 0 +#define IWFFFF_EFFECT_REVERB 1 +#define IWFFFF_EFFECT_CHORUS 2 +#define IWFFFF_EFFECT_ECHO 3 + +typedef struct { + unsigned short exclusion; + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ + + gf1_wave_t *wave; /* first waveform */ +} gf1_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * gf1_xinstrument IWFFFF_STRU_INSTR + * +gf1_xwave IWFFFF_STRU_WAVE + * + */ + +#define GF1_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E') +#define GF1_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Wavetable definitions + */ + +typedef struct gf1_xwave { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u32 format; /* wave format */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + + __u8 flags; /* GF1 patch flags */ + __u8 pad; + __u32 sample_rate; /* sample rate in Hz */ + __u32 low_frequency; /* low frequency range */ + __u32 high_frequency; /* high frequency range */ + __u32 root_frequency; /* root frequency range */ + __s16 tune; + __u8 balance; + __u8 envelope_rate[6]; + __u8 envelope_offset[6]; + __u8 tremolo_sweep; + __u8 tremolo_rate; + __u8 tremolo_depth; + __u8 vibrato_sweep; + __u8 vibrato_rate; + __u8 vibrato_depth; + __u16 scale_frequency; + __u16 scale_factor; /* 0-2048 or 0-2 */ +} gf1_xwave_t; + +/* + * Instrument + */ + +typedef struct gf1_xinstrument { + __u32 stype; + + __u16 exclusion; + __u16 exclusion_group; /* 0 - none, 1-65535 */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} gf1_xinstrument_t; + +/* + * Instrument info + */ + +#define GF1_INFO_ENVELOPE (1<<0) +#define GF1_INFO_TREMOLO (1<<1) +#define GF1_INFO_VIBRATO (1<<2) + +typedef struct gf1_info { + unsigned char flags; /* supported wave flags */ + unsigned char pad[3]; + unsigned int features; /* supported features */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} gf1_info_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_gf1_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, gf1_info_t *info); + int (*put_sample)(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, gf1_wave_t *wave, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_gf1_ops_t; + +int snd_seq_gf1_init(snd_gf1_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_GF1_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ainstr_iw.h linux/include/sound/ainstr_iw.h --- linux-2.4.21-rc1.orig/include/sound/ainstr_iw.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ainstr_iw.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,375 @@ +/* + * Advanced Linux Sound Architecture + * + * InterWave FFFF Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_IW_H +#define __SOUND_AINSTR_IW_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define IWFFFF_SHARE_FILE 0 + +/* + * wave formats + */ + +#define IWFFFF_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define IWFFFF_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define IWFFFF_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define IWFFFF_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define IWFFFF_WAVE_LOOP 0x0008 /* loop mode */ +#define IWFFFF_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define IWFFFF_WAVE_ULAW 0x0020 /* uLaw compressed wave */ +#define IWFFFF_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */ +#define IWFFFF_WAVE_ROM 0x0080 /* wave is in ROM */ +#define IWFFFF_WAVE_STEREO 0x0100 /* wave is stereo */ + +/* + * Wavetable definitions + */ + +typedef struct iwffff_wave { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this wave */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + unsigned int sample_ratio; /* sample ratio (44100 * 1024 / rate) */ + unsigned char attenuation; /* 0 - 127 (no corresponding midi controller) */ + unsigned char low_note; /* lower frequency range for this waveform */ + unsigned char high_note; /* higher frequency range for this waveform */ + unsigned char pad; + + struct iwffff_wave *next; +} iwffff_wave_t; + +/* + * Layer + */ + +#define IWFFFF_LFO_SHAPE_TRIANGLE 0 +#define IWFFFF_LFO_SHAPE_POSTRIANGLE 1 + +typedef struct iwffff_lfo { + unsigned short freq; /* (0-2047) 0.01Hz - 21.5Hz */ + signed short depth; /* volume +- (0-255) 0.48675dB/step */ + signed short sweep; /* 0 - 950 deciseconds */ + unsigned char shape; /* see to IWFFFF_LFO_SHAPE_XXXX */ + unsigned char delay; /* 0 - 255 deciseconds */ +} iwffff_lfo_t; + +#define IWFFFF_ENV_FLAG_RETRIGGER 0x0001 /* flag - retrigger */ + +#define IWFFFF_ENV_MODE_ONE_SHOT 0x0001 /* mode - one shot */ +#define IWFFFF_ENV_MODE_SUSTAIN 0x0002 /* mode - sustain */ +#define IWFFFF_ENV_MODE_NO_SUSTAIN 0x0003 /* mode - no sustain */ + +#define IWFFFF_ENV_INDEX_VELOCITY 0x0001 /* index - velocity */ +#define IWFFFF_ENV_INDEX_FREQUENCY 0x0002 /* index - frequency */ + +typedef struct iwffff_env_point { + unsigned short offset; + unsigned short rate; +} iwffff_env_point_t; + +typedef struct iwffff_env_record { + unsigned short nattack; + unsigned short nrelease; + unsigned short sustain_offset; + unsigned short sustain_rate; + unsigned short release_rate; + unsigned char hirange; + unsigned char pad; + struct iwffff_env_record *next; + /* points are stored here */ + /* count of points = nattack + nrelease */ +} iwffff_env_record_t; + +typedef struct iwffff_env { + unsigned char flags; + unsigned char mode; + unsigned char index; + unsigned char pad; + struct iwffff_env_record *record; +} iwffff_env_t; + +#define IWFFFF_LAYER_FLAG_RETRIGGER 0x0001 /* retrigger */ + +#define IWFFFF_LAYER_VELOCITY_TIME 0x0000 /* velocity mode = time */ +#define IWFFFF_LAYER_VELOCITY_RATE 0x0001 /* velocity mode = rate */ + +#define IWFFFF_LAYER_EVENT_KUP 0x0000 /* layer event - key up */ +#define IWFFFF_LAYER_EVENT_KDOWN 0x0001 /* layer event - key down */ +#define IWFFFF_LAYER_EVENT_RETRIG 0x0002 /* layer event - retrigger */ +#define IWFFFF_LAYER_EVENT_LEGATO 0x0003 /* layer event - legato */ + +typedef struct iwffff_layer { + unsigned char flags; + unsigned char velocity_mode; + unsigned char layer_event; + unsigned char low_range; /* range for layer based */ + unsigned char high_range; /* on either velocity or frequency */ + unsigned char pan; /* pan offset from CC1 (0 left - 127 right) */ + unsigned char pan_freq_scale; /* position based on frequency (0-127) */ + unsigned char attenuation; /* 0-127 (no corresponding midi controller) */ + iwffff_lfo_t tremolo; /* tremolo effect */ + iwffff_lfo_t vibrato; /* vibrato effect */ + unsigned short freq_scale; /* 0-2048, 1024 is equal to semitone scaling */ + unsigned char freq_center; /* center for keyboard frequency scaling */ + unsigned char pad; + iwffff_env_t penv; /* pitch envelope */ + iwffff_env_t venv; /* volume envelope */ + + iwffff_wave_t *wave; + struct iwffff_layer *next; +} iwffff_layer_t; + +/* + * Instrument + */ + +#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */ +#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */ +#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define IWFFFF_LAYER_NONE 0x0000 /* not layered */ +#define IWFFFF_LAYER_ON 0x0001 /* layered */ +#define IWFFFF_LAYER_VELOCITY 0x0002 /* layered by velocity */ +#define IWFFFF_LAYER_FREQUENCY 0x0003 /* layered by frequency */ + +#define IWFFFF_EFFECT_NONE 0 +#define IWFFFF_EFFECT_REVERB 1 +#define IWFFFF_EFFECT_CHORUS 2 +#define IWFFFF_EFFECT_ECHO 3 + +typedef struct { + unsigned short exclusion; + unsigned short layer_type; + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ + + iwffff_layer_t *layer; /* first layer */ +} iwffff_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * iwffff_xinstrument IWFFFF_STRU_INSTR + * +iwffff_xlayer IWFFFF_STRU_LAYER + * *iwffff_xenv_record IWFFFF_STRU_ENV_RECT (tremolo) + * *iwffff_xenv_record IWFFFF_STRU_EVN_RECT (vibrato) + * +iwffff_xwave IWFFFF_STRU_WAVE + * + */ + +#define IWFFFF_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E') +#define IWFFFF_STRU_ENV_RECP __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P') +#define IWFFFF_STRU_ENV_RECV __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V') +#define IWFFFF_STRU_LAYER __cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R') +#define IWFFFF_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Wavetable definitions + */ + +typedef struct iwffff_xwave { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + + __u32 format; /* wave format */ + __u32 offset; /* offset to ROM (address) */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + __u32 sample_ratio; /* sample ratio (44100 * 1024 / rate) */ + __u8 attenuation; /* 0 - 127 (no corresponding midi controller) */ + __u8 low_note; /* lower frequency range for this waveform */ + __u8 high_note; /* higher frequency range for this waveform */ + __u8 pad; +} iwffff_xwave_t; + +/* + * Layer + */ + +typedef struct iwffff_xlfo { + __u16 freq; /* (0-2047) 0.01Hz - 21.5Hz */ + __s16 depth; /* volume +- (0-255) 0.48675dB/step */ + __s16 sweep; /* 0 - 950 deciseconds */ + __u8 shape; /* see to ULTRA_IW_LFO_SHAPE_XXXX */ + __u8 delay; /* 0 - 255 deciseconds */ +} iwffff_xlfo_t; + +typedef struct iwffff_xenv_point { + __u16 offset; + __u16 rate; +} iwffff_xenv_point_t; + +typedef struct iwffff_xenv_record { + __u32 stype; + __u16 nattack; + __u16 nrelease; + __u16 sustain_offset; + __u16 sustain_rate; + __u16 release_rate; + __u8 hirange; + __u8 pad; + /* points are stored here.. */ + /* count of points = nattack + nrelease */ +} iwffff_xenv_record_t; + +typedef struct iwffff_xenv { + __u8 flags; + __u8 mode; + __u8 index; + __u8 pad; +} iwffff_xenv_t; + +typedef struct iwffff_xlayer { + __u32 stype; + __u8 flags; + __u8 velocity_mode; + __u8 layer_event; + __u8 low_range; /* range for layer based */ + __u8 high_range; /* on either velocity or frequency */ + __u8 pan; /* pan offset from CC1 (0 left - 127 right) */ + __u8 pan_freq_scale; /* position based on frequency (0-127) */ + __u8 attenuation; /* 0-127 (no corresponding midi controller) */ + iwffff_xlfo_t tremolo; /* tremolo effect */ + iwffff_xlfo_t vibrato; /* vibrato effect */ + __u16 freq_scale; /* 0-2048, 1024 is equal to semitone scaling */ + __u8 freq_center; /* center for keyboard frequency scaling */ + __u8 pad; + iwffff_xenv_t penv; /* pitch envelope */ + iwffff_xenv_t venv; /* volume envelope */ +} iwffff_xlayer_t; + +/* + * Instrument + */ + +typedef struct iwffff_xinstrument { + __u32 stype; + + __u16 exclusion; + __u16 layer_type; + __u16 exclusion_group; /* 0 - none, 1-65535 */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} iwffff_xinstrument_t; + +/* + * ROM support + * InterWave ROMs are Little-Endian (x86) + */ + +#define IWFFFF_ROM_HDR_SIZE 512 + +typedef struct { + __u8 iwave[8]; + __u8 revision; + __u8 series_number; + __u8 series_name[16]; + __u8 date[10]; + __u16 vendor_revision_major; + __u16 vendor_revision_minor; + __u32 rom_size; + __u8 copyright[128]; + __u8 vendor_name[64]; + __u8 description[128]; +} iwffff_rom_header_t; + +/* + * Instrument info + */ + +#define IWFFFF_INFO_LFO_VIBRATO (1<<0) +#define IWFFFF_INFO_LFO_VIBRATO_SHAPE (1<<1) +#define IWFFFF_INFO_LFO_TREMOLO (1<<2) +#define IWFFFF_INFO_LFO_TREMOLO_SHAPE (1<<3) + +typedef struct iwffff_info { + unsigned int format; /* supported format bits */ + unsigned int effects; /* supported effects (1 << IWFFFF_EFFECT*) */ + unsigned int lfos; /* LFO effects */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} iwffff_info_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_iwffff_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, iwffff_info_t *info); + int (*put_sample)(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, iwffff_wave_t *wave, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_iwffff_ops_t; + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_IW_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ainstr_simple.h linux/include/sound/ainstr_simple.h --- linux-2.4.21-rc1.orig/include/sound/ainstr_simple.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ainstr_simple.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,158 @@ +/* + * Advanced Linux Sound Architecture + * + * Simple (MOD player) Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_SIMPLE_H +#define __SOUND_AINSTR_SIMPLE_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define SIMPLE_SHARE_FILE 0 + +/* + * wave formats + */ + +#define SIMPLE_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define SIMPLE_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define SIMPLE_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define SIMPLE_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define SIMPLE_WAVE_LOOP 0x0008 /* loop mode */ +#define SIMPLE_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define SIMPLE_WAVE_STEREO 0x0100 /* stereo wave */ +#define SIMPLE_WAVE_ULAW 0x0200 /* uLaw compression mode */ + +/* + * instrument effects + */ + +#define SIMPLE_EFFECT_NONE 0 +#define SIMPLE_EFFECT_REVERB 1 +#define SIMPLE_EFFECT_CHORUS 2 +#define SIMPLE_EFFECT_ECHO 3 + +/* + * instrument info + */ + +typedef struct simple_instrument_info { + unsigned int format; /* supported format bits */ + unsigned int effects; /* supported effects (1 << SIMPLE_EFFECT_*) */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} simple_instrument_info_t; + +/* + * Instrument + */ + +typedef struct { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this instrument */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop end offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ +} simple_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * simple_xinstrument SIMPLE_STRU_INSTR + * + */ + +#define SIMPLE_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Instrument + */ + +typedef struct simple_xinstrument { + __u32 stype; + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u32 format; /* wave format */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} simple_xinstrument_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_simple_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, simple_instrument_info_t *info); + int (*put_sample)(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, simple_instrument_t *instr, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_simple_ops_t; + +int snd_seq_simple_init(snd_simple_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_SIMPLE_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/ak4531_codec.h linux/include/sound/ak4531_codec.h --- linux-2.4.21-rc1.orig/include/sound/ak4531_codec.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ak4531_codec.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,80 @@ +#ifndef __SOUND_AK4531_CODEC_H +#define __SOUND_AK4531_CODEC_H + +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "info.h" +#include "control.h" + +/* + * ASAHI KASEI - AK4531 codec + * - not really AC'97 codec, but it uses very similar interface as AC'97 + */ + +/* + * AK4531 codec registers + */ + +#define AK4531_LMASTER 0x00 /* master volume left */ +#define AK4531_RMASTER 0x01 /* master volume right */ +#define AK4531_LVOICE 0x02 /* channel volume left */ +#define AK4531_RVOICE 0x03 /* channel volume right */ +#define AK4531_LFM 0x04 /* FM volume left */ +#define AK4531_RFM 0x05 /* FM volume right */ +#define AK4531_LCD 0x06 /* CD volume left */ +#define AK4531_RCD 0x07 /* CD volume right */ +#define AK4531_LLINE 0x08 /* LINE volume left */ +#define AK4531_RLINE 0x09 /* LINE volume right */ +#define AK4531_LAUXA 0x0a /* AUXA volume left */ +#define AK4531_RAUXA 0x0b /* AUXA volume right */ +#define AK4531_MONO1 0x0c /* MONO1 volume left */ +#define AK4531_MONO2 0x0d /* MONO1 volume right */ +#define AK4531_MIC 0x0e /* MIC volume */ +#define AK4531_MONO_OUT 0x0f /* Mono-out volume */ +#define AK4531_OUT_SW1 0x10 /* Output mixer switch 1 */ +#define AK4531_OUT_SW2 0x11 /* Output mixer switch 2 */ +#define AK4531_LIN_SW1 0x12 /* Input left mixer switch 1 */ +#define AK4531_RIN_SW1 0x13 /* Input right mixer switch 1 */ +#define AK4531_LIN_SW2 0x14 /* Input left mixer switch 2 */ +#define AK4531_RIN_SW2 0x15 /* Input right mixer switch 2 */ +#define AK4531_RESET 0x16 /* Reset & power down */ +#define AK4531_CLOCK 0x17 /* Clock select */ +#define AK4531_AD_IN 0x18 /* AD input select */ +#define AK4531_MIC_GAIN 0x19 /* MIC amplified gain */ + +typedef struct _snd_ak4531 ak4531_t; + +struct _snd_ak4531 { + void (*write) (ak4531_t *ak4531, unsigned short reg, unsigned short val); + void *private_data; + void (*private_free) (ak4531_t *ak4531); + /* --- */ + unsigned char regs[0x20]; + spinlock_t reg_lock; +}; + +int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531); + +#endif /* __SOUND_AK4531_CODEC_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/amagic.h linux/include/sound/amagic.h --- linux-2.4.21-rc1.orig/include/sound/amagic.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/amagic.h 2003-04-29 05:46:59.000000000 -0600 @@ -0,0 +1,11 @@ +/* + * additional magic numbers for extra drivers + */ +#ifdef CONFIG_SND_DEBUG_MEMORY + +#define snd_msndpinnacle_pcm_t_magic 0xa15a3e01 +#define msndmidi_t_magic 0xa15a3e02 + +#define azf3328_t_magic 0xa15a4000 + +#endif diff -urN linux-2.4.21-rc1.orig/include/sound/asequencer.h linux/include/sound/asequencer.h --- linux-2.4.21-rc1.orig/include/sound/asequencer.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/asequencer.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,904 @@ +/* + * Main header file for the ALSA sequencer + * Copyright (c) 1998-1999 by Frank van de Pol + * (c) 1998-1999 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SOUND_ASEQUENCER_H +#define __SOUND_ASEQUENCER_H + +#ifndef __KERNEL__ +#include +#endif + +#include + +/** version of the sequencer */ +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 0) + +/** + * definition of sequencer event types + */ + +/** system messages + * event data type = #sndrv_seq_result_t + */ +#define SNDRV_SEQ_EVENT_SYSTEM 0 +#define SNDRV_SEQ_EVENT_RESULT 1 + +/** note messages (channel specific) + * event data type = #sndrv_seq_ev_note + */ +#define SNDRV_SEQ_EVENT_NOTE 5 +#define SNDRV_SEQ_EVENT_NOTEON 6 +#define SNDRV_SEQ_EVENT_NOTEOFF 7 +#define SNDRV_SEQ_EVENT_KEYPRESS 8 + +/** control messages (channel specific) + * event data type = #sndrv_seq_ev_ctrl + */ +#define SNDRV_SEQ_EVENT_CONTROLLER 10 +#define SNDRV_SEQ_EVENT_PGMCHANGE 11 +#define SNDRV_SEQ_EVENT_CHANPRESS 12 +#define SNDRV_SEQ_EVENT_PITCHBEND 13 /**< from -8192 to 8191 */ +#define SNDRV_SEQ_EVENT_CONTROL14 14 /**< 14 bit controller value */ +#define SNDRV_SEQ_EVENT_NONREGPARAM 15 /**< 14 bit NRPN */ +#define SNDRV_SEQ_EVENT_REGPARAM 16 /**< 14 bit RPN */ + +/** synchronisation messages + * event data type = #sndrv_seq_ev_ctrl + */ +#define SNDRV_SEQ_EVENT_SONGPOS 20 /* Song Position Pointer with LSB and MSB values */ +#define SNDRV_SEQ_EVENT_SONGSEL 21 /* Song Select with song ID number */ +#define SNDRV_SEQ_EVENT_QFRAME 22 /* midi time code quarter frame */ +#define SNDRV_SEQ_EVENT_TIMESIGN 23 /* SMF Time Signature event */ +#define SNDRV_SEQ_EVENT_KEYSIGN 24 /* SMF Key Signature event */ + +/** timer messages + * event data type = sndrv_seq_ev_queue_control_t + */ +#define SNDRV_SEQ_EVENT_START 30 /* midi Real Time Start message */ +#define SNDRV_SEQ_EVENT_CONTINUE 31 /* midi Real Time Continue message */ +#define SNDRV_SEQ_EVENT_STOP 32 /* midi Real Time Stop message */ +#define SNDRV_SEQ_EVENT_SETPOS_TICK 33 /* set tick queue position */ +#define SNDRV_SEQ_EVENT_SETPOS_TIME 34 /* set realtime queue position */ +#define SNDRV_SEQ_EVENT_TEMPO 35 /* (SMF) Tempo event */ +#define SNDRV_SEQ_EVENT_CLOCK 36 /* midi Real Time Clock message */ +#define SNDRV_SEQ_EVENT_TICK 37 /* midi Real Time Tick message */ +#define SNDRV_SEQ_EVENT_QUEUE_SKEW 38 /* skew queue tempo */ + +/** others + * event data type = none + */ +#define SNDRV_SEQ_EVENT_TUNE_REQUEST 40 /* tune request */ +#define SNDRV_SEQ_EVENT_RESET 41 /* reset to power-on state */ +#define SNDRV_SEQ_EVENT_SENSING 42 /* "active sensing" event */ + +/** echo back, kernel private messages + * event data type = any type + */ +#define SNDRV_SEQ_EVENT_ECHO 50 /* echo event */ +#define SNDRV_SEQ_EVENT_OSS 51 /* OSS raw event */ + +/** system status messages (broadcast for subscribers) + * event data type = sndrv_seq_addr_t + */ +#define SNDRV_SEQ_EVENT_CLIENT_START 60 /* new client has connected */ +#define SNDRV_SEQ_EVENT_CLIENT_EXIT 61 /* client has left the system */ +#define SNDRV_SEQ_EVENT_CLIENT_CHANGE 62 /* client status/info has changed */ +#define SNDRV_SEQ_EVENT_PORT_START 63 /* new port was created */ +#define SNDRV_SEQ_EVENT_PORT_EXIT 64 /* port was deleted from system */ +#define SNDRV_SEQ_EVENT_PORT_CHANGE 65 /* port status/info has changed */ + +/** port connection changes + * event data type = sndrv_seq_connect_t + */ +#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */ +#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */ + +/** synthesizer events + * event data type = sndrv_seq_eve_sample_control_t + */ +#define SNDRV_SEQ_EVENT_SAMPLE 70 /* sample select */ +#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER 71 /* sample cluster select */ +#define SNDRV_SEQ_EVENT_SAMPLE_START 72 /* voice start */ +#define SNDRV_SEQ_EVENT_SAMPLE_STOP 73 /* voice stop */ +#define SNDRV_SEQ_EVENT_SAMPLE_FREQ 74 /* playback frequency */ +#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME 75 /* volume and balance */ +#define SNDRV_SEQ_EVENT_SAMPLE_LOOP 76 /* sample loop */ +#define SNDRV_SEQ_EVENT_SAMPLE_POSITION 77 /* sample position */ +#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1 78 /* private (hardware dependent) event */ + +/** user-defined events with fixed length + * event data type = any + */ +#define SNDRV_SEQ_EVENT_USR0 90 +#define SNDRV_SEQ_EVENT_USR1 91 +#define SNDRV_SEQ_EVENT_USR2 92 +#define SNDRV_SEQ_EVENT_USR3 93 +#define SNDRV_SEQ_EVENT_USR4 94 +#define SNDRV_SEQ_EVENT_USR5 95 +#define SNDRV_SEQ_EVENT_USR6 96 +#define SNDRV_SEQ_EVENT_USR7 97 +#define SNDRV_SEQ_EVENT_USR8 98 +#define SNDRV_SEQ_EVENT_USR9 99 + +/** instrument layer + * variable length data can be passed directly to the driver + */ +#define SNDRV_SEQ_EVENT_INSTR_BEGIN 100 /* begin of instrument management */ +#define SNDRV_SEQ_EVENT_INSTR_END 101 /* end of instrument management */ +#define SNDRV_SEQ_EVENT_INSTR_INFO 102 /* instrument interface info */ +#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_FINFO 104 /* get format info */ +#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105 /* get format info */ +#define SNDRV_SEQ_EVENT_INSTR_RESET 106 /* reset instrument memory */ +#define SNDRV_SEQ_EVENT_INSTR_STATUS 107 /* instrument interface status */ +#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_PUT 109 /* put instrument to port */ +#define SNDRV_SEQ_EVENT_INSTR_GET 110 /* get instrument from port */ +#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_FREE 112 /* free instrument(s) */ +#define SNDRV_SEQ_EVENT_INSTR_LIST 113 /* instrument list */ +#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER 115 /* cluster parameters */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116 /* get cluster parameters */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_CHANGE 118 /* instrument change */ +/* 119-129: reserved */ + +/* 130-139: variable length events + * event data type = sndrv_seq_ev_ext + * (SNDRV_SEQ_EVENT_LENGTH_VARIABLE must be set) + */ +#define SNDRV_SEQ_EVENT_SYSEX 130 /* system exclusive data (variable length) */ +#define SNDRV_SEQ_EVENT_BOUNCE 131 /* error event */ +/* 132-134: reserved */ +#define SNDRV_SEQ_EVENT_USR_VAR0 135 +#define SNDRV_SEQ_EVENT_USR_VAR1 136 +#define SNDRV_SEQ_EVENT_USR_VAR2 137 +#define SNDRV_SEQ_EVENT_USR_VAR3 138 +#define SNDRV_SEQ_EVENT_USR_VAR4 139 + +/* 150-151: kernel events with quote - DO NOT use in user clients */ +#define SNDRV_SEQ_EVENT_KERNEL_ERROR 150 +#define SNDRV_SEQ_EVENT_KERNEL_QUOTE 151 + +/* 152-191: reserved */ + +/* 192-254: hardware specific events */ + +/* 255: special event */ +#define SNDRV_SEQ_EVENT_NONE 255 + + +typedef unsigned char sndrv_seq_event_type_t; + +/** event address */ +struct sndrv_seq_addr { + unsigned char client; /**< Client number: 0..255, 255 = broadcast to all clients */ + unsigned char port; /**< Port within client: 0..255, 255 = broadcast to all ports */ +}; + +/** port connection */ +struct sndrv_seq_connect { + struct sndrv_seq_addr sender; + struct sndrv_seq_addr dest; +}; + + +#define SNDRV_SEQ_ADDRESS_UNKNOWN 253 /* unknown source */ +#define SNDRV_SEQ_ADDRESS_SUBSCRIBERS 254 /* send event to all subscribed ports */ +#define SNDRV_SEQ_ADDRESS_BROADCAST 255 /* send event to all queues/clients/ports/channels */ +#define SNDRV_SEQ_QUEUE_DIRECT 253 /* direct dispatch */ + + /* event mode flag - NOTE: only 8 bits available! */ +#define SNDRV_SEQ_TIME_STAMP_TICK (0<<0) /* timestamp in clock ticks */ +#define SNDRV_SEQ_TIME_STAMP_REAL (1<<0) /* timestamp in real time */ +#define SNDRV_SEQ_TIME_STAMP_MASK (1<<0) + +#define SNDRV_SEQ_TIME_MODE_ABS (0<<1) /* absolute timestamp */ +#define SNDRV_SEQ_TIME_MODE_REL (1<<1) /* relative to current time */ +#define SNDRV_SEQ_TIME_MODE_MASK (1<<1) + +#define SNDRV_SEQ_EVENT_LENGTH_FIXED (0<<2) /* fixed event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /* variable event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARUSR (2<<2) /* variable event size - user memory space */ +#define SNDRV_SEQ_EVENT_LENGTH_MASK (3<<2) + +#define SNDRV_SEQ_PRIORITY_NORMAL (0<<4) /* normal priority */ +#define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */ +#define SNDRV_SEQ_PRIORITY_MASK (1<<4) + + + /* note event */ +struct sndrv_seq_ev_note { + unsigned char channel; + unsigned char note; + unsigned char velocity; + unsigned char off_velocity; /* only for SNDRV_SEQ_EVENT_NOTE */ + unsigned int duration; /* only for SNDRV_SEQ_EVENT_NOTE */ +}; + + /* controller event */ +struct sndrv_seq_ev_ctrl { + unsigned char channel; + unsigned char unused1, unused2, unused3; /* pad */ + unsigned int param; + signed int value; +}; + + /* generic set of bytes (12x8 bit) */ +struct sndrv_seq_ev_raw8 { + unsigned char d[12]; /* 8 bit value */ +}; + + /* generic set of integers (3x32 bit) */ +struct sndrv_seq_ev_raw32 { + unsigned int d[3]; /* 32 bit value */ +}; + + /* external stored data */ +struct sndrv_seq_ev_ext { + unsigned int len; /* length of data */ + void *ptr; /* pointer to data (note: maybe 64-bit) */ +} __attribute__((packed)); + +/* Instrument cluster type */ +typedef unsigned int sndrv_seq_instr_cluster_t; + +/* Instrument type */ +struct sndrv_seq_instr { + sndrv_seq_instr_cluster_t cluster; + unsigned int std; /* the upper byte means a private instrument (owner - client #) */ + unsigned short bank; + unsigned short prg; +}; + + /* sample number */ +struct sndrv_seq_ev_sample { + unsigned int std; + unsigned short bank; + unsigned short prg; +}; + + /* sample cluster */ +struct sndrv_seq_ev_cluster { + sndrv_seq_instr_cluster_t cluster; +}; + + /* sample position */ +typedef unsigned int sndrv_seq_position_t; /* playback position (in samples) * 16 */ + + /* sample stop mode */ +enum sndrv_seq_stop_mode { + SAMPLE_STOP_IMMEDIATELY = 0, /* terminate playing immediately */ + SAMPLE_STOP_VENVELOPE = 1, /* finish volume envelope */ + SAMPLE_STOP_LOOP = 2 /* terminate loop and finish wave */ +}; + + /* sample frequency */ +typedef int sndrv_seq_frequency_t; /* playback frequency in HZ * 16 */ + + /* sample volume control; if any value is set to -1 == do not change */ +struct sndrv_seq_ev_volume { + signed short volume; /* range: 0-16383 */ + signed short lr; /* left-right balance; range: 0-16383 */ + signed short fr; /* front-rear balance; range: 0-16383 */ + signed short du; /* down-up balance; range: 0-16383 */ +}; + + /* simple loop redefinition */ +struct sndrv_seq_ev_loop { + unsigned int start; /* loop start (in samples) * 16 */ + unsigned int end; /* loop end (in samples) * 16 */ +}; + +struct sndrv_seq_ev_sample_control { + unsigned char channel; + unsigned char unused1, unused2, unused3; /* pad */ + union { + struct sndrv_seq_ev_sample sample; + struct sndrv_seq_ev_cluster cluster; + sndrv_seq_position_t position; + enum sndrv_seq_stop_mode stop_mode; + sndrv_seq_frequency_t frequency; + struct sndrv_seq_ev_volume volume; + struct sndrv_seq_ev_loop loop; + unsigned char raw8[8]; + } param; +}; + + + +/* INSTR_BEGIN event */ +struct sndrv_seq_ev_instr_begin { + int timeout; /* zero = forever, otherwise timeout in ms */ +}; + +struct sndrv_seq_result { + int event; /* processed event type */ + int result; +}; + + +struct sndrv_seq_real_time { + unsigned int tv_sec; /* seconds */ + unsigned int tv_nsec; /* nanoseconds */ +}; + +typedef unsigned int sndrv_seq_tick_time_t; /* midi ticks */ + +union sndrv_seq_timestamp { + sndrv_seq_tick_time_t tick; + struct sndrv_seq_real_time time; +}; + +struct sndrv_seq_queue_skew { + unsigned int value; + unsigned int base; +}; + + /* queue timer control */ +struct sndrv_seq_ev_queue_control { + unsigned char queue; /* affected queue */ + unsigned char pad[3]; /* reserved */ + union { + signed int value; /* affected value (e.g. tempo) */ + union sndrv_seq_timestamp time; /* time */ + unsigned int position; /* sync position */ + struct sndrv_seq_queue_skew skew; + unsigned int d32[2]; + unsigned char d8[8]; + } param; +}; + + /* quoted event - inside the kernel only */ +struct sndrv_seq_ev_quote { + struct sndrv_seq_addr origin; /* original sender */ + unsigned short value; /* optional data */ + struct sndrv_seq_event *event; /* quoted event */ +} __attribute__((packed)); + + + /* sequencer event */ +struct sndrv_seq_event { + sndrv_seq_event_type_t type; /* event type */ + unsigned char flags; /* event flags */ + char tag; + + unsigned char queue; /* schedule queue */ + union sndrv_seq_timestamp time; /* schedule time */ + + + struct sndrv_seq_addr source; /* source address */ + struct sndrv_seq_addr dest; /* destination address */ + + union { /* event data... */ + struct sndrv_seq_ev_note note; + struct sndrv_seq_ev_ctrl control; + struct sndrv_seq_ev_raw8 raw8; + struct sndrv_seq_ev_raw32 raw32; + struct sndrv_seq_ev_ext ext; + struct sndrv_seq_ev_queue_control queue; + union sndrv_seq_timestamp time; + struct sndrv_seq_addr addr; + struct sndrv_seq_connect connect; + struct sndrv_seq_result result; + struct sndrv_seq_ev_instr_begin instr_begin; + struct sndrv_seq_ev_sample_control sample; + struct sndrv_seq_ev_quote quote; + } data; +}; + + +/* + * bounce event - stored as variable size data + */ +struct sndrv_seq_event_bounce { + int err; + struct sndrv_seq_event event; + /* external data follows here. */ +}; + +#define sndrv_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(sndrv_seq_event_bounce_t))) + +/* + * type check macros + */ +/* result events: 0-4 */ +#define sndrv_seq_ev_is_result_type(ev) ((ev)->type < 5) +/* channel specific events: 5-19 */ +#define sndrv_seq_ev_is_channel_type(ev) ((ev)->type >= 5 && (ev)->type < 20) +/* note events: 5-9 */ +#define sndrv_seq_ev_is_note_type(ev) ((ev)->type >= 5 && (ev)->type < 10) +/* control events: 10-19 */ +#define sndrv_seq_ev_is_control_type(ev) ((ev)->type >= 10 && (ev)->type < 20) +/* queue control events: 30-39 */ +#define sndrv_seq_ev_is_queue_type(ev) ((ev)->type >= 30 && (ev)->type < 40) +/* system status messages */ +#define sndrv_seq_ev_is_message_type(ev) ((ev)->type >= 60 && (ev)->type < 69) +/* sample messages */ +#define sndrv_seq_ev_is_sample_type(ev) ((ev)->type >= 70 && (ev)->type < 79) +/* user-defined messages */ +#define sndrv_seq_ev_is_user_type(ev) ((ev)->type >= 90 && (ev)->type < 99) +/* fixed length events: 0-99 */ +#define sndrv_seq_ev_is_fixed_type(ev) ((ev)->type < 100) +/* instrument layer events: 100-129 */ +#define sndrv_seq_ev_is_instr_type(ev) ((ev)->type >= 100 && (ev)->type < 130) +/* variable length events: 130-139 */ +#define sndrv_seq_ev_is_variable_type(ev) ((ev)->type >= 130 && (ev)->type < 140) +/* reserved for kernel */ +#define sndrv_seq_ev_is_reserved(ev) ((ev)->type >= 150) + +/* direct dispatched events */ +#define sndrv_seq_ev_is_direct(ev) ((ev)->queue == SNDRV_SEQ_QUEUE_DIRECT) + +/* + * macros to check event flags + */ +/* prior events */ +#define sndrv_seq_ev_is_prior(ev) (((ev)->flags & SNDRV_SEQ_PRIORITY_MASK) == SNDRV_SEQ_PRIORITY_HIGH) + +/* event length type */ +#define sndrv_seq_ev_length_type(ev) ((ev)->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) +#define sndrv_seq_ev_is_fixed(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_FIXED) +#define sndrv_seq_ev_is_variable(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) +#define sndrv_seq_ev_is_varusr(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARUSR) + +/* time-stamp type */ +#define sndrv_seq_ev_timestamp_type(ev) ((ev)->flags & SNDRV_SEQ_TIME_STAMP_MASK) +#define sndrv_seq_ev_is_tick(ev) (sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_TICK) +#define sndrv_seq_ev_is_real(ev) (sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_REAL) + +/* time-mode type */ +#define sndrv_seq_ev_timemode_type(ev) ((ev)->flags & SNDRV_SEQ_TIME_MODE_MASK) +#define sndrv_seq_ev_is_abstime(ev) (sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS) +#define sndrv_seq_ev_is_reltime(ev) (sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL) + +/* queue sync port */ +#define sndrv_seq_queue_sync_port(q) ((q) + 16) + + /* system information */ +struct sndrv_seq_system_info { + int queues; /* maximum queues count */ + int clients; /* maximum clients count */ + int ports; /* maximum ports per client */ + int channels; /* maximum channels per port */ + int cur_clients; /* current clients */ + int cur_queues; /* current queues */ + char reserved[24]; +}; + + + /* system running information */ +struct sndrv_seq_running_info { + unsigned char client; /* client id */ + unsigned char big_endian; /* 1 = big-endian */ + unsigned char cpu_mode; /* 4 = 32bit, 8 = 64bit */ + unsigned char pad; /* reserved */ + unsigned char reserved[12]; +}; + + + /* known client numbers */ +#define SNDRV_SEQ_CLIENT_SYSTEM 0 +#define SNDRV_SEQ_CLIENT_DUMMY 62 /* dummy ports */ +#define SNDRV_SEQ_CLIENT_OSS 63 /* oss sequencer emulator */ + + + /* client types */ +enum sndrv_seq_client_type { + NO_CLIENT = 0, + USER_CLIENT = 1, + KERNEL_CLIENT = 2 +}; + + /* event filter flags */ +#define SNDRV_SEQ_FILTER_BROADCAST (1<<0) /* accept broadcast messages */ +#define SNDRV_SEQ_FILTER_MULTICAST (1<<1) /* accept multicast messages */ +#define SNDRV_SEQ_FILTER_BOUNCE (1<<2) /* accept bounce event in error */ +#define SNDRV_SEQ_FILTER_USE_EVENT (1<<31) /* use event filter */ + +struct sndrv_seq_client_info { + int client; /* client number to inquire */ + enum sndrv_seq_client_type type; /* client type */ + char name[64]; /* client name */ + unsigned int filter; /* filter flags */ + unsigned char multicast_filter[8]; /* multicast filter bitmap */ + unsigned char event_filter[32]; /* event filter bitmap */ + int num_ports; /* RO: number of ports */ + int event_lost; /* number of lost events */ + char reserved[64]; /* for future use */ +}; + + +/* client pool size */ +struct sndrv_seq_client_pool { + int client; /* client number to inquire */ + int output_pool; /* outgoing (write) pool size */ + int input_pool; /* incoming (read) pool size */ + int output_room; /* minimum free pool size for select/blocking mode */ + int output_free; /* unused size */ + int input_free; /* unused size */ + char reserved[64]; +}; + + +/* Remove events by specified criteria */ + +#define SNDRV_SEQ_REMOVE_INPUT (1<<0) /* Flush input queues */ +#define SNDRV_SEQ_REMOVE_OUTPUT (1<<1) /* Flush output queues */ +#define SNDRV_SEQ_REMOVE_DEST (1<<2) /* Restrict by destination q:client:port */ +#define SNDRV_SEQ_REMOVE_DEST_CHANNEL (1<<3) /* Restrict by channel */ +#define SNDRV_SEQ_REMOVE_TIME_BEFORE (1<<4) /* Restrict to before time */ +#define SNDRV_SEQ_REMOVE_TIME_AFTER (1<<5) /* Restrict to time or after */ +#define SNDRV_SEQ_REMOVE_TIME_TICK (1<<6) /* Time is in ticks */ +#define SNDRV_SEQ_REMOVE_EVENT_TYPE (1<<7) /* Restrict to event type */ +#define SNDRV_SEQ_REMOVE_IGNORE_OFF (1<<8) /* Do not flush off events */ +#define SNDRV_SEQ_REMOVE_TAG_MATCH (1<<9) /* Restrict to events with given tag */ + +struct sndrv_seq_remove_events { + unsigned int remove_mode; /* Flags that determine what gets removed */ + + union sndrv_seq_timestamp time; + + unsigned char queue; /* Queue for REMOVE_DEST */ + struct sndrv_seq_addr dest; /* Address for REMOVE_DEST */ + unsigned char channel; /* Channel for REMOVE_DEST */ + + int type; /* For REMOVE_EVENT_TYPE */ + char tag; /* Tag for REMOVE_TAG */ + + int reserved[10]; /* To allow for future binary compatibility */ + +}; + + + /* known port numbers */ +#define SNDRV_SEQ_PORT_SYSTEM_TIMER 0 +#define SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE 1 + + /* port capabilities (32 bits) */ +#define SNDRV_SEQ_PORT_CAP_READ (1<<0) /* readable from this port */ +#define SNDRV_SEQ_PORT_CAP_WRITE (1<<1) /* writable to this port */ + +#define SNDRV_SEQ_PORT_CAP_SYNC_READ (1<<2) +#define SNDRV_SEQ_PORT_CAP_SYNC_WRITE (1<<3) + +#define SNDRV_SEQ_PORT_CAP_DUPLEX (1<<4) + +#define SNDRV_SEQ_PORT_CAP_SUBS_READ (1<<5) /* allow read subscription */ +#define SNDRV_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /* allow write subscription */ +#define SNDRV_SEQ_PORT_CAP_NO_EXPORT (1<<7) /* routing not allowed */ + + /* port type */ +#define SNDRV_SEQ_PORT_TYPE_SPECIFIC (1<<0) /* hardware specific */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC (1<<1) /* generic MIDI device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GM (1<<2) /* General MIDI compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GS (1<<3) /* GS compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ + +/* other standards...*/ +#define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device */ +#define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11) /* Sampling device (support sample download) */ +#define SNDRV_SEQ_PORT_TYPE_SAMPLE (1<<12) /* Sampling device (sample can be downloaded at any time) */ +/*...*/ +#define SNDRV_SEQ_PORT_TYPE_APPLICATION (1<<20) /* application (sequencer/editor) */ + +/* misc. conditioning flags */ +#define SNDRV_SEQ_PORT_FLG_GIVEN_PORT (1<<0) + +struct sndrv_seq_port_info { + struct sndrv_seq_addr addr; /* client/port numbers */ + char name[64]; /* port name */ + + unsigned int capability; /* port capability bits */ + unsigned int type; /* port type bits */ + int midi_channels; /* channels per MIDI port */ + int midi_voices; /* voices per MIDI port */ + int synth_voices; /* voices per SYNTH port */ + + int read_use; /* R/O: subscribers for output (from this port) */ + int write_use; /* R/O: subscribers for input (to this port) */ + + void *kernel; /* reserved for kernel use (must be NULL) */ + unsigned int flags; /* misc. conditioning */ + char reserved[60]; /* for future use */ +}; + + +/* queue flags */ +#define SNDRV_SEQ_QUEUE_FLG_SYNC (1<<0) /* sync enabled */ + +/* queue information */ +struct sndrv_seq_queue_info { + int queue; /* queue id */ + + /* + * security settings, only owner of this queue can start/stop timer + * etc. if the queue is locked for other clients + */ + int owner; /* client id for owner of the queue */ + int locked:1; /* timing queue locked for other queues */ + char name[64]; /* name of this queue */ + unsigned int flags; /* flags */ + char reserved[60]; /* for future use */ + +}; + +/* queue info/status */ +struct sndrv_seq_queue_status { + int queue; /* queue id */ + int events; /* read-only - queue size */ + sndrv_seq_tick_time_t tick; /* current tick */ + struct sndrv_seq_real_time time; /* current time */ + int running; /* running state of queue */ + int flags; /* various flags */ + char reserved[64]; /* for the future */ +}; + + +/* queue tempo */ +struct sndrv_seq_queue_tempo { + int queue; /* sequencer queue */ + unsigned int tempo; /* current tempo, us/tick */ + int ppq; /* time resolution, ticks/quarter */ + unsigned int skew_value; /* queue skew */ + unsigned int skew_base; /* queue skew base */ + char reserved[24]; /* for the future */ +}; + + +/* sequencer timer sources */ +#define SNDRV_SEQ_TIMER_ALSA 0 /* ALSA timer */ +#define SNDRV_SEQ_TIMER_MIDI_CLOCK 1 /* Midi Clock (CLOCK event) */ +#define SNDRV_SEQ_TIMER_MIDI_TICK 2 /* Midi Timer Tick (TICK event) */ + +/* queue timer info */ +struct sndrv_seq_queue_timer { + int queue; /* sequencer queue */ + int type; /* source timer type */ + union { + struct { + struct sndrv_timer_id id; /* ALSA's timer ID */ + unsigned int resolution; /* resolution in Hz */ + } alsa; + } u; + char reserved[64]; /* for the future use */ +}; + + +struct sndrv_seq_queue_client { + int queue; /* sequencer queue */ + int client; /* sequencer client */ + int used; /* queue is used with this client + (must be set for accepting events) */ + /* per client watermarks */ + char reserved[64]; /* for future use */ +}; + + +#define SNDRV_SEQ_PORT_SUBS_EXCLUSIVE (1<<0) /* exclusive connection */ +#define SNDRV_SEQ_PORT_SUBS_TIMESTAMP (1<<1) +#define SNDRV_SEQ_PORT_SUBS_TIME_REAL (1<<2) + +struct sndrv_seq_port_subscribe { + struct sndrv_seq_addr sender; /* sender address */ + struct sndrv_seq_addr dest; /* destination address */ + unsigned int voices; /* number of voices to be allocated (0 = don't care) */ + unsigned int flags; /* modes */ + unsigned char queue; /* input time-stamp queue (optional) */ + unsigned char pad[3]; /* reserved */ + char reserved[64]; +}; + +/* type of query subscription */ +#define SNDRV_SEQ_QUERY_SUBS_READ 0 +#define SNDRV_SEQ_QUERY_SUBS_WRITE 1 + +struct sndrv_seq_query_subs { + struct sndrv_seq_addr root; /* client/port id to be searched */ + int type; /* READ or WRITE */ + int index; /* 0..N-1 */ + int num_subs; /* R/O: number of subscriptions on this port */ + struct sndrv_seq_addr addr; /* R/O: result */ + unsigned char queue; /* R/O: result */ + unsigned int flags; /* R/O: result */ + char reserved[64]; /* for future use */ +}; + + +/* + * Instrument abstraction layer + * - based on events + */ + +/* instrument types */ +#define SNDRV_SEQ_INSTR_ATYPE_DATA 0 /* instrument data */ +#define SNDRV_SEQ_INSTR_ATYPE_ALIAS 1 /* instrument alias */ + +/* instrument ASCII identifiers */ +#define SNDRV_SEQ_INSTR_ID_DLS1 "DLS1" +#define SNDRV_SEQ_INSTR_ID_DLS2 "DLS2" +#define SNDRV_SEQ_INSTR_ID_SIMPLE "Simple Wave" +#define SNDRV_SEQ_INSTR_ID_SOUNDFONT "SoundFont" +#define SNDRV_SEQ_INSTR_ID_GUS_PATCH "GUS Patch" +#define SNDRV_SEQ_INSTR_ID_INTERWAVE "InterWave FFFF" +#define SNDRV_SEQ_INSTR_ID_OPL2_3 "OPL2/3 FM" +#define SNDRV_SEQ_INSTR_ID_OPL4 "OPL4" + +/* instrument types */ +#define SNDRV_SEQ_INSTR_TYPE0_DLS1 (1<<0) /* MIDI DLS v1 */ +#define SNDRV_SEQ_INSTR_TYPE0_DLS2 (1<<1) /* MIDI DLS v2 */ +#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE (1<<0) /* Simple Wave */ +#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT (1<<1) /* EMU SoundFont */ +#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH (1<<2) /* Gravis UltraSound Patch */ +#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE (1<<3) /* InterWave FFFF */ +#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3 (1<<0) /* Yamaha OPL2/3 FM */ +#define SNDRV_SEQ_INSTR_TYPE2_OPL4 (1<<1) /* Yamaha OPL4 */ + +/* put commands */ +#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE 0 +#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE 1 +#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY 2 +#define SNDRV_SEQ_INSTR_PUT_CMD_ADD 3 +#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE 4 + +/* get commands */ +#define SNDRV_SEQ_INSTR_GET_CMD_FULL 0 +#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL 1 + +/* query flags */ +#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0) + +/* free commands */ +#define SNDRV_SEQ_INSTR_FREE_CMD_ALL 0 +#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE 1 +#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER 2 +#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE 3 + +/* size of ROM/RAM */ +typedef unsigned int sndrv_seq_instr_size_t; + +/* INSTR_INFO */ + +struct sndrv_seq_instr_info { + int result; /* operation result */ + unsigned int formats[8]; /* bitmap of supported formats */ + int ram_count; /* count of RAM banks */ + sndrv_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */ + int rom_count; /* count of ROM banks */ + sndrv_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */ + char reserved[128]; +}; + +/* INSTR_STATUS */ + +struct sndrv_seq_instr_status { + int result; /* operation result */ + sndrv_seq_instr_size_t free_ram[16]; /* free RAM in banks */ + int instrument_count; /* count of downloaded instruments */ + char reserved[128]; +}; + +/* INSTR_FORMAT_INFO */ + +struct sndrv_seq_instr_format_info { + char format[16]; /* format identifier - SNDRV_SEQ_INSTR_ID_* */ + unsigned int len; /* max data length (without this structure) */ +}; + +struct sndrv_seq_instr_format_info_result { + int result; /* operation result */ + char format[16]; /* format identifier */ + unsigned int len; /* filled data length (without this structure) */ +}; + +/* instrument data */ +struct sndrv_seq_instr_data { + char name[32]; /* instrument name */ + char reserved[16]; /* for the future use */ + int type; /* instrument type */ + union { + char format[16]; /* format identifier */ + struct sndrv_seq_instr alias; + } data; +}; + +/* INSTR_PUT/GET, data are stored in one block (extended), header + data */ + +struct sndrv_seq_instr_header { + union { + struct sndrv_seq_instr instr; + sndrv_seq_instr_cluster_t cluster; + } id; /* instrument identifier */ + unsigned int cmd; /* get/put/free command */ + unsigned int flags; /* query flags (only for get) */ + unsigned int len; /* real instrument data length (without header) */ + int result; /* operation result */ + char reserved[16]; /* for the future */ + struct sndrv_seq_instr_data data; /* instrument data (for put/get result) */ +}; + +/* INSTR_CLUSTER_SET */ + +struct sndrv_seq_instr_cluster_set { + sndrv_seq_instr_cluster_t cluster; /* cluster identifier */ + char name[32]; /* cluster name */ + int priority; /* cluster priority */ + char reserved[64]; /* for the future use */ +}; + +/* INSTR_CLUSTER_GET */ + +struct sndrv_seq_instr_cluster_get { + sndrv_seq_instr_cluster_t cluster; /* cluster identifier */ + char name[32]; /* cluster name */ + int priority; /* cluster priority */ + char reserved[64]; /* for the future use */ +}; + +/* + * IOCTL commands + */ + +#define SNDRV_SEQ_IOCTL_PVERSION _IOR ('S', 0x00, int) +#define SNDRV_SEQ_IOCTL_CLIENT_ID _IOR ('S', 0x01, int) +#define SNDRV_SEQ_IOCTL_SYSTEM_INFO _IOWR('S', 0x02, struct sndrv_seq_system_info) +#define SNDRV_SEQ_IOCTL_RUNNING_MODE _IOWR('S', 0x03, struct sndrv_seq_running_info) + +#define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO _IOWR('S', 0x10, struct sndrv_seq_client_info) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO _IOW ('S', 0x11, struct sndrv_seq_client_info) + +#define SNDRV_SEQ_IOCTL_CREATE_PORT _IOWR('S', 0x20, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_DELETE_PORT _IOW ('S', 0x21, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_GET_PORT_INFO _IOWR('S', 0x22, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_SET_PORT_INFO _IOW ('S', 0x23, struct sndrv_seq_port_info) + +#define SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT _IOW ('S', 0x30, struct sndrv_seq_port_subscribe) +#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT _IOW ('S', 0x31, struct sndrv_seq_port_subscribe) + +#define SNDRV_SEQ_IOCTL_CREATE_QUEUE _IOWR('S', 0x32, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_DELETE_QUEUE _IOW ('S', 0x33, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_INFO _IOWR('S', 0x34, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_INFO _IOWR('S', 0x35, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE _IOWR('S', 0x36, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct sndrv_seq_queue_status) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct sndrv_seq_queue_tempo) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct sndrv_seq_queue_tempo) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER _IOWR('S', 0x43, struct sndrv_seq_queue_owner) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER _IOW ('S', 0x44, struct sndrv_seq_queue_owner) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct sndrv_seq_queue_timer) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct sndrv_seq_queue_timer) +/* XXX +#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct sndrv_seq_queue_sync) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct sndrv_seq_queue_sync) +*/ +#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct sndrv_seq_queue_client) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct sndrv_seq_queue_client) +#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct sndrv_seq_client_pool) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_POOL _IOW ('S', 0x4c, struct sndrv_seq_client_pool) +#define SNDRV_SEQ_IOCTL_REMOVE_EVENTS _IOW ('S', 0x4e, struct sndrv_seq_remove_events) +#define SNDRV_SEQ_IOCTL_QUERY_SUBS _IOWR('S', 0x4f, struct sndrv_seq_query_subs) +#define SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION _IOWR('S', 0x50, struct sndrv_seq_port_subscribe) +#define SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT _IOWR('S', 0x51, struct sndrv_seq_client_info) +#define SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT _IOWR('S', 0x52, struct sndrv_seq_port_info) + +#endif /* __SOUND_ASEQUENCER_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/asound.h linux/include/sound/asound.h --- linux-2.4.21-rc1.orig/include/sound/asound.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/asound.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,889 @@ +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 1994-2003 by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_ASOUND_H +#define __SOUND_ASOUND_H + +#if defined(LINUX) || defined(__LINUX__) || defined(__linux__) + +#include + +#ifdef __KERNEL__ + +#include +#include +#include + +#if __LITTLE_ENDIAN == 1234 +#define SNDRV_LITTLE_ENDIAN +#elif __BIG_ENDIAN == 4321 +#define SNDRV_BIG_ENDIAN +#else +#error "Unsupported endian..." +#endif + +#else /* !__KERNEL__ */ + +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SNDRV_LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +#define SNDRV_BIG_ENDIAN +#else +#error "Unsupported endian..." +#endif + +#endif /* __KERNEL **/ + +#endif /* LINUX */ + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * protocol version + */ + +#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor)) +#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff) +#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff) +#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff) +#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) \ + (SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || \ + (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && \ + SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion))) + +/**************************************************************************** + * * + * Digital audio interface * + * * + ****************************************************************************/ + +struct sndrv_aes_iec958 { + unsigned char status[24]; /* AES/IEC958 channel status bits */ + unsigned char subcode[147]; /* AES/IEC958 subcode bits */ + unsigned char pad; /* nothing */ + unsigned char dig_subframe[4]; /* AES/IEC958 subframe bits */ +}; + +/**************************************************************************** + * * + * Section for driver hardware dependent interface - /dev/snd/hw? * + * * + ****************************************************************************/ + +#define SNDRV_HWDEP_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 1) + +enum sndrv_hwdep_iface { + SNDRV_HWDEP_IFACE_OPL2 = 0, + SNDRV_HWDEP_IFACE_OPL3, + SNDRV_HWDEP_IFACE_OPL4, + SNDRV_HWDEP_IFACE_SB16CSP, /* Creative Signal Processor */ + SNDRV_HWDEP_IFACE_EMU10K1, /* FX8010 processor in EMU10K1 chip */ + SNDRV_HWDEP_IFACE_YSS225, /* Yamaha FX processor */ + SNDRV_HWDEP_IFACE_ICS2115, /* Wavetable synth */ + SNDRV_HWDEP_IFACE_SSCAPE, /* Ensoniq SoundScape ISA card (MC68EC000) */ + SNDRV_HWDEP_IFACE_VX, /* Digigram VX cards */ + + /* Don't forget to change the following: */ + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_VX, +}; + +struct sndrv_hwdep_info { + unsigned int device; /* WR: device number */ + int card; /* R: card number */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* hwdep name */ + enum sndrv_hwdep_iface iface; /* hwdep interface */ + unsigned char reserved[64]; /* reserved for future */ +}; + +/* generic DSP loader */ +struct sndrv_hwdep_dsp_status { + unsigned int version; /* R: driver-specific version */ + unsigned char id[32]; /* R: driver-specific ID string */ + unsigned int num_dsps; /* R: number of DSP images to transfer */ + unsigned int dsp_loaded; /* R: bit flags indicating the loaded DSPs */ + unsigned int chip_ready; /* R: 1 = initialization finished */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +struct sndrv_hwdep_dsp_image { + unsigned int index; /* W: DSP index */ + unsigned char name[64]; /* W: ID (e.g. file name) */ + unsigned char *image; /* W: binary image */ + size_t length; /* W: size of image in bytes */ + unsigned long driver_data; /* W: driver-specific data */ +}; + +enum { + SNDRV_HWDEP_IOCTL_PVERSION = _IOR ('H', 0x00, int), + SNDRV_HWDEP_IOCTL_INFO = _IOR ('H', 0x01, struct sndrv_hwdep_info), + SNDRV_HWDEP_IOCTL_DSP_STATUS = _IOR('H', 0x02, struct sndrv_hwdep_dsp_status), + SNDRV_HWDEP_IOCTL_DSP_LOAD = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image) +}; + +/***************************************************************************** + * * + * Digital Audio (PCM) interface - /dev/snd/pcm?? * + * * + *****************************************************************************/ + +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 5) + +typedef unsigned long sndrv_pcm_uframes_t; +typedef long sndrv_pcm_sframes_t; + +enum sndrv_pcm_class { + SNDRV_PCM_CLASS_GENERIC = 0, /* standard mono or stereo device */ + SNDRV_PCM_CLASS_MULTI, /* multichannel device */ + SNDRV_PCM_CLASS_MODEM, /* software modem class */ + SNDRV_PCM_CLASS_DIGITIZER, /* digitizer class */ + /* Don't forget to change the following: */ + SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER, +}; + +enum sndrv_pcm_subclass { + SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, /* mono or stereo subdevices are mixed together */ + SNDRV_PCM_SUBCLASS_MULTI_MIX, /* multichannel subdevices are mixed together */ + /* Don't forget to change the following: */ + SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX, +}; + +enum sndrv_pcm_stream { + SNDRV_PCM_STREAM_PLAYBACK = 0, + SNDRV_PCM_STREAM_CAPTURE, + SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, +}; + +enum sndrv_pcm_access { + SNDRV_PCM_ACCESS_MMAP_INTERLEAVED = 0, /* interleaved mmap */ + SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED, /* noninterleaved mmap */ + SNDRV_PCM_ACCESS_MMAP_COMPLEX, /* complex mmap */ + SNDRV_PCM_ACCESS_RW_INTERLEAVED, /* readi/writei */ + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, /* readn/writen */ + SNDRV_PCM_ACCESS_LAST = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, +}; + +enum sndrv_pcm_format { + SNDRV_PCM_FORMAT_S8 = 0, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_S24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT_LE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT_BE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_LE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_BE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, /* IEC-958 subframe, Little Endian */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, /* IEC-958 subframe, Big Endian */ + SNDRV_PCM_FORMAT_MU_LAW, + SNDRV_PCM_FORMAT_A_LAW, + SNDRV_PCM_FORMAT_IMA_ADPCM, + SNDRV_PCM_FORMAT_MPEG, + SNDRV_PCM_FORMAT_GSM, + SNDRV_PCM_FORMAT_SPECIAL = 31, + SNDRV_PCM_FORMAT_S24_3LE = 32, /* in three bytes */ + SNDRV_PCM_FORMAT_S24_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_U24_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_U24_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_S20_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_S20_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_U20_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_U20_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_S18_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_S18_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_U18_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_U18_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_LAST = SNDRV_PCM_FORMAT_U18_3BE, + +#ifdef SNDRV_LITTLE_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_LE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_LE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, +#endif +#ifdef SNDRV_BIG_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_BE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_BE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif +}; + +enum sndrv_pcm_subformat { + SNDRV_PCM_SUBFORMAT_STD = 0, + SNDRV_PCM_SUBFORMAT_LAST = SNDRV_PCM_SUBFORMAT_STD, +}; + +#define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ +#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ +#define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ +#define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ +#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ +#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ +#define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ +#define SNDRV_PCM_INFO_BLOCK_TRANSFER 0x00010000 /* hardware transfer block of samples */ +#define SNDRV_PCM_INFO_OVERRANGE 0x00020000 /* hardware supports ADC (capture) overrange detection */ +#define SNDRV_PCM_INFO_RESUME 0x00040000 /* hardware supports stream resume after suspend */ +#define SNDRV_PCM_INFO_PAUSE 0x00080000 /* pause ioctl is supported */ +#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */ +#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ +#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ + +enum sndrv_pcm_state { + SNDRV_PCM_STATE_OPEN = 0, /* stream is open */ + SNDRV_PCM_STATE_SETUP, /* stream has a setup */ + SNDRV_PCM_STATE_PREPARED, /* stream is ready to start */ + SNDRV_PCM_STATE_RUNNING, /* stream is running */ + SNDRV_PCM_STATE_XRUN, /* stream reached an xrun */ + SNDRV_PCM_STATE_DRAINING, /* stream is draining */ + SNDRV_PCM_STATE_PAUSED, /* stream is paused */ + SNDRV_PCM_STATE_SUSPENDED, /* hardware is suspended */ + SNDRV_PCM_STATE_LAST = SNDRV_PCM_STATE_SUSPENDED, +}; + +enum { + SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000, + SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000, + SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000, +}; + +union sndrv_pcm_sync_id { + unsigned char id[16]; + unsigned short id16[8]; + unsigned int id32[4]; +}; + +struct sndrv_pcm_info { + unsigned int device; /* RO/WR (control): device number */ + unsigned int subdevice; /* RO/WR (control): subdevice number */ + enum sndrv_pcm_stream stream; /* RO/WR (control): stream number */ + int card; /* R: card number */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* name of this device */ + unsigned char subname[32]; /* subdevice name */ + enum sndrv_pcm_class dev_class; /* SNDRV_PCM_CLASS_* */ + enum sndrv_pcm_subclass dev_subclass; /* SNDRV_PCM_SUBCLASS_* */ + unsigned int subdevices_count; + unsigned int subdevices_avail; + union sndrv_pcm_sync_id sync; /* hardware synchronization ID */ + unsigned char reserved[64]; /* reserved for future... */ +}; + +enum sndrv_pcm_hw_param { + SNDRV_PCM_HW_PARAM_ACCESS = 0, /* Access type */ + SNDRV_PCM_HW_PARAM_FIRST_MASK = SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, /* Format */ + SNDRV_PCM_HW_PARAM_SUBFORMAT, /* Subformat */ + SNDRV_PCM_HW_PARAM_LAST_MASK = SNDRV_PCM_HW_PARAM_SUBFORMAT, + + SNDRV_PCM_HW_PARAM_SAMPLE_BITS = 8, /* Bits per sample */ + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + SNDRV_PCM_HW_PARAM_FRAME_BITS, /* Bits per frame */ + SNDRV_PCM_HW_PARAM_CHANNELS, /* Channels */ + SNDRV_PCM_HW_PARAM_RATE, /* Approx rate */ + SNDRV_PCM_HW_PARAM_PERIOD_TIME, /* Approx distance between interrupts + in us */ + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, /* Approx frames between interrupts */ + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, /* Approx bytes between interrupts */ + SNDRV_PCM_HW_PARAM_PERIODS, /* Approx interrupts per buffer */ + SNDRV_PCM_HW_PARAM_BUFFER_TIME, /* Approx duration of buffer in us */ + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, /* Size of buffer in frames */ + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, /* Size of buffer in bytes */ + SNDRV_PCM_HW_PARAM_TICK_TIME, /* Approx tick duration in us */ + SNDRV_PCM_HW_PARAM_LAST_INTERVAL = SNDRV_PCM_HW_PARAM_TICK_TIME +}; + +#define SNDRV_PCM_HW_PARAMS_RUNTIME (1<<0) + +struct sndrv_interval { + unsigned int min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +#define SNDRV_MASK_MAX 256 + +struct sndrv_mask { + u_int32_t bits[(SNDRV_MASK_MAX+31)/32]; +}; + +struct sndrv_pcm_hw_params { + unsigned int flags; + struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct sndrv_mask mres[5]; /* reserved masks */ + struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct sndrv_interval ires[9]; /* reserved intervals */ + unsigned int rmask; /* W: requested masks */ + unsigned int cmask; /* R: changed masks */ + unsigned int info; /* R: Info flags for returned setup */ + unsigned int msbits; /* R: used most significant bits */ + unsigned int rate_num; /* R: rate numerator */ + unsigned int rate_den; /* R: rate denominator */ + sndrv_pcm_uframes_t fifo_size; /* R: chip FIFO size in frames */ + unsigned char reserved[64]; /* reserved for future */ +}; + +enum sndrv_pcm_tstamp { + SNDRV_PCM_TSTAMP_NONE = 0, + SNDRV_PCM_TSTAMP_MMAP, + SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP, +}; + +struct sndrv_pcm_sw_params { + enum sndrv_pcm_tstamp tstamp_mode; /* timestamp mode */ + unsigned int period_step; + unsigned int sleep_min; /* min ticks to sleep */ + sndrv_pcm_uframes_t avail_min; /* min avail frames for wakeup */ + sndrv_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ + sndrv_pcm_uframes_t start_threshold; /* min hw_avail frames for automatic start */ + sndrv_pcm_uframes_t stop_threshold; /* min avail frames for automatic stop */ + sndrv_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ + sndrv_pcm_uframes_t silence_size; /* silence block size */ + sndrv_pcm_uframes_t boundary; /* pointers wrap point */ + unsigned char reserved[64]; /* reserved for future */ +}; + +struct sndrv_pcm_channel_info { + unsigned int channel; + off_t offset; /* mmap offset */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +}; + +struct sndrv_pcm_status { + enum sndrv_pcm_state state; /* stream state */ + struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ + struct timespec tstamp; /* reference timestamp */ + sndrv_pcm_uframes_t appl_ptr; /* appl ptr */ + sndrv_pcm_uframes_t hw_ptr; /* hw ptr */ + sndrv_pcm_sframes_t delay; /* current delay in frames */ + sndrv_pcm_uframes_t avail; /* number of frames available */ + sndrv_pcm_uframes_t avail_max; /* max frames available on hw since last status */ + sndrv_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ + enum sndrv_pcm_state suspended_state; /* suspended stream state */ + unsigned char reserved[60]; /* must be filled with zero */ +}; + +struct sndrv_pcm_mmap_status { + enum sndrv_pcm_state state; /* RO: state - SNDRV_PCM_STATE_XXXX */ + int pad1; /* Needed for 64 bit alignment */ + sndrv_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ + struct timespec tstamp; /* Timestamp */ + enum sndrv_pcm_state suspended_state; /* RO: suspended stream state */ +}; + +struct sndrv_pcm_mmap_control { + sndrv_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ + sndrv_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ +}; + +struct sndrv_xferi { + sndrv_pcm_sframes_t result; + void *buf; + sndrv_pcm_uframes_t frames; +}; + +struct sndrv_xfern { + sndrv_pcm_sframes_t result; + void **bufs; + sndrv_pcm_uframes_t frames; +}; + +enum { + SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), + SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info), + SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int), + SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params), + SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params), + SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), + SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct sndrv_pcm_sw_params), + SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct sndrv_pcm_status), + SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, sndrv_pcm_sframes_t), + SNDRV_PCM_IOCTL_HWSYNC = _IO('A', 0x22), + SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct sndrv_pcm_channel_info), + SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40), + SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41), + SNDRV_PCM_IOCTL_START = _IO('A', 0x42), + SNDRV_PCM_IOCTL_DROP = _IO('A', 0x43), + SNDRV_PCM_IOCTL_DRAIN = _IO('A', 0x44), + SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int), + SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, sndrv_pcm_uframes_t), + SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47), + SNDRV_PCM_IOCTL_XRUN = _IO('A', 0x48), + SNDRV_PCM_IOCTL_FORWARD = _IOW('A', 0x49, sndrv_pcm_uframes_t), + SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct sndrv_xferi), + SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct sndrv_xferi), + SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct sndrv_xfern), + SNDRV_PCM_IOCTL_READN_FRAMES = _IOR('A', 0x53, struct sndrv_xfern), + SNDRV_PCM_IOCTL_LINK = _IOW('A', 0x60, int), + SNDRV_PCM_IOCTL_UNLINK = _IO('A', 0x61), +}; + +/* Trick to make alsa-lib/acinclude.m4 happy */ +#define SNDRV_PCM_IOCTL_REWIND SNDRV_PCM_IOCTL_REWIND + +/***************************************************************************** + * * + * MIDI v1.0 interface * + * * + *****************************************************************************/ + +/* + * Raw MIDI section - /dev/snd/midi?? + */ + +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +enum sndrv_rawmidi_stream { + SNDRV_RAWMIDI_STREAM_OUTPUT = 0, + SNDRV_RAWMIDI_STREAM_INPUT, + SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT, +}; + +#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 +#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 +#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 + +struct sndrv_rawmidi_info { + unsigned int device; /* RO/WR (control): device number */ + unsigned int subdevice; /* RO/WR (control): subdevice number */ + enum sndrv_rawmidi_stream stream; /* WR: stream */ + int card; /* R: card number */ + unsigned int flags; /* SNDRV_RAWMIDI_INFO_XXXX */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* name of device */ + unsigned char subname[32]; /* name of active or selected subdevice */ + unsigned int subdevices_count; + unsigned int subdevices_avail; + unsigned char reserved[64]; /* reserved for future use */ +}; + +struct sndrv_rawmidi_params { + enum sndrv_rawmidi_stream stream; + size_t buffer_size; /* queue size in bytes */ + size_t avail_min; /* minimum avail bytes for wakeup */ + unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +struct sndrv_rawmidi_status { + enum sndrv_rawmidi_stream stream; + struct timespec tstamp; /* Timestamp */ + size_t avail; /* available bytes */ + size_t xruns; /* count of overruns since last status (in bytes) */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +enum { + SNDRV_RAWMIDI_IOCTL_PVERSION = _IOR('W', 0x00, int), + SNDRV_RAWMIDI_IOCTL_INFO = _IOR('W', 0x01, struct sndrv_rawmidi_info), + SNDRV_RAWMIDI_IOCTL_PARAMS = _IOWR('W', 0x10, struct sndrv_rawmidi_params), + SNDRV_RAWMIDI_IOCTL_STATUS = _IOWR('W', 0x20, struct sndrv_rawmidi_status), + SNDRV_RAWMIDI_IOCTL_DROP = _IOW('W', 0x30, int), + SNDRV_RAWMIDI_IOCTL_DRAIN = _IOW('W', 0x31, int), +}; + +/* + * Timer section - /dev/snd/timer + */ + +#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1) + +enum sndrv_timer_class { + SNDRV_TIMER_CLASS_NONE = -1, + SNDRV_TIMER_CLASS_SLAVE = 0, + SNDRV_TIMER_CLASS_GLOBAL, + SNDRV_TIMER_CLASS_CARD, + SNDRV_TIMER_CLASS_PCM, + SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM, +}; + +/* slave timer classes */ +enum sndrv_timer_slave_class { + SNDRV_TIMER_SCLASS_NONE = 0, + SNDRV_TIMER_SCLASS_APPLICATION, + SNDRV_TIMER_SCLASS_SEQUENCER, /* alias */ + SNDRV_TIMER_SCLASS_OSS_SEQUENCER, /* alias */ + SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, +}; + +/* global timers (device member) */ +#define SNDRV_TIMER_GLOBAL_SYSTEM 0 +#define SNDRV_TIMER_GLOBAL_RTC 1 + +/* info flags */ +#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */ + +struct sndrv_timer_id { + enum sndrv_timer_class dev_class; + enum sndrv_timer_slave_class dev_sclass; + int card; + int device; + int subdevice; +}; + +struct sndrv_timer_ginfo { + struct sndrv_timer_id tid; /* requested timer ID */ + unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */ + int card; /* card number */ + unsigned char id[64]; /* timer identification */ + unsigned char name[80]; /* timer name */ + unsigned long reserved0; /* reserved for future use */ + unsigned long resolution; /* average period resolution in ns */ + unsigned long resolution_min; /* minimal period resolution in ns */ + unsigned long resolution_max; /* maximal period resolution in ns */ + unsigned int clients; /* active timer clients */ + unsigned char reserved[32]; +}; + +struct sndrv_timer_gparams { + struct sndrv_timer_id tid; /* requested timer ID */ + unsigned long period_num; /* requested precise period duration (in seconds) - numerator */ + unsigned long period_den; /* requested precise period duration (in seconds) - denominator */ + unsigned char reserved[32]; +}; + +struct sndrv_timer_gstatus { + struct sndrv_timer_id tid; /* requested timer ID */ + unsigned long resolution; /* current period resolution in ns */ + unsigned long resolution_num; /* precise current period resolution (in seconds) - numerator */ + unsigned long resolution_den; /* precise current period resolution (in seconds) - denominator */ + unsigned char reserved[32]; +}; + +struct sndrv_timer_select { + struct sndrv_timer_id id; /* bind to timer ID */ + unsigned char reserved[32]; /* reserved */ +}; + +struct sndrv_timer_info { + unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */ + int card; /* card number */ + unsigned char id[64]; /* timer identificator */ + unsigned char name[80]; /* timer name */ + unsigned long reserved0; /* reserved for future use */ + unsigned long resolution; /* average period resolution in ns */ + unsigned char reserved[64]; /* reserved */ +}; + +#define SNDRV_TIMER_PSFLG_AUTO (1<<0) /* auto start, otherwise one-shot */ +#define SNDRV_TIMER_PSFLG_EXCLUSIVE (1<<1) /* exclusive use, precise start/stop/pause/continue */ + +struct sndrv_timer_params { + unsigned int flags; /* flags - SNDRV_MIXER_PSFLG_* */ + unsigned int ticks; /* requested resolution in ticks */ + unsigned int queue_size; /* total size of queue (32-1024) */ + unsigned int reserved0; /* reserved, was: failure locations */ + unsigned int filter; /* event filter (bitmask of SNDRV_TIMER_EVENT_*) */ + unsigned char reserved[60]; /* reserved */ +}; + +struct sndrv_timer_status { + struct timespec tstamp; /* Timestamp - last update */ + unsigned int resolution; /* current period resolution in ns */ + unsigned int lost; /* counter of master tick lost */ + unsigned int overrun; /* count of read queue overruns */ + unsigned int queue; /* used queue size */ + unsigned char reserved[64]; /* reserved */ +}; + +enum { + SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int), + SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct sndrv_timer_id), + SNDRV_TIMER_IOCTL_TREAD = _IOW('T', 0x02, int), + SNDRV_TIMER_IOCTL_GINFO = _IOWR('T', 0x03, struct sndrv_timer_ginfo), + SNDRV_TIMER_IOCTL_GPARAMS = _IOW('T', 0x04, struct sndrv_timer_gparams), + SNDRV_TIMER_IOCTL_GSTATUS = _IOWR('T', 0x05, struct sndrv_timer_gstatus), + SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct sndrv_timer_select), + SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info), + SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params), + SNDRV_TIMER_IOCTL_STATUS = _IOR('T', 0x14, struct sndrv_timer_status), + SNDRV_TIMER_IOCTL_START = _IO('T', 0x20), + SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21), + SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22), + SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0x23), +}; + +struct sndrv_timer_read { + unsigned int resolution; + unsigned int ticks; +}; + +enum sndrv_timer_event { + SNDRV_TIMER_EVENT_RESOLUTION = 0, /* val = resolution in ns */ + SNDRV_TIMER_EVENT_TICK, /* val = ticks */ + SNDRV_TIMER_EVENT_START, /* val = resolution in ns */ + SNDRV_TIMER_EVENT_STOP, /* val = 0 */ + SNDRV_TIMER_EVENT_CONTINUE, /* val = resolution in ns */ + SNDRV_TIMER_EVENT_PAUSE, /* val = 0 */ + /* master timer events for slave timer instances */ + SNDRV_TIMER_EVENT_MSTART = SNDRV_TIMER_EVENT_START + 10, + SNDRV_TIMER_EVENT_MSTOP = SNDRV_TIMER_EVENT_STOP + 10, + SNDRV_TIMER_EVENT_MCONTINUE = SNDRV_TIMER_EVENT_CONTINUE + 10, + SNDRV_TIMER_EVENT_MPAUSE = SNDRV_TIMER_EVENT_PAUSE + 10, +}; + +struct sndrv_timer_tread { + enum sndrv_timer_event event; + struct timespec tstamp; + unsigned int val; +}; + +/**************************************************************************** + * * + * Section for driver control interface - /dev/snd/control? * + * * + ****************************************************************************/ + +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1) + +struct sndrv_ctl_card_info { + int card; /* card number */ + int pad; /* reserved for future (was type) */ + unsigned char id[16]; /* ID of card (user selectable) */ + unsigned char driver[16]; /* Driver name */ + unsigned char name[32]; /* Short name of soundcard */ + unsigned char longname[80]; /* name + info text about soundcard */ + unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */ + unsigned char mixername[80]; /* visual mixer identification */ + unsigned char components[80]; /* card components / fine identification, delimited with one space (AC97 etc..) */ + unsigned char reserved[48]; /* reserved for future */ +}; + +enum sndrv_ctl_elem_type { + SNDRV_CTL_ELEM_TYPE_NONE = 0, /* invalid */ + SNDRV_CTL_ELEM_TYPE_BOOLEAN, /* boolean type */ + SNDRV_CTL_ELEM_TYPE_INTEGER, /* integer type */ + SNDRV_CTL_ELEM_TYPE_ENUMERATED, /* enumerated type */ + SNDRV_CTL_ELEM_TYPE_BYTES, /* byte array */ + SNDRV_CTL_ELEM_TYPE_IEC958, /* IEC958 (S/PDIF) setup */ + SNDRV_CTL_ELEM_TYPE_INTEGER64, /* 64-bit integer type */ + SNDRV_CTL_ELEM_TYPE_LAST = SNDRV_CTL_ELEM_TYPE_INTEGER64, +}; + +enum sndrv_ctl_elem_iface { + SNDRV_CTL_ELEM_IFACE_CARD = 0, /* global control */ + SNDRV_CTL_ELEM_IFACE_HWDEP, /* hardware dependent device */ + SNDRV_CTL_ELEM_IFACE_MIXER, /* virtual mixer device */ + SNDRV_CTL_ELEM_IFACE_PCM, /* PCM device */ + SNDRV_CTL_ELEM_IFACE_RAWMIDI, /* RawMidi device */ + SNDRV_CTL_ELEM_IFACE_TIMER, /* timer device */ + SNDRV_CTL_ELEM_IFACE_SEQUENCER, /* sequencer client */ + SNDRV_CTL_ELEM_IFACE_LAST = SNDRV_CTL_ELEM_IFACE_SEQUENCER, +}; + +#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) +#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) +#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ +#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<2) /* when was control changed */ +#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ +#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ +#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ +#define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access */ + +/* for further details see the ACPI and PCI power management specification */ +#define SNDRV_CTL_POWER_D0 0x0000 /* full On */ +#define SNDRV_CTL_POWER_D1 0x0100 /* partial On */ +#define SNDRV_CTL_POWER_D2 0x0200 /* partial On */ +#define SNDRV_CTL_POWER_D3 0x0300 /* Off */ +#define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) /* Off, with power */ +#define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) /* Off, without power */ + +struct sndrv_ctl_elem_id { + unsigned int numid; /* numeric identifier, zero = invalid */ + enum sndrv_ctl_elem_iface iface; /* interface identifier */ + unsigned int device; /* device/client number */ + unsigned int subdevice; /* subdevice (substream) number */ + unsigned char name[44]; /* ASCII name of item */ + unsigned int index; /* index of item */ +}; + +struct sndrv_ctl_elem_list { + unsigned int offset; /* W: first element ID to get */ + unsigned int space; /* W: count of element IDs to get */ + unsigned int used; /* R: count of element IDs set */ + unsigned int count; /* R: count of all elements */ + struct sndrv_ctl_elem_id *pids; /* R: IDs */ + unsigned char reserved[50]; +}; + +struct sndrv_ctl_elem_info { + struct sndrv_ctl_elem_id id; /* W: element ID */ + enum sndrv_ctl_elem_type type; /* R: value type - SNDRV_CTL_ELEM_TYPE_* */ + unsigned int access; /* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */ + unsigned int count; /* count of values */ + pid_t owner; /* owner's PID of this control */ + union { + struct { + long min; /* R: minimum value */ + long max; /* R: maximum value */ + long step; /* R: step (0 variable) */ + } integer; + struct { + long long min; /* R: minimum value */ + long long max; /* R: maximum value */ + long long step; /* R: step (0 variable) */ + } integer64; + struct { + unsigned int items; /* R: number of items */ + unsigned int item; /* W: item number */ + char name[64]; /* R: value name */ + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +}; + +struct sndrv_ctl_elem_value { + struct sndrv_ctl_elem_id id; /* W: element ID */ + unsigned int indirect: 1; /* W: use indirect pointer (xxx_ptr member) */ + union { + union { + long value[128]; + long *value_ptr; + } integer; + union { + long long value[64]; + long long *value_ptr; + } integer64; + union { + unsigned int item[128]; + unsigned int *item_ptr; + } enumerated; + union { + unsigned char data[512]; + unsigned char *data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; /* RO */ + struct timespec tstamp; + unsigned char reserved[128-sizeof(struct timespec)]; +}; + +enum { + SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), + SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), + SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct sndrv_ctl_elem_list), + SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct sndrv_ctl_elem_info), + SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct sndrv_ctl_elem_value), + SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct sndrv_ctl_elem_value), + SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int), + SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), + SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), + SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), + SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct sndrv_pcm_info), + SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int), + SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int), + SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct sndrv_rawmidi_info), + SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int), + SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int), + SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int), +}; + +/* + * Read interface. + */ + +enum sndrv_ctl_event_type { + SNDRV_CTL_EVENT_ELEM = 0, + SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM, +}; + +#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */ +#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */ +#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */ +#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */ + +struct sndrv_ctl_event { + enum sndrv_ctl_event_type type; /* event type - SNDRV_CTL_EVENT_* */ + union { + struct { + unsigned int mask; + struct sndrv_ctl_elem_id id; + } elem; + unsigned char data8[60]; + } data; +}; + +/* + * Control names + */ + +#define SNDRV_CTL_NAME_NONE "" +#define SNDRV_CTL_NAME_PLAYBACK "Playback " +#define SNDRV_CTL_NAME_CAPTURE "Capture " + +#define SNDRV_CTL_NAME_IEC958_NONE "" +#define SNDRV_CTL_NAME_IEC958_SWITCH "Switch" +#define SNDRV_CTL_NAME_IEC958_VOLUME "Volume" +#define SNDRV_CTL_NAME_IEC958_DEFAULT "Default" +#define SNDRV_CTL_NAME_IEC958_MASK "Mask" +#define SNDRV_CTL_NAME_IEC958_CON_MASK "Con Mask" +#define SNDRV_CTL_NAME_IEC958_PRO_MASK "Pro Mask" +#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream" +#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what + +/* + * + */ + +struct sndrv_xferv { + const struct iovec *vector; + unsigned long count; +}; + +enum { + SNDRV_IOCTL_READV = _IOW('K', 0x00, struct sndrv_xferv), + SNDRV_IOCTL_WRITEV = _IOW('K', 0x01, struct sndrv_xferv), +}; + +#endif /* __SOUND_ASOUND_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/asound_fm.h linux/include/sound/asound_fm.h --- linux-2.4.21-rc1.orig/include/sound/asound_fm.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/asound_fm.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,119 @@ +#ifndef __SOUND_ASOUND_FM_H +#define __SOUND_ASOUND_FM_H + +/* + * Advanced Linux Sound Architecture - ALSA + * + * Interface file between ALSA driver & user space + * Copyright (c) 1994-98 by Jaroslav Kysela , + * 4Front Technologies + * + * Direct FM control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_DM_FM_MODE_OPL2 0x00 +#define SNDRV_DM_FM_MODE_OPL3 0x01 + +typedef struct snd_dm_fm_info { + unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ + unsigned char rhythm; /* percussion mode flag */ +} snd_dm_fm_info_t; + +/* + * Data structure composing an FM "note" or sound event. + */ + +typedef struct snd_dm_fm_voice { + unsigned char op; /* operator cell (0 or 1) */ + unsigned char voice; /* FM voice (0 to 17) */ + + unsigned char am; /* amplitude modulation */ + unsigned char vibrato; /* vibrato effect */ + unsigned char do_sustain; /* sustain phase */ + unsigned char kbd_scale; /* keyboard scaling */ + unsigned char harmonic; /* 4 bits: harmonic and multiplier */ + unsigned char scale_level; /* 2 bits: decrease output freq rises */ + unsigned char volume; /* 6 bits: volume */ + + unsigned char attack; /* 4 bits: attack rate */ + unsigned char decay; /* 4 bits: decay rate */ + unsigned char sustain; /* 4 bits: sustain level */ + unsigned char release; /* 4 bits: release rate */ + + unsigned char feedback; /* 3 bits: feedback for op0 */ + unsigned char connection; /* 0 for serial, 1 for parallel */ + unsigned char left; /* stereo left */ + unsigned char right; /* stereo right */ + unsigned char waveform; /* 3 bits: waveform shape */ +} snd_dm_fm_voice_t; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +typedef struct snd_dm_fm_note { + unsigned char voice; /* 0-17 voice channel */ + unsigned char octave; /* 3 bits: what octave to play */ + unsigned int fnum; /* 10 bits: frequency number */ + unsigned char key_on; /* set for active, clear for silent */ +} snd_dm_fm_note_t; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +typedef struct snd_dm_fm_params { + unsigned char am_depth; /* amplitude modulation depth (1=hi) */ + unsigned char vib_depth; /* vibrato depth (1=hi) */ + unsigned char kbd_split; /* keyboard split */ + unsigned char rhythm; /* percussion mode select */ + + /* This block is the percussion instrument data */ + unsigned char bass; + unsigned char snare; + unsigned char tomtom; + unsigned char cymbal; + unsigned char hihat; +} snd_dm_fm_params_t; + +/* + * FM mode ioctl settings + */ + +#define SNDRV_DM_FM_IOCTL_INFO _IOR('H', 0x20, snd_dm_fm_info_t) +#define SNDRV_DM_FM_IOCTL_RESET _IO ('H', 0x21) +#define SNDRV_DM_FM_IOCTL_PLAY_NOTE _IOW('H', 0x22, snd_dm_fm_note_t) +#define SNDRV_DM_FM_IOCTL_SET_VOICE _IOW('H', 0x23, snd_dm_fm_voice_t) +#define SNDRV_DM_FM_IOCTL_SET_PARAMS _IOW('H', 0x24, snd_dm_fm_params_t) +#define SNDRV_DM_FM_IOCTL_SET_MODE _IOW('H', 0x25, int) +/* for OPL3 only */ +#define SNDRV_DM_FM_IOCTL_SET_CONNECTION _IOW('H', 0x26, int) + +#ifdef __SND_OSS_COMPAT__ + +#define SNDRV_DM_FM_OSS_IOCTL_RESET 0x20 +#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE 0x21 +#define SNDRV_DM_FM_OSS_IOCTL_SET_VOICE 0x22 +#define SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS 0x23 +#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE 0x24 +#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL 0x25 + +#endif + +#endif /* __SOUND_ASOUND_FM_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/asoundef.h linux/include/sound/asoundef.h --- linux-2.4.21-rc1.orig/include/sound/asoundef.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/asoundef.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,227 @@ +#ifndef __SOUND_ASOUNDEF_H +#define __SOUND_ASOUNDEF_H + +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/**************************************************************************** + * * + * Digital audio interface * + * * + ****************************************************************************/ + +/* AES/IEC958 channel status bits */ +#define IEC958_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ +#define IEC958_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ +#define IEC958_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define IEC958_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ +#define IEC958_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ +#define IEC958_AES0_PRO_FS (3<<6) /* mask - sample frequency */ +#define IEC958_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ +#define IEC958_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ +#define IEC958_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ +#define IEC958_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ +#define IEC958_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ +#define IEC958_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ +#define IEC958_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ +#define IEC958_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ +#define IEC958_AES0_CON_MODE (3<<6) /* mask - mode */ +#define IEC958_AES1_PRO_MODE (15<<0) /* mask - channel mode */ +#define IEC958_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ +#define IEC958_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ +#define IEC958_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ +#define IEC958_AES1_PRO_MODE_TWO (8<<0) /* two channels */ +#define IEC958_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ +#define IEC958_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ +#define IEC958_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ +#define IEC958_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ +#define IEC958_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ +#define IEC958_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ +#define IEC958_AES1_CON_CATEGORY 0x7f +#define IEC958_AES1_CON_GENERAL 0x00 +#define IEC958_AES1_CON_EXPERIMENTAL 0x40 +#define IEC958_AES1_CON_SOLIDMEM_MASK 0x0f +#define IEC958_AES1_CON_SOLIDMEM_ID 0x08 +#define IEC958_AES1_CON_BROADCAST1_MASK 0x07 +#define IEC958_AES1_CON_BROADCAST1_ID 0x04 +#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07 +#define IEC958_AES1_CON_DIGDIGCONV_ID 0x02 +#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f +#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x06 +#define IEC958_AES1_CON_ADC_MASK 0x1f +#define IEC958_AES1_CON_ADC_ID 0x16 +#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f +#define IEC958_AES1_CON_BROADCAST2_ID 0x0e +#define IEC958_AES1_CON_LASEROPT_MASK 0x07 +#define IEC958_AES1_CON_LASEROPT_ID 0x01 +#define IEC958_AES1_CON_MUSICAL_MASK 0x07 +#define IEC958_AES1_CON_MUSICAL_ID 0x05 +#define IEC958_AES1_CON_MAGNETIC_MASK 0x07 +#define IEC958_AES1_CON_MAGNETIC_ID 0x03 +#define IEC958_AES1_CON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x00) +#define IEC958_AES1_CON_NON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x08) +#define IEC958_AES1_CON_PCM_CODER (IEC958_AES1_CON_DIGDIGCONV_ID|0x00) +#define IEC958_AES1_CON_SAMPLER (IEC958_AES1_CON_DIGDIGCONV_ID|0x20) +#define IEC958_AES1_CON_MIXER (IEC958_AES1_CON_DIGDIGCONV_ID|0x10) +#define IEC958_AES1_CON_RATE_CONVERTER (IEC958_AES1_CON_DIGDIGCONV_ID|0x18) +#define IEC958_AES1_CON_SYNTHESIZER (IEC958_AES1_CON_MUSICAL_ID|0x00) +#define IEC958_AES1_CON_MICROPHONE (IEC958_AES1_CON_MUSICAL_ID|0x08) +#define IEC958_AES1_CON_DAT (IEC958_AES1_CON_MAGNETIC_ID|0x00) +#define IEC958_AES1_CON_VCR (IEC958_AES1_CON_MAGNETIC_ID|0x08) +#define IEC958_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ +#define IEC958_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ +#define IEC958_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ +#define IEC958_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ +#define IEC958_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ +#define IEC958_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ +#define IEC958_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ +#define IEC958_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ +#define IEC958_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ +#define IEC958_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ +#define IEC958_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ +#define IEC958_AES2_CON_SOURCE (15<<0) /* mask - source number */ +#define IEC958_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ +#define IEC958_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ +#define IEC958_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ +#define IEC958_AES3_CON_FS (15<<0) /* mask - sample frequency */ +#define IEC958_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ +#define IEC958_AES3_CON_FS_48000 (2<<0) /* 48kHz */ +#define IEC958_AES3_CON_FS_32000 (3<<0) /* 32kHz */ +#define IEC958_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ +#define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ +#define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ +#define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ + +/***************************************************************************** + * * + * MIDI v1.0 interface * + * * + *****************************************************************************/ + +#define MIDI_CHANNELS 16 +#define MIDI_GM_DRUM_CHANNEL (10-1) + +/* + * MIDI commands + */ + +#define MIDI_CMD_NOTE_OFF 0x80 +#define MIDI_CMD_NOTE_ON 0x90 +#define MIDI_CMD_NOTE_PRESSURE 0xa0 +#define MIDI_CMD_CONTROL 0xb0 +#define MIDI_CMD_PGM_CHANGE 0xc0 +#define MIDI_CMD_CHANNEL_PRESSURE 0xd0 +#define MIDI_CMD_BENDER 0xe0 + +#define MIDI_CMD_COMMON_SYSEX 0xf0 +#define MIDI_CMD_COMMON_MTC_QUARTER 0xf1 +#define MIDI_CMD_COMMON_SONG_POS 0xf2 +#define MIDI_CMD_COMMON_SONG_SELECT 0xf3 +#define MIDI_CMD_COMMON_TUNE_REQUEST 0xf6 +#define MIDI_CMD_COMMON_SYSEX_END 0xf7 +#define MIDI_CMD_COMMON_CLOCK 0xf8 +#define MIDI_CMD_COMMON_START 0xfa +#define MIDI_CMD_COMMON_CONTINUE 0xfb +#define MIDI_CMD_COMMON_STOP 0xfc +#define MIDI_CMD_COMMON_SENSING 0xfe +#define MIDI_CMD_COMMON_RESET 0xff + +/* + * MIDI controllers + */ + +#define MIDI_CTL_MSB_BANK 0x00 +#define MIDI_CTL_MSB_MODWHEEL 0x01 +#define MIDI_CTL_MSB_BREATH 0x02 +#define MIDI_CTL_MSB_FOOT 0x04 +#define MIDI_CTL_MSB_PORTAMENTO_TIME 0x05 +#define MIDI_CTL_MSB_DATA_ENTRY 0x06 +#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 +#define MIDI_CTL_MSB_BALANCE 0x08 +#define MIDI_CTL_MSB_PAN 0x0a +#define MIDI_CTL_MSB_EXPRESSION 0x0b +#define MIDI_CTL_MSB_EFFECT1 0x0c +#define MIDI_CTL_MSB_EFFECT2 0x0d +#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 +#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 +#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 +#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 +#define MIDI_CTL_LSB_BANK 0x20 +#define MIDI_CTL_LSB_MODWHEEL 0x21 +#define MIDI_CTL_LSB_BREATH 0x22 +#define MIDI_CTL_LSB_FOOT 0x24 +#define MIDI_CTL_LSB_PORTAMENTO_TIME 0x25 +#define MIDI_CTL_LSB_DATA_ENTRY 0x26 +#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 +#define MIDI_CTL_LSB_BALANCE 0x28 +#define MIDI_CTL_LSB_PAN 0x2a +#define MIDI_CTL_LSB_EXPRESSION 0x2b +#define MIDI_CTL_LSB_EFFECT1 0x2c +#define MIDI_CTL_LSB_EFFECT2 0x2d +#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 +#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 +#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 +#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 +#define MIDI_CTL_SUSTAIN 0x40 +#define MIDI_CTL_PORTAMENTO 0x41 +#define MIDI_CTL_SUSTENUTO 0x42 +#define MIDI_CTL_SOFT_PEDAL 0x43 +#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 +#define MIDI_CTL_HOLD2 0x45 +#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 +#define MIDI_CTL_SC2_TIMBRE 0x47 +#define MIDI_CTL_SC3_RELEASE_TIME 0x48 +#define MIDI_CTL_SC4_ATTACK_TIME 0x49 +#define MIDI_CTL_SC5_BRIGHTNESS 0x4a +#define MIDI_CTL_SC6 0x4b +#define MIDI_CTL_SC7 0x4c +#define MIDI_CTL_SC8 0x4d +#define MIDI_CTL_SC9 0x4e +#define MIDI_CTL_SC10 0x4f +#define MIDI_CTL_GENERAL_PURPOSE5 0x50 +#define MIDI_CTL_GENERAL_PURPOSE6 0x51 +#define MIDI_CTL_GENERAL_PURPOSE7 0x52 +#define MIDI_CTL_GENERAL_PURPOSE8 0x53 +#define MIDI_CTL_PORTAMENTO_CONTROL 0x54 +#define MIDI_CTL_E1_REVERB_DEPTH 0x5b +#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5c +#define MIDI_CTL_E3_CHORUS_DEPTH 0x5d +#define MIDI_CTL_E4_DETUNE_DEPTH 0x5e +#define MIDI_CTL_E5_PHASER_DEPTH 0x5f +#define MIDI_CTL_DATA_INCREMENT 0x60 +#define MIDI_CTL_DATA_DECREMENT 0x61 +#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 +#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 +#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 +#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 +#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 +#define MIDI_CTL_RESET_CONTROLLERS 0x79 +#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7a +#define MIDI_CTL_ALL_NOTES_OFF 0x7b +#define MIDI_CTL_OMNI_OFF 0x7c +#define MIDI_CTL_OMNI_ON 0x7d +#define MIDI_CTL_MONO1 0x7e +#define MIDI_CTL_MONO2 0x7f + +#endif /* __SOUND_ASOUNDEF_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/control.h linux/include/sound/control.h --- linux-2.4.21-rc1.orig/include/sound/control.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/control.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,120 @@ +#ifndef __SOUND_CONTROL_H +#define __SOUND_CONTROL_H + +/* + * Header file for control interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +typedef struct sndrv_aes_iec958 snd_aes_iec958_t; +typedef struct sndrv_ctl_card_info snd_ctl_card_info_t; +typedef enum sndrv_ctl_elem_type snd_ctl_elem_type_t; +typedef enum sndrv_ctl_elem_iface snd_ctl_elem_iface_t; +typedef struct sndrv_ctl_elem_id snd_ctl_elem_id_t; +typedef struct sndrv_ctl_elem_list snd_ctl_elem_list_t; +typedef struct sndrv_ctl_elem_info snd_ctl_elem_info_t; +typedef struct sndrv_ctl_elem_value snd_ctl_elem_value_t; +typedef enum sndrv_ctl_event_type snd_ctl_event_type_t; +typedef struct sndrv_ctl_event snd_ctl_event_t; + +#define _snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) +#define snd_kcontrol_chip(kcontrol) snd_magic_cast1(chip_t, _snd_kcontrol_chip(kcontrol), return -ENXIO) + +typedef int (snd_kcontrol_info_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo); +typedef int (snd_kcontrol_get_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +typedef int (snd_kcontrol_put_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +typedef struct _snd_kcontrol_new { + snd_ctl_elem_iface_t iface; /* interface identifier */ + unsigned int device; /* device/client number */ + unsigned int subdevice; /* subdevice (substream) number */ + unsigned char *name; /* ASCII name of item */ + unsigned int index; /* index of item */ + unsigned int access; /* access rights */ + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + unsigned long private_value; +} snd_kcontrol_new_t; + +struct _snd_kcontrol { + struct list_head list; /* list of controls */ + snd_ctl_elem_id_t id; + snd_ctl_file_t *owner; /* locked */ + pid_t owner_pid; + unsigned int access; /* access rights */ + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + unsigned long private_value; + void *private_data; + void (*private_free)(snd_kcontrol_t *kcontrol); +}; + +#define snd_kcontrol(n) list_entry(n, snd_kcontrol_t, list) + +typedef struct _snd_kctl_event { + struct list_head list; /* list of events */ + snd_ctl_elem_id_t id; + unsigned int mask; +} snd_kctl_event_t; + +#define snd_kctl_event(n) list_entry(n, snd_kctl_event_t, list) + +struct _snd_ctl_file { + struct list_head list; /* list of all control files */ + snd_card_t *card; + pid_t pid; + int prefer_pcm_subdevice; + int prefer_rawmidi_subdevice; + wait_queue_head_t change_sleep; + spinlock_t read_lock; + struct fasync_struct *fasync; + int subscribed; /* read interface is activated */ + struct list_head events; /* waiting events for read */ +}; + +#define snd_ctl_file(n) list_entry(n, snd_ctl_file_t, list) + +typedef int (*snd_kctl_ioctl_func_t) (snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg); + +void snd_ctl_notify(snd_card_t * card, unsigned int mask, snd_ctl_elem_id_t * id); + +snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * kcontrol); +snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * kcontrolnew, void * private_data); +void snd_ctl_free_one(snd_kcontrol_t * kcontrol); +int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol); +int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol); +int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id); +int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id); +snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid); +snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id); + +int snd_ctl_register(snd_card_t *card); +int snd_ctl_disconnect(snd_card_t *card); +int snd_ctl_can_unregister(snd_card_t *card); +int snd_ctl_unregister(snd_card_t *card); +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); + +#endif /* __SOUND_CONTROL_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/core.h linux/include/sound/core.h --- linux-2.4.21-rc1.orig/include/sound/core.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/core.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,451 @@ +#ifndef __SOUND_CORE_H +#define __SOUND_CORE_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2001 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include /* wake_up() */ +#include /* struct semaphore */ +#include /* struct rw_semaphore */ + +/* Typedef's */ +typedef struct timespec snd_timestamp_t; +typedef struct sndrv_interval snd_interval_t; +typedef enum sndrv_card_type snd_card_type; +typedef struct sndrv_xferi snd_xferi_t; +typedef struct sndrv_xfern snd_xfern_t; +typedef struct sndrv_xferv snd_xferv_t; + +/* forward declarations */ +#ifdef CONFIG_PCI +struct pci_dev; +#endif +#ifdef CONFIG_SBUS +struct sbus_dev; +#endif + +/* device allocation stuff */ + +#define SNDRV_DEV_TYPE_RANGE_SIZE 0x1000 + +typedef enum { + SNDRV_DEV_TOPLEVEL = (0*SNDRV_DEV_TYPE_RANGE_SIZE), + SNDRV_DEV_LOWLEVEL_PRE, + SNDRV_DEV_LOWLEVEL_NORMAL = (1*SNDRV_DEV_TYPE_RANGE_SIZE), + SNDRV_DEV_PCM, + SNDRV_DEV_RAWMIDI, + SNDRV_DEV_TIMER, + SNDRV_DEV_SEQUENCER, + SNDRV_DEV_HWDEP, + SNDRV_DEV_INFO, + SNDRV_DEV_LOWLEVEL = (2*SNDRV_DEV_TYPE_RANGE_SIZE) +} snd_device_type_t; + +typedef enum { + SNDRV_DEV_BUILD, + SNDRV_DEV_REGISTERED, + SNDRV_DEV_DISCONNECTED +} snd_device_state_t; + +typedef enum { + SNDRV_DEV_CMD_PRE = 0, + SNDRV_DEV_CMD_NORMAL = 1, + SNDRV_DEV_CMD_POST = 2 +} snd_device_cmd_t; + +typedef struct _snd_card snd_card_t; +typedef struct _snd_device snd_device_t; + +typedef int (snd_dev_free_t)(snd_device_t *device); +typedef int (snd_dev_register_t)(snd_device_t *device); +typedef int (snd_dev_disconnect_t)(snd_device_t *device); +typedef int (snd_dev_unregister_t)(snd_device_t *device); + +typedef struct { + snd_dev_free_t *dev_free; + snd_dev_register_t *dev_register; + snd_dev_disconnect_t *dev_disconnect; + snd_dev_unregister_t *dev_unregister; +} snd_device_ops_t; + +struct _snd_device { + struct list_head list; /* list of registered devices */ + snd_card_t *card; /* card which holds this device */ + snd_device_state_t state; /* state of the device */ + snd_device_type_t type; /* device type */ + void *device_data; /* device structure */ + snd_device_ops_t *ops; /* operations */ +}; + +#define snd_device(n) list_entry(n, snd_device_t, list) + +/* various typedefs */ + +typedef struct snd_info_entry snd_info_entry_t; +typedef struct _snd_pcm snd_pcm_t; +typedef struct _snd_pcm_str snd_pcm_str_t; +typedef struct _snd_pcm_substream snd_pcm_substream_t; +typedef struct _snd_mixer snd_kmixer_t; +typedef struct _snd_rawmidi snd_rawmidi_t; +typedef struct _snd_ctl_file snd_ctl_file_t; +typedef struct _snd_kcontrol snd_kcontrol_t; +typedef struct _snd_timer snd_timer_t; +typedef struct _snd_timer_instance snd_timer_instance_t; +typedef struct _snd_hwdep snd_hwdep_t; +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +typedef struct _snd_oss_mixer snd_mixer_oss_t; +#endif + +/* monitor files for graceful shutdown (hotplug) */ + +struct snd_monitor_file { + struct file *file; + struct snd_monitor_file *next; +}; + +struct snd_shutdown_f_ops; /* define it later */ + +/* main structure for soundcard */ + +struct _snd_card { + int number; /* number of soundcard (index to snd_cards) */ + + char id[16]; /* id string of this card */ + char driver[16]; /* driver name */ + char shortname[32]; /* short name of this soundcard */ + char longname[80]; /* name of this soundcard */ + char mixername[80]; /* mixer name */ + char components[80]; /* card components delimited with space */ + + struct module *module; /* top-level module */ + + void *private_data; /* private data for soundcard */ + void (*private_free) (snd_card_t *card); /* callback for freeing of private data */ + + struct list_head devices; /* devices */ + + unsigned int last_numid; /* last used numeric ID */ + struct rw_semaphore controls_rwsem; /* controls list lock */ + rwlock_t ctl_files_rwlock; /* ctl_files list lock */ + int controls_count; /* count of all controls */ + struct list_head controls; /* all controls for this card */ + struct list_head ctl_files; /* active control files */ + + snd_info_entry_t *proc_root; /* root for soundcard specific files */ + snd_info_entry_t *proc_id; /* the card id */ + struct proc_dir_entry *proc_root_link; /* number link to real id */ + + struct snd_monitor_file *files; /* all files associated to this card */ + struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown state */ + spinlock_t files_lock; /* lock the files for this card */ + int shutdown; /* this card is going down */ + wait_queue_head_t shutdown_sleep; + +#ifdef CONFIG_PM + int (*set_power_state) (snd_card_t *card, unsigned int state); + void *power_state_private_data; + unsigned int power_state; /* power state */ + struct semaphore power_lock; /* power lock */ + wait_queue_head_t power_sleep; +#endif + +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + snd_mixer_oss_t *mixer_oss; + int mixer_oss_change_count; +#endif +}; + +#ifdef CONFIG_PM +static inline void snd_power_lock(snd_card_t *card) +{ + down(&card->power_lock); +} + +static inline void snd_power_unlock(snd_card_t *card) +{ + up(&card->power_lock); +} + +void snd_power_wait(snd_card_t *card); + +static inline unsigned int snd_power_get_state(snd_card_t *card) +{ + return card->power_state; +} + +static inline void snd_power_change_state(snd_card_t *card, unsigned int state) +{ + card->power_state = state; + wake_up(&card->power_sleep); +} +#else +#define snd_power_lock(card) do { (void)(card); } while (0) +#define snd_power_unlock(card) do { (void)(card); } while (0) +#define snd_power_wait(card) do { (void)(card); } while (0) +#define snd_power_get_state(card) SNDRV_CTL_POWER_D0 +#define snd_power_change_state(card, state) do { (void)(card); } while (0) +#endif + +/* device.c */ + +struct _snd_minor { + struct list_head list; /* list of all minors per card */ + int number; /* minor number */ + int device; /* device number */ + const char *comment; /* for /proc/asound/devices */ + snd_info_entry_t *dev; /* for /proc/asound/dev */ + struct file_operations *f_ops; /* file operations */ +}; + +typedef struct _snd_minor snd_minor_t; + +/* sound.c */ + +extern int snd_ecards_limit; +extern int device_mode; +extern int device_gid; +extern int device_uid; + +void snd_request_card(int card); + +int snd_register_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name); +int snd_unregister_device(int type, snd_card_t *card, int dev); + +#ifdef CONFIG_SND_OSSEMUL +int snd_register_oss_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name); +int snd_unregister_oss_device(int type, snd_card_t *card, int dev); +#endif + +int snd_minor_info_init(void); +int snd_minor_info_done(void); + +/* sound_oss.c */ + +#ifdef CONFIG_SND_OSSEMUL + +int snd_minor_info_oss_init(void); +int snd_minor_info_oss_done(void); + +int snd_oss_init_module(void); + +#endif + +/* memory.c */ + +#ifdef CONFIG_SND_DEBUG_MEMORY +void snd_memory_init(void); +void snd_memory_done(void); +int snd_memory_info_init(void); +int snd_memory_info_done(void); +void *snd_hidden_kmalloc(size_t size, int flags); +void snd_hidden_kfree(const void *obj); +void *snd_hidden_vmalloc(unsigned long size); +void snd_hidden_vfree(void *obj); +#define kmalloc(size, flags) snd_hidden_kmalloc(size, flags) +#define kfree(obj) snd_hidden_kfree(obj) +#define vmalloc(size) snd_hidden_vmalloc(size) +#define vfree(obj) snd_hidden_vfree(obj) +#define kmalloc_nocheck(size, flags) snd_wrapper_kmalloc(size, flags) +#define vmalloc_nocheck(size) snd_wrapper_vmalloc(size) +#define kfree_nocheck(obj) snd_wrapper_kfree(obj) +#define vfree_nocheck(obj) snd_wrapper_vfree(obj) +#else +#define kmalloc_nocheck(size, flags) kmalloc(size, flags) +#define vmalloc_nocheck(size) vmalloc(size) +#define kfree_nocheck(obj) kfree(obj) +#define vfree_nocheck(obj) vfree(obj) +#endif +void *snd_kcalloc(size_t size, int flags); +char *snd_kmalloc_strdup(const char *string, int flags); +int copy_to_user_fromio(void *dst, unsigned long src, size_t count); +int copy_from_user_toio(unsigned long dst, const void *src, size_t count); + +/* init.c */ + +extern int snd_cards_count; +extern unsigned int snd_cards_lock; +extern snd_card_t *snd_cards[SNDRV_CARDS]; +extern rwlock_t snd_card_rwlock; +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#define SND_MIXER_OSS_NOTIFY_REGISTER 0 +#define SND_MIXER_OSS_NOTIFY_DISCONNECT 1 +#define SND_MIXER_OSS_NOTIFY_FREE 2 +extern int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int cmd); +#endif + +snd_card_t *snd_card_new(int idx, const char *id, + struct module *module, int extra_size); +int snd_card_disconnect(snd_card_t *card); +int snd_card_free(snd_card_t *card); +int snd_card_free_in_thread(snd_card_t *card); +int snd_card_register(snd_card_t *card); +int snd_card_info_init(void); +int snd_card_info_done(void); +int snd_component_add(snd_card_t *card, const char *component); +int snd_card_file_add(snd_card_t *card, struct file *file); +int snd_card_file_remove(snd_card_t *card, struct file *file); + +/* device.c */ + +int snd_device_new(snd_card_t *card, snd_device_type_t type, + void *device_data, snd_device_ops_t *ops); +int snd_device_register(snd_card_t *card, void *device_data); +int snd_device_register_all(snd_card_t *card); +int snd_device_disconnect(snd_card_t *card, void *device_data); +int snd_device_disconnect_all(snd_card_t *card); +int snd_device_free(snd_card_t *card, void *device_data); +int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd); + +/* isadma.c */ + +#define DMA_MODE_NO_ENABLE 0x0100 + +void snd_dma_program(unsigned long dma, unsigned long addr, unsigned int size, unsigned short mode); +void snd_dma_disable(unsigned long dma); +unsigned int snd_dma_pointer(unsigned long dma, unsigned int size); + +/* misc.c */ + +int snd_task_name(struct task_struct *task, char *name, size_t size); +#ifdef CONFIG_SND_VERBOSE_PRINTK +void snd_verbose_printk(const char *file, int line, const char *format, ...); +#endif +#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) +void snd_verbose_printd(const char *file, int line, const char *format, ...); +#endif + +/* --- */ + +#ifdef CONFIG_SND_VERBOSE_PRINTK +/** + * snd_printk - printk wrapper + * @fmt: format string + * + * Works like print() but prints the file and the line of the caller + * when configured with CONFIG_SND_VERBOSE_PRINTK. + */ +#define snd_printk(fmt, args...) \ + snd_verbose_printk(__FILE__, __LINE__, fmt ,##args) +#else +#define snd_printk(fmt, args...) \ + printk(fmt ,##args) +#endif + +#ifdef CONFIG_SND_DEBUG + +#define __ASTRING__(x) #x + +#ifdef CONFIG_SND_VERBOSE_PRINTK +/** + * snd_printd - debug printk + * @format: format string + * + * Compiled only when Works like snd_printk() for debugging purpose. + * Ignored when CONFIG_SND_DEBUG is not set. + */ +#define snd_printd(fmt, args...) \ + snd_verbose_printd(__FILE__, __LINE__, fmt ,##args) +#else +#define snd_printd(fmt, args...) \ + printk(fmt ,##args) +#endif +/** + * snd_assert - run-time assersion macro + * @expr: expression + * @args...: the action + * + * This macro checks the expression in run-time and invokes the commands + * given in the rest arguments if the assertion is failed. + * When CONFIG_SND_DEBUG is not set, the expression is executed but + * not checked. + */ +#define snd_assert(expr, args...) do {\ + if (!(expr)) {\ + snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) +/** + * snd_runtime_check - run-time assersion macro + * @expr: expression + * @args...: the action + * + * This macro checks the expression in run-time and invokes the commands + * given in the rest arguments if the assertion is failed. + * Unlike snd_assert(), the action commands are executed even if + * CONFIG_SND_DEBUG is not set but without any error messages. + */ +#define snd_runtime_check(expr, args...) do {\ + if (!(expr)) {\ + snd_printk("ERROR (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) + +#else /* !CONFIG_SND_DEBUG */ + +#define snd_printd(fmt, args...) /* nothing */ +#define snd_assert(expr, args...) (void)(expr) +#define snd_runtime_check(expr, args...) do { if (!(expr)) { args; } } while (0) + +#endif /* CONFIG_SND_DEBUG */ + +#ifdef CONFIG_SND_DEBUG_DETECT +/** + * snd_printdd - debug printk + * @format: format string + * + * Compiled only when Works like snd_printk() for debugging purpose. + * Ignored when CONFIG_SND_DEBUG_DETECT is not set. + */ +#define snd_printdd(format, args...) snd_printk(format, ##args) +#else +#define snd_printdd(format, args...) /* nothing */ +#endif + +#define snd_BUG() snd_assert(0, ) + + +static inline void snd_timestamp_now(struct timespec *tstamp, int timespec) +{ + struct timeval val; + /* FIXME: use a linear time source */ + do_gettimeofday(&val); + tstamp->tv_sec = val.tv_sec; + tstamp->tv_nsec = val.tv_usec; + if (timespec) + tstamp->tv_nsec *= 1000L; +} + +static inline void snd_timestamp_zero(struct timespec *tstamp) +{ + tstamp->tv_sec = 0; + tstamp->tv_nsec = 0; +} + +static inline int snd_timestamp_null(struct timespec *tstamp) +{ + return tstamp->tv_sec == 0 && tstamp->tv_nsec == 0; +} + +#define SNDRV_OSS_VERSION ((3<<16)|(8<<8)|(1<<4)|(0)) /* 3.8.1a */ + +#endif /* __SOUND_CORE_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs4231.h linux/include/sound/cs4231.h --- linux-2.4.21-rc1.orig/include/sound/cs4231.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs4231.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,372 @@ +#ifndef __SOUND_CS4231_H +#define __SOUND_CS4231_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for CS4231 & InterWave chips & compatible chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" +#include "timer.h" + +#ifdef CONFIG_SBUS +#define SBUS_SUPPORT +#include +#endif + +#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64) +#define EBUS_SUPPORT +#include +#include +#endif + +#if !defined(SBUS_SUPPORT) && !defined(EBUS_SUPPORT) +#define LEGACY_SUPPORT +#endif + +/* IO ports */ + +#define CS4231P(x) (c_d_c_CS4231##x) + +#define c_d_c_CS4231REGSEL 0 +#define c_d_c_CS4231REG 1 +#define c_d_c_CS4231STATUS 2 +#define c_d_c_CS4231PIO 3 + +/* codec registers */ + +#define CS4231_LEFT_INPUT 0x00 /* left input control */ +#define CS4231_RIGHT_INPUT 0x01 /* right input control */ +#define CS4231_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define CS4231_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define CS4231_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define CS4231_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define CS4231_LEFT_OUTPUT 0x06 /* left output control register */ +#define CS4231_RIGHT_OUTPUT 0x07 /* right output control register */ +#define CS4231_PLAYBK_FORMAT 0x08 /* clock and data format - playback - bits 7-0 MCE */ +#define CS4231_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define CS4231_PIN_CTRL 0x0a /* pin control */ +#define CS4231_TEST_INIT 0x0b /* test and initialization */ +#define CS4231_MISC_INFO 0x0c /* miscellaneaous information */ +#define CS4231_LOOPBACK 0x0d /* loopback control */ +#define CS4231_PLY_UPR_CNT 0x0e /* playback upper base count */ +#define CS4231_PLY_LWR_CNT 0x0f /* playback lower base count */ +#define CS4231_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ +#define AD1845_AF1_MIC_LEFT 0x10 /* alternate #1 feature + MIC left */ +#define CS4231_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ +#define AD1845_AF2_MIC_RIGHT 0x11 /* alternate #2 feature + MIC right */ +#define CS4231_LEFT_LINE_IN 0x12 /* left line input control */ +#define CS4231_RIGHT_LINE_IN 0x13 /* right line input control */ +#define CS4231_TIMER_LOW 0x14 /* timer low byte */ +#define CS4231_TIMER_HIGH 0x15 /* timer high byte */ +#define CS4231_LEFT_MIC_INPUT 0x16 /* left MIC input control register (InterWave only) */ +#define AD1845_UPR_FREQ_SEL 0x16 /* upper byte of frequency select */ +#define CS4231_RIGHT_MIC_INPUT 0x17 /* right MIC input control register (InterWave only) */ +#define AD1845_LWR_FREQ_SEL 0x17 /* lower byte of frequency select */ +#define CS4236_EXT_REG 0x17 /* extended register access */ +#define CS4231_IRQ_STATUS 0x18 /* irq status register */ +#define CS4231_LINE_LEFT_OUTPUT 0x19 /* left line output control register (InterWave only) */ +#define CS4231_VERSION 0x19 /* CS4231(A) - version values */ +#define CS4231_MONO_CTRL 0x1a /* mono input/output control */ +#define CS4231_LINE_RIGHT_OUTPUT 0x1b /* right line output control register (InterWave only) */ +#define AD1845_PWR_DOWN 0x1b /* power down control */ +#define CS4235_LEFT_MASTER 0x1b /* left master output control */ +#define CS4231_REC_FORMAT 0x1c /* clock and data format - record - bits 7-0 MCE */ +#define CS4231_PLY_VAR_FREQ 0x1d /* playback variable frequency */ +#define AD1845_CLOCK 0x1d /* crystal clock select and total power down */ +#define CS4235_RIGHT_MASTER 0x1d /* right master output control */ +#define CS4231_REC_UPR_CNT 0x1e /* record upper count */ +#define CS4231_REC_LWR_CNT 0x1f /* record lower count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define CS4231_INIT 0x80 /* CODEC is initializing */ +#define CS4231_MCE 0x40 /* mode change enable */ +#define CS4231_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define CS4231_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for codec irq status */ + +#define CS4231_PLAYBACK_IRQ 0x10 +#define CS4231_RECORD_IRQ 0x20 +#define CS4231_TIMER_IRQ 0x40 +#define CS4231_ALL_IRQS 0x70 +#define CS4231_REC_UNDERRUN 0x08 +#define CS4231_REC_OVERRUN 0x04 +#define CS4231_PLY_OVERRUN 0x02 +#define CS4231_PLY_UNDERRUN 0x01 + +/* definitions for CS4231_LEFT_INPUT and CS4231_RIGHT_INPUT registers */ + +#define CS4231_ENABLE_MIC_GAIN 0x20 + +#define CS4231_MIXS_LINE 0x00 +#define CS4231_MIXS_AUX1 0x40 +#define CS4231_MIXS_MIC 0x80 +#define CS4231_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - CS4231_PLAYBK_FORMAT */ + +#define CS4231_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define CS4231_ALAW_8 0x60 /* 8-bit A-law companded */ +#define CS4231_ULAW_8 0x20 /* 8-bit U-law companded */ +#define CS4231_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define CS4231_LINEAR_16_BIG 0xc0 /* 16-bit twos complement data - big endian */ +#define CS4231_ADPCM_16 0xa0 /* 16-bit ADPCM */ +#define CS4231_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define CS4231_XTAL1 0x00 /* 24.576 crystal */ +#define CS4231_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - CS4231_IFACE_CTRL */ + +#define CS4231_RECORD_PIO 0x80 /* record PIO enable */ +#define CS4231_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define CS4231_CALIB_MODE 0x18 /* calibration mode bits */ +#define CS4231_AUTOCALIB 0x08 /* auto calibrate */ +#define CS4231_SINGLE_DMA 0x04 /* use single DMA channel */ +#define CS4231_RECORD_ENABLE 0x02 /* record enable */ +#define CS4231_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - CS4231_PIN_CTRL */ + +#define CS4231_IRQ_ENABLE 0x02 /* enable IRQ */ +#define CS4231_XCTL1 0x40 /* external control #1 */ +#define CS4231_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - CS4231_TEST_INIT */ + +#define CS4231_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define CS4231_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* definitions for misc control register - CS4231_MISC_INFO */ + +#define CS4231_MODE2 0x40 /* MODE 2 */ +#define CS4231_IW_MODE3 0x6c /* MODE 3 - InterWave enhanced mode */ +#define CS4231_4236_MODE3 0xe0 /* MODE 3 - CS4236+ enhanced mode */ + +/* definitions for alternate feature 1 register - CS4231_ALT_FEATURE_1 */ + +#define CS4231_DACZ 0x01 /* zero DAC when underrun */ +#define CS4231_TIMER_ENABLE 0x40 /* codec timer enable */ +#define CS4231_OLB 0x80 /* output level bit */ + +/* definitions for Extended Registers - CS4236+ */ + +#define CS4236_REG(i23val) (((i23val << 2) & 0x10) | ((i23val >> 4) & 0x0f)) +#define CS4236_I23VAL(reg) ((((reg)&0xf) << 4) | (((reg)&0x10) >> 2) | 0x8) + +#define CS4236_LEFT_LINE 0x08 /* left LINE alternate volume */ +#define CS4236_RIGHT_LINE 0x18 /* right LINE alternate volume */ +#define CS4236_LEFT_MIC 0x28 /* left MIC volume */ +#define CS4236_RIGHT_MIC 0x38 /* right MIC volume */ +#define CS4236_LEFT_MIX_CTRL 0x48 /* synthesis and left input mixer control */ +#define CS4236_RIGHT_MIX_CTRL 0x58 /* right input mixer control */ +#define CS4236_LEFT_FM 0x68 /* left FM volume */ +#define CS4236_RIGHT_FM 0x78 /* right FM volume */ +#define CS4236_LEFT_DSP 0x88 /* left DSP serial port volume */ +#define CS4236_RIGHT_DSP 0x98 /* right DSP serial port volume */ +#define CS4236_RIGHT_LOOPBACK 0xa8 /* right loopback monitor volume */ +#define CS4236_DAC_MUTE 0xb8 /* DAC mute and IFSE enable */ +#define CS4236_ADC_RATE 0xc8 /* indenpendent ADC sample frequency */ +#define CS4236_DAC_RATE 0xd8 /* indenpendent DAC sample frequency */ +#define CS4236_LEFT_MASTER 0xe8 /* left master digital audio volume */ +#define CS4236_RIGHT_MASTER 0xf8 /* right master digital audio volume */ +#define CS4236_LEFT_WAVE 0x0c /* left wavetable serial port volume */ +#define CS4236_RIGHT_WAVE 0x1c /* right wavetable serial port volume */ +#define CS4236_VERSION 0x9c /* chip version and ID */ + +/* defines for codec.mode */ + +#define CS4231_MODE_NONE 0x0000 +#define CS4231_MODE_PLAY 0x0001 +#define CS4231_MODE_RECORD 0x0002 +#define CS4231_MODE_TIMER 0x0004 +#define CS4231_MODE_OPEN (CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER) + +/* defines for codec.hardware */ + +#define CS4231_HW_DETECT 0x0000 /* let CS4231 driver detect chip */ +#define CS4231_HW_DETECT3 0x0001 /* allow mode 3 */ +#define CS4231_HW_TYPE_MASK 0xff00 /* type mask */ +#define CS4231_HW_CS4231_MASK 0x0100 /* CS4231 serie */ +#define CS4231_HW_CS4231 0x0100 /* CS4231 chip */ +#define CS4231_HW_CS4231A 0x0101 /* CS4231A chip */ +#define CS4231_HW_AD1845 0x0102 /* AD1845 chip */ +#define CS4231_HW_CS4232_MASK 0x0200 /* CS4232 serie (has control ports) */ +#define CS4231_HW_CS4232 0x0200 /* CS4232 */ +#define CS4231_HW_CS4232A 0x0201 /* CS4232A */ +#define CS4231_HW_CS4236 0x0202 /* CS4236 */ +#define CS4231_HW_CS4236B_MASK 0x0400 /* CS4236B serie (has extended control regs) */ +#define CS4231_HW_CS4235 0x0400 /* CS4235 - Crystal Clear (tm) stereo enhancement */ +#define CS4231_HW_CS4236B 0x0401 /* CS4236B */ +#define CS4231_HW_CS4237B 0x0402 /* CS4237B - SRS 3D */ +#define CS4231_HW_CS4238B 0x0403 /* CS4238B - QSOUND 3D */ +#define CS4231_HW_CS4239 0x0404 /* CS4239 - Crystal Clear (tm) stereo enhancement */ +/* compatible, but clones */ +#define CS4231_HW_INTERWAVE 0x1000 /* InterWave chip */ +#define CS4231_HW_OPL3SA2 0x1001 /* OPL3-SA2 chip */ + +/* defines for codec.hwshare */ +#define CS4231_HWSHARE_IRQ (1<<0) +#define CS4231_HWSHARE_DMA1 (1<<1) +#define CS4231_HWSHARE_DMA2 (1<<2) + +typedef struct _snd_cs4231 cs4231_t; + +struct _snd_cs4231 { + unsigned long port; /* base i/o port */ +#ifdef LEGACY_SUPPORT + struct resource *res_port; + unsigned long cport; /* control base i/o port (CS4236) */ + struct resource *res_cport; + int irq; /* IRQ line */ + int dma1; /* playback DMA */ + int dma2; /* record DMA */ +#endif + unsigned short version; /* version of CODEC chip */ + unsigned short mode; /* see to CS4231_MODE_XXXX */ + unsigned short hardware; /* see to CS4231_HW_XXXX */ + unsigned short hwshare; /* shared resources */ + unsigned short single_dma:1, /* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */ + ebus_flag:1; /* SPARC: EBUS present */ + +#ifdef EBUS_SUPPORT + struct ebus_dma_info eb2c; + struct ebus_dma_info eb2p; +#endif + +#if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) + union { +#ifdef SBUS_SUPPORT + struct sbus_dev *sdev; +#endif +#ifdef EBUS_SUPPORT + struct pci_dev *pdev; +#endif + } dev_u; + unsigned int p_periods_sent; + unsigned int c_periods_sent; +#endif + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_timer_t *timer; + + unsigned char image[32]; /* registers image */ + unsigned char eimage[32]; /* extended registers image */ + unsigned char cimage[16]; /* control registers image */ + int mce_bit; + int calibrate_mute; + int sw_3d_bit; +#ifdef LEGACY_SUPPORT + unsigned int p_dma_size; + unsigned int c_dma_size; +#endif + + spinlock_t reg_lock; + struct semaphore mce_mutex; + struct semaphore open_mutex; + + int (*rate_constraint) (snd_pcm_runtime_t *runtime); + void (*set_playback_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char pdfr); + void (*set_capture_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char cdfr); + void (*trigger) (cs4231_t *chip, unsigned int what, int start); +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + void (*suspend) (cs4231_t *chip); + void (*resume) (cs4231_t *chip); +#endif + void *dma_private_data; +#ifdef LEGACY_SUPPORT + int (*claim_dma) (cs4231_t *chip, void *dma_private_data, int dma); + int (*release_dma) (cs4231_t *chip, void *dma_private_data, int dma); +#endif +}; + +/* exported functions */ + +void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char val); +unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg); +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, unsigned char mask, unsigned char val); +void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val); +unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg); +void snd_cs4231_mce_up(cs4231_t *chip); +void snd_cs4231_mce_down(cs4231_t *chip); + +void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +const char *snd_cs4231_chip_id(cs4231_t *chip); + +int snd_cs4231_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip); +int snd_cs4231_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm); +int snd_cs4231_timer(cs4231_t * chip, int device, snd_timer_t **rtimer); +int snd_cs4231_mixer(cs4231_t * chip); + +int snd_cs4236_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip); +int snd_cs4236_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm); +int snd_cs4236_mixer(cs4231_t * chip); + +/* + * mixer library + */ + +#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4231_info_single, \ + .get = snd_cs4231_get_single, .put = snd_cs4231_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4231_info_double, \ + .get = snd_cs4231_get_double, .put = snd_cs4231_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#ifdef CONFIG_SND_DEBUG +void snd_cs4231_debug(cs4231_t *chip); +#endif + +#endif /* __SOUND_CS4231_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs46xx.h linux/include/sound/cs46xx.h --- linux-2.4.21-rc1.orig/include/sound/cs46xx.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs46xx.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,1777 @@ +#ifndef __SOUND_CS46XX_H +#define __SOUND_CS46XX_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Cirrus Logic, Inc. + * Definitions for Cirrus Logic CS46xx chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "ac97_codec.h" +#include "cs46xx_dsp_spos.h" + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4610 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4612 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4615 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 +#endif + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS46xx part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS46xx part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CHIP_TYPE_MASK 0x00000001 +#define SERACC_CHIP_TYPE_1_03 0x00000000 +#define SERACC_CHIP_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#define SERACC_ODT 0x00000010 /* only CS4630 */ +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ + +/* + * + */ + +#define SAVE_REG_MAX 0x10 +#define POWER_DOWN_ALL 0x7f0f + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define MAX_NR_AC97 4 +#define CS46XX_PRIMARY_CODEC_INDEX 0 +#define CS46XX_SECONDARY_CODEC_INDEX 1 +#define CS46XX_SECONDARY_CODEC_OFFSET 0x80 +#define CS46XX_DSP_CAPTURE_CHANNEL 1 + +/* capture */ +#define CS46XX_DSP_CAPTURE_CHANNEL 1 + +/* mixer */ +#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1 +#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2 + +typedef struct _snd_cs46xx cs46xx_t; + +typedef struct _snd_cs46xx_pcm_t { + unsigned char *hw_area; + dma_addr_t hw_addr; /* PCI bus address, not accessible */ + unsigned long hw_size; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + unsigned int sw_bufsize; + unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ + unsigned int sw_io; + int sw_ready; /* Bytes ready to be transferred to/from hw */ + unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ + unsigned int hw_io; /* Ring buffer hw pointer */ + int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ + size_t appl_ptr; /* Last seen appl_ptr */ + snd_pcm_substream_t *substream; + + pcm_channel_descriptor_t * pcm_channel; + + int pcm_channel_id; /* Fron Rear, Center Lfe ... */ +} cs46xx_pcm_t; + +typedef struct { + char name[24]; + unsigned long base; + unsigned long remap_addr; + unsigned long size; + struct resource *resource; +} snd_cs46xx_region_t; + +struct _snd_cs46xx { + int irq; + unsigned long ba0_addr; + unsigned long ba1_addr; + union { + struct { + snd_cs46xx_region_t ba0; + snd_cs46xx_region_t data0; + snd_cs46xx_region_t data1; + snd_cs46xx_region_t pmem; + snd_cs46xx_region_t reg; + } name; + snd_cs46xx_region_t idx[5]; + } region; + + unsigned int mode; + + struct { + unsigned char *hw_area; + dma_addr_t hw_addr; /* PCI bus address, not accessible */ + unsigned long hw_size; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + unsigned int sw_bufsize; + unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ + unsigned int sw_io; + int sw_ready; /* Bytes ready to be transferred to/from hw */ + unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ + unsigned int hw_io; /* Ring buffer hw pointer */ + int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ + size_t appl_ptr; /* Last seen appl_ptr */ + snd_pcm_substream_t *substream; + } capt; + + + int nr_ac97_codecs; + ac97_t *ac97[MAX_NR_AC97]; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + + int amplifier; + void (*amplifier_ctrl)(cs46xx_t *, int); + void (*active_ctrl)(cs46xx_t *, int); + void (*mixer_init)(cs46xx_t *); + struct pci_dev *acpi_dev; + int acpi_port; + snd_kcontrol_t *eapd_switch; /* for amplifier hack */ + int accept_valid; /* accept mmap valid (for OSS) */ + + struct snd_cs46xx_gameport *gameport; + +#ifdef CONFIG_PM + struct pm_dev *pm_dev; +#endif +#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO + int current_gpio; +#endif +#ifdef CONFIG_SND_CS46XX_NEW_DSP + struct semaphore spos_mutex; + + dsp_spos_instance_t * dsp_spos_instance; + + snd_pcm_t *pcm_rear; + snd_pcm_t *pcm_iec958; +#else /* for compatibility */ + cs46xx_pcm_t *playback_pcm; + unsigned int play_ctl; +#endif +}; + +int snd_cs46xx_create(snd_card_t *card, + struct pci_dev *pci, + int external_amp, int thinkpad, + cs46xx_t **rcodec); + +int snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t **rpcm); +int snd_cs46xx_pcm_rear(cs46xx_t *chip, int device, snd_pcm_t **rpcm); +int snd_cs46xx_pcm_iec958(cs46xx_t *chip, int device, snd_pcm_t **rpcm); +int snd_cs46xx_mixer(cs46xx_t *chip); +int snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rmidi); +int snd_cs46xx_start_dsp(cs46xx_t *chip); +void snd_cs46xx_gameport(cs46xx_t *chip); + +#ifdef CONFIG_PM +void snd_cs46xx_suspend(cs46xx_t *chip); +void snd_cs46xx_resume(cs46xx_t *chip); +#endif + +#endif /* __SOUND_CS46XX_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs46xx_dsp_scb_types.h linux/include/sound/cs46xx_dsp_scb_types.h --- linux-2.4.21-rc1.orig/include/sound/cs46xx_dsp_scb_types.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs46xx_dsp_scb_types.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,936 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * NOTE: comments are copy/paste from cwcemb80.lst + * provided by Tom Woller at Cirrus (my only + * documentation about the SP OS running inside + * the DSP) + */ + +#ifndef __CS46XX_DSP_SCB_TYPES_H__ +#define __CS46XX_DSP_SCB_TYPES_H__ + +/* This structs are used internally by the SP */ + +typedef struct _basic_dma_req_t { + /* DMA Requestor Word 0 (DCW) fields: + + 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0] + _______________________________________________________________________________________ + |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword | + |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1| + */ + u32 dcw; /* DMA Control Word */ + u32 dmw; /* DMA Mode Word */ + u32 saw; /* Source Address Word */ + u32 daw; /* Destination Address Word */ +} basic_dma_req_t; + +typedef struct _scatter_gather_ext_t { + u32 npaw; /* Next-Page Address Word */ + + /* DMA Requestor Word 5 (NPCW) fields: + + 31-30 29 28 [27:16] [15:12] [11:3] [2:0] + _________________________________________________________________________________________ + |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | | + |page|__|__| ___________________________|_________|__page, if !sample-end___________|____| + */ + u32 npcw; /* Next-Page Control Word */ + u32 lbaw; /* Loop-Begin Address Word */ + u32 nplbaw; /* Next-Page after Loop-Begin Address Word */ + u32 sgaw; /* Scatter/Gather Address Word */ +} scatter_gather_ext_t; + +typedef struct _volume_control_t { + u16 rightTarg; /* Target volume for left & right channels */ + u16 leftTarg; + + u16 rightVol; /* Current left & right channel volumes */ + u16 leftVol; +} volume_control_t; + +/* Generic stream control block (SCB) structure definition */ +typedef struct _generic_scb_t { + /* For streaming I/O, the DSP should never alter any words in the DMA + requestor or the scatter/gather extension. Only ad hoc DMA request + streams are free to alter the requestor (currently only occur in the + DOS-based MIDI controller and in debugger-inserted code). + + If an SCB does not have any associated DMA requestor, these 9 ints + may be freed for use by other tasks, but the pointer to the SCB must + still be such that the insOrd:nextSCB appear at offset 9 from the + SCB pointer. + + Basic (non scatter/gather) DMA requestor (4 ints) + */ + + /* Initialized by the host, only modified by DMA + R/O for the DSP task */ + basic_dma_req_t basic_req; /* Optional */ + + /* Scatter/gather DMA requestor extension (5 ints) + Initialized by the host, only modified by DMA + DSP task never needs to even read these. + */ + scatter_gather_ext_t sg_ext; /* Optional */ + + /* Sublist pointer & next stream control block (SCB) link. + Initialized & modified by the host R/O for the DSP task + */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + + /* Pointer to this tasks parameter block & stream function pointer + Initialized by the host R/O for the DSP task */ + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + + /* rsConfig register for stream buffer (rsDMA reg. + is loaded from basicReq.daw for incoming streams, or + basicReq.saw, for outgoing streams) + + 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] + ______________________________________________________________________________ + |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod | + |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______| + 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] + + + Initialized by the host R/O for the DSP task + */ + u32 strm_rs_config; /* REQUIRED */ + // + /* On mixer input streams: indicates mixer input stream configuration + On Tees, this is copied from the stream being snooped + + Stream sample pointer & MAC-unit mode for this stream + + Initialized by the host Updated by the DSP task + */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* On mixer input streams: points to next mixer input and is updated by the + mixer subroutine in the "parent" DSP task + (least-significant 16 bits are preserved, unused) + + On Tees, the pointer is copied from the stream being snooped on + initialization, and, subsequently, it is copied into the + stream being snooped. + + On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for + fractional phase accumulation + + Fractional increment per output sample in the input sample buffer + + (Not used on mixer input streams & redefined on Tees) + On wavetable/3D voices: this 32-bit word specifies the integer.fractional + increment per output sample. + */ + u32 strmPhiIncr; + + + /* Standard stereo volume control + Initialized by the host (host updates target volumes) + + Current volumes update by the DSP task + On mixer input streams: required & updated by the mixer subroutine in the + "parent" DSP task + + On Tees, both current & target volumes are copied up on initialization, + and, subsequently, the target volume is copied up while the current + volume is copied down. + + These two 32-bit words are redefined for wavetable & 3-D voices. + */ + volume_control_t vol_ctrl_t; /* Optional */ +} generic_scb_t; + + +typedef struct _spos_control_block_t { + /* WARNING: Certain items in this structure are modified by the host + Any dword that can be modified by the host, must not be + modified by the SP as the host can only do atomic dword + writes, and to do otherwise, even a read modify write, + may lead to corrupted data on the SP. + + This rule does not apply to one off boot time initialisation prior to starting the SP + */ + + + /* First element on the Hyper forground task tree */ + u16 hfg_tree_root_ptr; /* HOST */ + /* First 3 dwords are written by the host and read-only on the DSP */ + u16 hfg_stack_base; /* HOST */ + + /* Point to this data structure to enable easy access */ + u16 spos_cb_ptr; /* SP */ + u16 prev_task_tree_ptr; /* SP && HOST */ + + + /* Currently Unused */ + u16 xxinterval_timer_period; + /* Enable extension of SPOS data structure */ + u16 HFGSPB_ptr; + + + u16 xxnum_HFG_ticks_thisInterval; + /* Modified by the DSP */ + u16 xxnum_tntervals; + + + /* Set by DSP upon encountering a trap (breakpoint) or a spurious + interrupt. The host must clear this dword after reading it + upon receiving spInt1. */ + u16 spurious_int_flag; /* (Host & SP) Nature of the spurious interrupt */ + u16 trap_flag; /* (Host & SP) Nature of detected Trap */ + + + u16 unused2; + u16 invalid_IP_flag; /* (Host & SP ) Indicate detection of invalid instruction pointer */ + + + /* pointer to forground task tree header for use in next task search */ + u16 fg_task_tree_hdr_ptr; /* HOST */ + /* Data structure for controlling synchronous link update */ + u16 hfg_sync_update_ptr; /* HOST */ + + u16 begin_foreground_FCNT; /* SP */ + /* Place holder for holding sleep timing */ + u16 last_FCNT_before_sleep; /* SP */ + + u16 unused7; /* SP */ + u16 next_task_treePtr; /* SP */ + + u32 nused5; + + u16 active_flags; /* SP */ + /* State flags, used to assist control of execution of Hyper Forground */ + u16 HFG_flags; /* SP */ + + u16 unused9; + u16 unused8; + + /* Space for saving enough context so that we can set up enough + to save some more context. + */ + u32 rFE_save_for_invalid_IP; + u32 r32_save_for_spurious_int; + u32 r32_save_for_trap; + u32 r32_save_for_HFG; +} spos_control_block_t; + +/* SPB for MIX_TO_OSTREAM algorithm family */ +typedef struct _mix2_ostream_spb_t +{ + /* 16b.16b integer.frac approximation to the + number of 3 sample triplets to output each + frame. (approximation must be floor, to + insure that the fractional error is always + positive) + */ + u32 outTripletsPerFrame; + + /* 16b.16b integer.frac accumulated number of + output triplets since the start of group + */ + u32 accumOutTriplets; +} mix2_ostream_spb_t; + +/* SCB for Timing master algorithm */ +typedef struct _timing_master_scb_t { + /* First 12 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + + /* Initial values are 0000:xxxx */ + u16 reserved; + u16 extra_sample_accum; + + + /* Initial values are xxxx:0000 + hi: Current CODEC output FIFO pointer + (0 to 0x0f) + lo: Flag indicating that the CODEC + FIFO is sync'd (host clears to + resynchronize the FIFO pointer + upon start/restart) + */ + u16 codec_FIFO_syncd; + u16 codec_FIFO_ptr; + + + /* Init. 8000:0005 for 44.1k + 8000:0001 for 48k + hi: Fractional sample accumulator 0.16b + lo: Number of frames remaining to be + processed in the current group of + frames + */ + u16 frac_samp_accum_qm1; + u16 TM_frms_left_in_group; + + /* Init. 0001:0005 for 44.1k + 0000:0001 for 48k + hi: Fractional sample correction factor 0.16b + to be added every frameGroupLength frames + to correct for truncation error in + nsamp_per_frm_q15 + lo: Number of frames in the group + */ + u16 frac_samp_correction_qm1; + u16 TM_frm_group_length; + + /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k + 48k*65536/8k = 0x00060000 for 48k + 16b.16b integer.frac approximation to the + number of samples to output each frame. + (approximation must be floor, to insure */ + u32 nsamp_per_frm_q15; +} timing_master_scb_t; + +/* SCB for CODEC output algorithm */ +typedef struct _codec_output_scb_t { + /* First 13 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* NOTE: The CODEC output task reads samples from the first task on its + sublist at the stream buffer pointer (init. to lag DMA destination + address word). After the required number of samples is transferred, + the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples + consumed. + */ + + /* Init. 0000:0010 for SDout + 0060:0010 for SDout2 + 0080:0010 for SDout3 + hi: Base IO address of FIFO to which + the left-channel samples are to + be written. + lo: Displacement for the base IO + address for left-channel to obtain + the base IO address for the FIFO + to which the right-channel samples + are to be written. + */ + u16 left_chan_base_IO_addr; + u16 right_chan_IO_disp; + + + /* Init: 0x0080:0004 for non-AC-97 + Init: 0x0080:0000 for AC-97 + hi: Exponential volume change rate + for input stream + lo: Positive shift count to shift the + 16-bit input sample to obtain the + 32-bit output word + */ + u16 CO_scale_shift_count; + u16 CO_exp_vol_change_rate; + + /* Pointer to SCB at end of input chain */ + u16 reserved; + u16 last_sub_ptr; +} codec_output_scb_t; + +/* SCB for CODEC input algorithm */ +typedef struct _codec_input_scb_t { + /* First 13 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* NOTE: The CODEC input task reads samples from the hardware FIFO + sublist at the DMA source address word (sub_list_ptr->basic_req.saw). + After the required number of samples is transferred, the CODEC + output task advances sub_list_ptr->basic_req.saw past the samples + consumed. SPuD must initialize the sub_list_ptr->basic_req.saw + to point half-way around from the initial sub_list_ptr->strm_nuf_ptr + to allow for lag/lead. + */ + + /* Init. 0000:0010 for SDout + 0060:0010 for SDout2 + 0080:0010 for SDout3 + hi: Base IO address of FIFO to which + the left-channel samples are to + be written. + lo: Displacement for the base IO + address for left-channel to obtain + the base IO address for the FIFO + to which the right-channel samples + are to be written. + */ + u16 rightChanINdisp; + u16 left_chan_base_IN_addr; + + /* Init. ?:fffc + lo: Negative shift count to shift the + 32-bit input dword to obtain the + 16-bit sample msb-aligned (count + is negative to shift left) + */ + u16 scaleShiftCount; + u16 reserver1; + + u32 reserved2; +} codec_input_scb_t; + + +typedef struct _pcm_serial_input_scb_t { + /* First 13 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + u32 strm_buf_ptr; /* REQUIRED */ + u32 strm_rs_config; /* REQUIRED */ + + /* Init. Ptr to CODEC input SCB + hi: Pointer to the SCB containing the + input buffer to which CODEC input + samples are written + lo: Flag indicating the link to the CODEC + input task is to be initialized + */ + u16 init_codec_input_link; + u16 codec_input_buf_scb; + + /* Initialized by the host (host updates target volumes) */ + volume_control_t psi_vol_ctrl; + +} pcm_serial_input_scb_t; + +typedef struct _src_task_scb_t { + u16 frames_left_in_gof; + u16 gofs_left_in_sec; + + u16 const2_thirds; + u16 num_extra_tnput_samples; + + u16 cor_per_gof; + u16 correction_per_sec; + + u16 output_buf_producer_ptr; + u16 junk_DMA_MID; + + u16 gof_length; + u16 gofs_per_sec; + + u32 input_buf_strm_config; + + u16 reserved_for_SRC_use; + u16 input_buf_consumer_ptr; + + u32 accum_phi; + + u16 exp_src_vol_change_rate; + u16 input_buf_producer_ptr; + + u16 src_next_scb; + u16 src_sub_list_ptr; + + u16 src_entry_point; + u16 src_this_sbp; + + u32 src_strm_rs_config; + u32 src_strm_buf_ptr; + + u32 phiIncr6int_26frac; + + volume_control_t src_vol_ctrl; +} src_task_scb_t; + +typedef struct _decimate_by_pow2_scb_t { + /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory + when compared to cascading decimators) + */ + u16 dec2_coef_base_ptr; + u16 dec2_coef_increment; + /* coefIncrement = 128 / decimationFactor (for our ROM filter) + coefBasePtr = 0x8000 (for our ROM filter) + */ + + u16 dec2_in_samples_per_out_triplet; + u16 dec2_extra_in_samples; + /* extraInSamples: # of accumulated, unused input samples (init. to 0) + inSamplesPerOutTriplet = 3 * decimationFactor + */ + + u16 dec2_const2_thirds; + u16 dec2_half_num_taps_mp5; + /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5 + const2thirds: constant 2/3 in 16Q0 format (sign.15) + */ + + u16 dec2_output_buf_producer_ptr; + u16 dec2_junkdma_mid; + + u32 dec2_reserved2; + + u32 dec2_input_nuf_strm_config; + /* inputBufStrmConfig: rsConfig for the input buffer to the decimator + (buffer size = decimationFactor * 32 dwords) + */ + + u16 dec2_phi_incr; + u16 dec2_input_buf_consumer_ptr; + /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) + phiIncr = decimationFactor * 4 + */ + + u32 dec2_reserved3; + + u16 dec2_exp_vol_change_rate; + u16 dec2_input_buf_producer_ptr; + /* inputBufProducerPtr: Input buffer write pointer + expVolChangeRate: Exponential volume change rate for possible + future mixer on input streams + */ + + u16 dec2_next_scb; + u16 dec2_sub_list_ptr; + + u16 dec2_entry_point; + u16 dec2_this_spb; + + u32 dec2_strm_rs_config; + u32 dec2_strm_buf_ptr; + + u32 dec2_reserved4; + + volume_control_t dec2_vol_ctrl; /* Not used! */ +} decimate_by_pow2_scb_t; + +typedef struct _vari_decimate_scb_t { + u16 vdec_frames_left_in_gof; + u16 vdec_gofs_left_in_sec; + + u16 vdec_const2_thirds; + u16 vdec_extra_in_samples; + /* extraInSamples: # of accumulated, unused input samples (init. to 0) + const2thirds: constant 2/3 in 16Q0 format (sign.15) */ + + u16 vdec_cor_per_gof; + u16 vdec_correction_per_sec; + + u16 vdec_output_buf_producer_ptr; + u16 vdec_input_buf_consumer_ptr; + /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */ + + u16 vdec_gof_length; + u16 vdec_gofs_per_sec; + + u32 vdec_input_buf_strm_config; + /* inputBufStrmConfig: rsConfig for the input buffer to the decimator + (buffer size = 64 dwords) */ + u32 vdec_coef_increment; + /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */ + + u32 vdec_accumphi; + /* accumPhi: accumulated fractional phase increment (6.26) */ + + u16 vdec_exp_vol_change_rate; + u16 vdec_input_buf_producer_ptr; + /* inputBufProducerPtr: Input buffer write pointer + expVolChangeRate: Exponential volume change rate for possible + future mixer on input streams */ + + u16 vdec_next_scb; + u16 vdec_sub_list_ptr; + + u16 vdec_entry_point; + u16 vdec_this_spb; + + u32 vdec_strm_rs_config; + u32 vdec_strm_buf_ptr; + + u32 vdec_phi_incr_6int_26frac; + + volume_control_t vdec_vol_ctrl; +} vari_decimate_scb_t; + + +/* SCB for MIX_TO_OSTREAM algorithm family */ +typedef struct _mix2_ostream_scb_t { + /* First 13 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + + /* hi: Number of mixed-down input triplets + computed since start of group + lo: Number of frames remaining to be + processed in the current group of + frames + */ + u16 frames_left_in_group; + u16 accum_input_triplets; + + /* hi: Exponential volume change rate + for mixer on input streams + lo: Number of frames in the group + */ + u16 frame_group_length; + u16 exp_vol_change_rate; + + u16 const_FFFF; + u16 const_zero; +} mix2_ostream_scb_t; + + +/* SCB for S16_MIX algorithm */ +typedef struct _mix_only_scb_t { + /* First 13 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + u32 reserved; + volume_control_t vol_ctrl; +} mix_only_scb_t; + +/* SCB for the async. CODEC input algorithm */ +typedef struct _async_codec_input_scb_t { + u32 io_free2; + + u32 io_current_total; + u32 io_previous_total; + + u16 io_count; + u16 io_count_limit; + + u16 o_fifo_base_addr; + u16 ost_mo_format; + /* 1 = stereo; 0 = mono + xxx for ASER 1 (not allowed); 118 for ASER2 */ + + u32 ostrm_rs_config; + u32 ostrm_buf_ptr; + + u16 io_sclks_per_lr_clk; + u16 io_io_enable; + + u32 io_free4; + + u16 io_next_scb; + u16 io_sub_list_ptr; + + u16 io_entry_point; + u16 io_this_spb; + + u32 istrm_rs_config; + u32 istrm_buf_ptr; + + /* Init. 0000:8042: for ASER1 + 0000:8044: for ASER2 */ + u16 io_stat_reg_addr; + u16 iofifo_pointer; + + /* Init 1 stero:100 ASER1 + Init 0 mono:110 ASER2 + */ + u16 ififo_base_addr; + u16 ist_mo_format; + + u32 i_free; +} async_codec_input_scb_t; + + +/* SCB for the SP/DIF CODEC input and output */ +typedef struct _spdifiscb_t { + u16 status_ptr; + u16 status_start_ptr; + + u32 current_total; + u32 previous_total; + + u16 count; + u16 count_limit; + + u32 status_data; + + u16 status; + u16 free4; + + u32 free3; + + u16 free2; + u16 bit_count; + + u32 temp_status; + + u16 next_SCB; + u16 sub_list_ptr; + + u16 entry_point; + u16 this_spb; + + u32 strm_rs_config; + u32 strm_buf_ptr; + + u16 stat_reg_addr; + u16 fifo_pointer; + + u16 fifo_base_addr; + u16 st_mo_format; + + u32 Free1; +} spdifiscb_t; + + +/* SCB for the SP/DIF CODEC input and output */ +typedef struct _spdifoscb_t { + + + u32 free2; + + u32 free3[4]; + + /* Need to be here for compatibility with AsynchFGTxCode */ + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u16 status; + u16 free5; + + u32 free4; + + u16 next_scb; + u16 sub_list_ptr; + + u16 entry_point; + u16 this_spb; + + u32 free6[2]; + + u16 stat_reg_addr; + u16 fifo_pointer; + + u16 fifo_base_addr; + u16 st_mo_format; + + u32 free1; +} spdifoscb_t; + + + +typedef struct _asynch_fg_rx_scb_t { + + u16 bot_buf_mask; + u16 buf_Mask; + + u16 max; + u16 min; + + u16 old_producer_pointer; + u16 hfg_scb_ptr; + + u16 delta; + u16 adjust_count; + + u32 unused2[5]; + + u16 sibling_ptr; + u16 child_ptr; + + u16 code_ptr; + u16 this_ptr; + + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u32 unused_phi_incr; + + u16 righttarg; + u16 left_targ; + + u16 rightVol; + u16 leftVol; +} asynch_fg_rx_scb_t; + + + +typedef struct _asynch_fg_tx_scb_t { + u16 not_buf_mask; + u16 buf_mask; + + u16 Max; + u16 min; + + u16 unused1; + u16 hfg_scb_ptr; + + u16 delta; + u16 adjust_count; + + u32 accum_phi; + + u16 unused2; + u16 const_one_third; + + u32 unused3[3]; + + u16 sibling_ptr; + u16 child_ptr; + + u16 codePtr; + u16 this_ptr; + + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u32 phi_incr; + + u16 unused_right_targ; + u16 unused_left_targ; + + u16 unused_right_vol; + u16 unused_left_vol; +} asynch_fg_tx_scb_t; + + +typedef struct _output_snoop_scb_t { + /* First 13 dwords from generic_scb_t */ + basic_dma_req_t basic_req; /* Optional */ + scatter_gather_ext_t sg_ext; /* Optional */ + u16 next_scb; /* REQUIRED */ + u16 sub_list_ptr; /* REQUIRED */ + + u16 entry_point; /* REQUIRED */ + u16 this_spb; /* REQUIRED */ + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + u16 init_snoop_input_link; + u16 snoop_child_input_scb; + + u32 snoop_input_buf_ptr; + + u16 reserved; + u16 input_scb; +} output_snoop_scb_t; + +typedef struct _spio_write_scb_t { + u16 address1; + u16 address2; + + u32 data1; + + u32 data2; + + u16 address3; + u16 address4; + + u32 data3; + + u32 data4; + + u16 unused1; + u16 data_ptr; + + u32 unused2[2]; + + u16 sibling_ptr; + u16 child_ptr; + + u16 entry_point; + u16 this_ptr; + + u32 unused3[5]; +} spio_write_scb_t; + +typedef struct _magic_snoop_task_t { + u32 i0; + u32 i1; + + u32 strm_buf_ptr1; + + u16 i2; + u16 snoop_scb; + + u32 i3; + u32 i4; + u32 i5; + u32 i6; + + u32 i7; + + u16 next_scb; + u16 sub_list_ptr; + + u16 entry_point; + u16 this_ptr; + + u32 strm_buf_config; + u32 strm_buf_ptr2; + + u32 i8; + + volume_control_t vdec_vol_ctrl; +} magic_snoop_task_t; +#endif /* __DSP_SCB_TYPES_H__ */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs46xx_dsp_spos.h linux/include/sound/cs46xx_dsp_spos.h --- linux-2.4.21-rc1.orig/include/sound/cs46xx_dsp_spos.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs46xx_dsp_spos.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,229 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __CS46XX_DSP_SPOS_H__ +#define __CS46XX_DSP_SPOS_H__ + +#include "cs46xx_dsp_scb_types.h" +#include "cs46xx_dsp_task_types.h" + +#define SYMBOL_CONSTANT 0x0 +#define SYMBOL_SAMPLE 0x1 +#define SYMBOL_PARAMETER 0x2 +#define SYMBOL_CODE 0x3 + +#define SEGTYPE_SP_PROGRAM 0x00000001 +#define SEGTYPE_SP_PARAMETER 0x00000002 +#define SEGTYPE_SP_SAMPLE 0x00000003 +#define SEGTYPE_SP_COEFFICIENT 0x00000004 + +#define DSP_SPOS_UU 0x0deadul /* unused */ +#define DSP_SPOS_DC 0x0badul /* dont care */ +#define DSP_SPOS_DC_DC 0x0bad0badul /* dont care */ +#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */ +#define DSP_SPOS_UUHI 0xdeadul +#define DSP_SPOS_UULO 0xc0edul +#define DSP_SPOS_DCDC 0x0badf1d0ul /* dont care */ +#define DSP_SPOS_DCDCHI 0x0badul +#define DSP_SPOS_DCDCLO 0xf1d0ul + +#define DSP_MAX_TASK_NAME 60 +#define DSP_MAX_SYMBOL_NAME 100 +#define DSP_MAX_SCB_NAME 60 +#define DSP_MAX_SCB_DESC 200 +#define DSP_MAX_TASK_DESC 50 + +#define DSP_MAX_PCM_CHANNELS 32 +#define DSP_MAX_SRC_NR 6 + +#define DSP_PCM_MAIN_CHANNEL 1 +#define DSP_PCM_REAR_CHANNEL 2 +#define DSP_PCM_CENTER_CHANNEL 3 +#define DSP_PCM_LFE_CHANNEL 4 +#define DSP_IEC958_CHANNEL 5 + +#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1 +#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2 +#define DSP_SPDIF_STATUS_HW_ENABLED 4 + +struct _dsp_module_desc_t; + +typedef struct _symbol_entry_t { + u32 address; + char symbol_name[DSP_MAX_SYMBOL_NAME]; + int symbol_type; + + /* initialized by driver */ + struct _dsp_module_desc_t * module; + int deleted; +} symbol_entry_t; + +typedef struct _symbol_desc_t { + int nsymbols; + + symbol_entry_t * symbols; + + /* initialized by driver */ + int highest_frag_index; +} symbol_desc_t; + + +typedef struct _segment_desc_t { + int segment_type; + u32 offset; + u32 size; + u32 * data; +} segment_desc_t; + +typedef struct _dsp_module_desc_t { + char * module_name; + symbol_desc_t symbol_table; + int nsegments; + segment_desc_t * segments; + + /* initialized by driver */ + u32 overlay_begin_address; + u32 load_address; + int nfixups; +} dsp_module_desc_t; + +typedef struct _dsp_scb_descriptor_t { + char scb_name[DSP_MAX_SCB_NAME]; + u32 address; + int index; + + struct _dsp_scb_descriptor_t * sub_list_ptr; + struct _dsp_scb_descriptor_t * next_scb_ptr; + struct _dsp_scb_descriptor_t * parent_scb_ptr; + + symbol_entry_t * task_entry; + symbol_entry_t * scb_symbol; + + snd_info_entry_t *proc_info; + int ref_count; + spinlock_t lock; + + int deleted; +} dsp_scb_descriptor_t; + +typedef struct _dsp_task_descriptor_t { + char task_name[DSP_MAX_TASK_NAME]; + int size; + u32 address; + int index; +} dsp_task_descriptor_t; + +typedef struct _pcm_channel_descriptor_t { + int active; + int src_slot; + int pcm_slot; + u32 sample_rate; + u32 unlinked; + dsp_scb_descriptor_t * pcm_reader_scb; + dsp_scb_descriptor_t * src_scb; + dsp_scb_descriptor_t * mixer_scb; + + void * private_data; +} pcm_channel_descriptor_t; + +typedef struct _dsp_spos_instance_t { + symbol_desc_t symbol_table; /* currently availble loaded symbols in SP */ + + int nmodules; + dsp_module_desc_t * modules; /* modules loaded into SP */ + + segment_desc_t code; + + /* Main PCM playback mixer */ + dsp_scb_descriptor_t * master_mix_scb; + u16 dac_volume_right; + u16 dac_volume_left; + + /* Rear PCM playback mixer */ + dsp_scb_descriptor_t * rear_mix_scb; + + int npcm_channels; + int nsrc_scb; + pcm_channel_descriptor_t pcm_channels[DSP_MAX_PCM_CHANNELS]; + int src_scb_slots[DSP_MAX_SRC_NR]; + + /* cache this symbols */ + symbol_entry_t * null_algorithm; /* used by PCMreaderSCB's */ + symbol_entry_t * s16_up; /* used by SRCtaskSCB's */ + + /* proc fs */ + snd_card_t * snd_card; + snd_info_entry_t * proc_dsp_dir; + snd_info_entry_t * proc_sym_info_entry; + snd_info_entry_t * proc_modules_info_entry; + snd_info_entry_t * proc_parameter_dump_info_entry; + snd_info_entry_t * proc_sample_dump_info_entry; + + /* SCB's descriptors */ + int nscb; + int scb_highest_frag_index; + dsp_scb_descriptor_t scbs[DSP_MAX_SCB_DESC]; + snd_info_entry_t * proc_scb_info_entry; + dsp_scb_descriptor_t * the_null_scb; + + /* Task's descriptors */ + int ntask; + dsp_task_descriptor_t tasks[DSP_MAX_TASK_DESC]; + snd_info_entry_t * proc_task_info_entry; + + /* SPDIF status */ + int spdif_status_out; + int spdif_status_in; + u16 spdif_input_volume_right; + u16 spdif_input_volume_left; + /* spdif channel status, + left right and user validity bits */ + unsigned int spdif_csuv_default; + unsigned int spdif_csuv_stream; + + /* SPDIF input sample rate converter */ + dsp_scb_descriptor_t * spdif_in_src; + /* SPDIF input asynch. receiver */ + dsp_scb_descriptor_t * asynch_rx_scb; + + /* Capture record mixer SCB */ + dsp_scb_descriptor_t * record_mixer_scb; + + /* CODEC input SCB */ + dsp_scb_descriptor_t * codec_in_scb; + + /* reference snooper */ + dsp_scb_descriptor_t * ref_snoop_scb; + + /* SPDIF output PCM reference */ + dsp_scb_descriptor_t * spdif_pcm_input_scb; + + /* asynch TX task */ + dsp_scb_descriptor_t * asynch_tx_scb; + + /* record sources */ + dsp_scb_descriptor_t * pcm_input; + dsp_scb_descriptor_t * adc_input; + + int spdif_in_sample_rate; +} dsp_spos_instance_t; + +#endif /* __DSP_SPOS_H__ */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs46xx_dsp_task_types.h linux/include/sound/cs46xx_dsp_task_types.h --- linux-2.4.21-rc1.orig/include/sound/cs46xx_dsp_task_types.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs46xx_dsp_task_types.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,215 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * NOTE: comments are copy/paste from cwcemb80.lst + * provided by Tom Woller at Cirrus (my only + * documentation about the SP OS running inside + * the DSP) + */ + +#ifndef __CS46XX_DSP_TASK_TYPES_H__ +#define __CS46XX_DSP_TASK_TYPES_H__ + +/********************************************************************************************* +Example hierarchy of stream control blocks in the SP + +hfgTree +Ptr____Call (c) + \ + -------+------ ------------- ------------- ------------- ----- +| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul | +| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r + -------------- (g) ------------- ------------- ------------- ----- + |c |c |c |c + | | | | + \/ ------------- ------------- ------------- + | Foreground |_\ | Middlegr'nd |_\ | Background |_\ + | tree |g/ | tree |g/ | tree |g/ + ------------- ------------- ------------- + |c |c |c + | | | + \/ \/ \/ + +*********************************************************************************************/ + +#define HFG_FIRST_EXECUTE_MODE 0x0001 +#define HFG_FIRST_EXECUTE_MODE_BIT 0 +#define HFG_CONTEXT_SWITCH_MODE 0x0002 +#define HFG_CONTEXT_SWITCH_MODE_BIT 1 + +#define MAX_FG_STACK_SIZE 32 // THESE NEED TO BE COMPUTED PROPERLY +#define MAX_MG_STACK_SIZE 16 +#define MAX_BG_STACK_SIZE 9 +#define MAX_HFG_STACK_SIZE 4 + +#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep + This should only ever be used on the Background thread */ +#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */ +#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread + This should only ever be used on the Background thread */ + +#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep + at the end of BG */ + +/* Minimal context save area for Hyper Forground */ +typedef struct _hf_save_area_t { + u32 r10_save; + u32 r54_save; + u32 r98_save; + + u16 status_save; + u16 ind_save; + + u16 rci1_save; + u16 rci0_save; + + u32 r32_save; + u32 r76_save; + u32 rsd2_save; + + u16 rsi2_save; /* See TaskTreeParameterBlock for + remainder of registers */ + u16 rsa2Save; + /* saved as part of HFG context */ +} hf_save_area_t; + + +/* Task link data structure */ +typedef struct _tree_link_t { + /* Pointer to sibling task control block */ + u16 next_scb; + /* Pointer to child task control block */ + u16 sub_ptr; + + /* Pointer to code entry point */ + u16 entry_point; + /* Pointer to local data */ + u16 this_spb; +} tree_link_t; + + +typedef struct _task_tree_data_t { + /* Initial tock count; controls task tree execution rate */ + u16 tock_count_limit; + /* Tock down counter */ + u16 tock_count; + + /* Add to ActiveCount when TockCountLimit reached: + Subtract on task tree termination */ + u16 active_tncrement; + /* Number of pending activations for task tree */ + u16 active_count; + + /* BitNumber to enable modification of correct bit in ActiveTaskFlags */ + u16 active_bit; + /* Pointer to OS location for indicating current activity on task level */ + u16 active_task_flags_ptr; + + /* Data structure for controlling movement of memory blocks:- + currently unused */ + u16 mem_upd_ptr; + /* Data structure for controlling synchronous link update */ + u16 link_upd_ptr; + + /* Save area for remainder of full context. */ + u16 save_area; + /* Address of start of local stack for data storage */ + u16 data_stack_base_ptr; + +} task_tree_data_t; + + + +typedef struct _interval_timer_data_t +{ + /* These data items have the same relative locations to those */ + u16 interval_timer_period; + u16 itd_unused; + + /* used for this data in the SPOS control block for SPOS 1.0 */ + u16 num_FG_ticks_this_interval; + u16 num_intervals; +} interval_timer_data_t; + + +/* This structure contains extra storage for the task tree + Currently, this additional data is related only to a full context save */ +typedef struct _task_tree_context_block_t { + /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for + The access to the context switch (call or interrupt), and 1 spare that + users should never use. This last may be required by the system */ + u16 stack1; + u16 stack0; + u16 stack3; + u16 stack2; + u16 stack5; + u16 stack4; + u16 stack7; + u16 stack6; + u16 stack9; + u16 stack8; + + u32 saverfe; + + /* Value may be overwriten by stack save algorithm. + Retain the size of the stack data saved here if used */ + u16 reserved1; + u16 stack_size; + u32 saverba; /* (HFG) */ + u32 saverdc; + u32 savers_config_23; /* (HFG) */ + u32 savers_DMA23; /* (HFG) */ + u32 saversa0; + u32 saversi0; + u32 saversa1; + u32 saversi1; + u32 saversa3; + u32 saversd0; + u32 saversd1; + u32 saversd3; + u32 savers_config01; + u32 savers_DMA01; + u32 saveacc0hl; + u32 saveacc1hl; + u32 saveacc0xacc1x; + u32 saveacc2hl; + u32 saveacc3hl; + u32 saveacc2xacc3x; + u32 saveaux0hl; + u32 saveaux1hl; + u32 saveaux0xaux1x; + u32 saveaux2hl; + u32 saveaux3hl; + u32 saveaux2xaux3x; + u32 savershouthl; + u32 savershoutxmacmode; +} task_tree_context_block_t; + + +typedef struct _task_tree_control_block_t { + hf_save_area_t context; + tree_link_t links; + task_tree_data_t data; + task_tree_context_block_t context_blk; + interval_timer_data_t int_timer; +} task_tree_control_block_t; + + +#endif /* __DSP_TASK_TYPES_H__ */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs8403.h linux/include/sound/cs8403.h --- linux-2.4.21-rc1.orig/include/sound/cs8403.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs8403.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,257 @@ +#ifndef __SOUND_CS8403_H +#define __SOUND_CS8403_H + +/* + * Routines for Cirrus Logic CS8403/CS8404A IEC958 (S/PDIF) Transmitter + * Copyright (c) by Jaroslav Kysela , + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef SND_CS8403 + +#ifndef SND_CS8403_DECL +#define SND_CS8403_DECL static +#endif +#ifndef SND_CS8403_DECODE +#define SND_CS8403_DECODE snd_cs8403_decode_spdif_bits +#endif +#ifndef SND_CS8403_ENCODE +#define SND_CS8403_ENCODE snd_cs8403_encode_spdif_bits +#endif + + +SND_CS8403_DECL void SND_CS8403_DECODE(snd_aes_iec958_t *diga, unsigned char bits) +{ + if (bits & 0x01) { /* consumer */ + if (!(bits & 0x02)) + diga->status[0] |= IEC958_AES0_NONAUDIO; + if (!(bits & 0x08)) + diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + switch (bits & 0x10) { + case 0x10: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; break; + case 0x00: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; break; + } + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_CON_ORIGINAL; + switch (bits & 0x60) { + case 0x00: diga->status[1] |= IEC958_AES1_CON_MAGNETIC_ID; break; + case 0x20: diga->status[1] |= IEC958_AES1_CON_DIGDIGCONV_ID; break; + case 0x40: diga->status[1] |= IEC958_AES1_CON_LASEROPT_ID; break; + case 0x60: diga->status[1] |= IEC958_AES1_CON_GENERAL; break; + } + switch (bits & 0x06) { + case 0x00: diga->status[3] |= IEC958_AES3_CON_FS_44100; break; + case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_48000; break; + case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_32000; break; + } + } else { + diga->status[0] = IEC958_AES0_PROFESSIONAL; + switch (bits & 0x18) { + case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 0x10: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 0x08: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break; + case 0x18: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + switch (bits & 0x60) { + case 0x20: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break; + case 0x40: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break; + case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break; + case 0x60: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break; + } + if (bits & 0x80) + diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; + } +} + +SND_CS8403_DECL unsigned char SND_CS8403_ENCODE(snd_aes_iec958_t *diga) +{ + unsigned char bits; + + if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) { + bits = 0x01; /* consumer mode */ + if (diga->status[0] & IEC958_AES0_NONAUDIO) + bits &= ~0x02; + else + bits |= 0x02; + if (diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) + bits &= ~0x08; + else + bits |= 0x08; + switch (diga->status[0] & IEC958_AES0_CON_EMPHASIS) { + default: + case IEC958_AES0_CON_EMPHASIS_NONE: bits |= 0x10; break; + case IEC958_AES0_CON_EMPHASIS_5015: bits |= 0x00; break; + } + if (diga->status[1] & IEC958_AES1_CON_ORIGINAL) + bits &= ~0x80; + else + bits |= 0x80; + if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL) + bits |= 0x60; + else { + switch(diga->status[1] & IEC958_AES1_CON_MAGNETIC_MASK) { + case IEC958_AES1_CON_MAGNETIC_ID: + bits |= 0x00; break; + case IEC958_AES1_CON_DIGDIGCONV_ID: + bits |= 0x20; break; + default: + case IEC958_AES1_CON_LASEROPT_ID: + bits |= 0x40; break; + } + } + switch (diga->status[3] & IEC958_AES3_CON_FS) { + default: + case IEC958_AES3_CON_FS_44100: bits |= 0x00; break; + case IEC958_AES3_CON_FS_48000: bits |= 0x02; break; + case IEC958_AES3_CON_FS_32000: bits |= 0x04; break; + } + } else { + bits = 0x00; /* professional mode */ + if (diga->status[0] & IEC958_AES0_NONAUDIO) + bits &= ~0x02; + else + bits |= 0x02; + /* CHECKME: I'm not sure about the bit order in val here */ + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_32000: bits |= 0x00; break; + case IEC958_AES0_PRO_FS_44100: bits |= 0x10; break; /* 44.1kHz */ + case IEC958_AES0_PRO_FS_48000: bits |= 0x08; break; /* 48kHz */ + default: + case IEC958_AES0_PRO_FS_NOTID: bits |= 0x18; break; + } + switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) { + case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x20; break; + case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x40; break; + case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break; + default: + case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x60; break; + } + switch (diga->status[1] & IEC958_AES1_PRO_MODE) { + case IEC958_AES1_PRO_MODE_TWO: + case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break; + default: bits |= 0x80; break; + } + } + return bits; +} + +#endif /* SND_CS8403 */ + +#ifdef SND_CS8404 + +#ifndef SND_CS8404_DECL +#define SND_CS8404_DECL static +#endif +#ifndef SND_CS8404_DECODE +#define SND_CS8404_DECODE snd_cs8404_decode_spdif_bits +#endif +#ifndef SND_CS8404_ENCODE +#define SND_CS8404_ENCODE snd_cs8404_encode_spdif_bits +#endif + + +SND_CS8404_DECL void SND_CS8404_DECODE(snd_aes_iec958_t *diga, unsigned char bits) +{ + if (bits & 0x10) { /* consumer */ + if (!(bits & 0x20)) + diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + if (!(bits & 0x40)) + diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_CON_ORIGINAL; + switch (bits & 0x03) { + case 0x00: diga->status[1] |= IEC958_AES1_CON_DAT; break; + case 0x03: diga->status[1] |= IEC958_AES1_CON_GENERAL; break; + } + switch (bits & 0x06) { + case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_32000; break; + case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_48000; break; + case 0x06: diga->status[3] |= IEC958_AES3_CON_FS_44100; break; + } + } else { + diga->status[0] = IEC958_AES0_PROFESSIONAL; + if (!(bits & 0x04)) + diga->status[0] |= IEC958_AES0_NONAUDIO; + switch (bits & 0x60) { + case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 0x40: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 0x20: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break; + case 0x60: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + switch (bits & 0x03) { + case 0x02: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break; + case 0x01: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break; + case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break; + case 0x03: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break; + } + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; + } +} + +SND_CS8404_DECL unsigned char SND_CS8404_ENCODE(snd_aes_iec958_t *diga) +{ + unsigned char bits; + + if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) { + bits = 0x10; /* consumer mode */ + if (!(diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT)) + bits |= 0x20; + if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_NONE) + bits |= 0x40; + if (!(diga->status[1] & IEC958_AES1_CON_ORIGINAL)) + bits |= 0x80; + if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL) + bits |= 0x03; + switch (diga->status[3] & IEC958_AES3_CON_FS) { + default: + case IEC958_AES3_CON_FS_44100: bits |= 0x06; break; + case IEC958_AES3_CON_FS_48000: bits |= 0x04; break; + case IEC958_AES3_CON_FS_32000: bits |= 0x02; break; + } + } else { + bits = 0x00; /* professional mode */ + if (!(diga->status[0] & IEC958_AES0_NONAUDIO)) + bits |= 0x04; + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_32000: bits |= 0x00; break; + case IEC958_AES0_PRO_FS_44100: bits |= 0x40; break; /* 44.1kHz */ + case IEC958_AES0_PRO_FS_48000: bits |= 0x20; break; /* 48kHz */ + default: + case IEC958_AES0_PRO_FS_NOTID: bits |= 0x00; break; + } + switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) { + case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x02; break; + case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x01; break; + case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break; + default: + case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x03; break; + } + switch (diga->status[1] & IEC958_AES1_PRO_MODE) { + case IEC958_AES1_PRO_MODE_TWO: + case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break; + default: bits |= 0x80; break; + } + } + return bits; +} + +#endif /* SND_CS8404 */ + +#endif /* __SOUND_CS8403_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/cs8427.h linux/include/sound/cs8427.h --- linux-2.4.21-rc1.orig/include/sound/cs8427.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/cs8427.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,195 @@ +#ifndef __SOUND_CS8427_H +#define __SOUND_CS8427_H + +/* + * Routines for Cirrus Logic CS8427 + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#define CS8427_BASE_ADDR 0x10 /* base I2C address */ + +#define CS8427_REG_AUTOINC 0x80 /* flag - autoincrement */ +#define CS8427_REG_CONTROL1 0x01 +#define CS8427_REG_CONTROL2 0x02 +#define CS8427_REG_DATAFLOW 0x03 +#define CS8427_REG_CLOCKSOURCE 0x04 +#define CS8427_REG_SERIALINPUT 0x05 +#define CS8427_REG_SERIALOUTPUT 0x06 +#define CS8427_REG_INT1STATUS 0x07 +#define CS8427_REG_INT2STATUS 0x08 +#define CS8427_REG_INT1MASK 0x09 +#define CS8427_REG_INT1MODEMSB 0x0a +#define CS8427_REG_INT1MODELSB 0x0b +#define CS8427_REG_INT2MASK 0x0c +#define CS8427_REG_INT2MODEMSB 0x0d +#define CS8427_REG_INT2MODELSB 0x0e +#define CS8427_REG_RECVCSDATA 0x0f +#define CS8427_REG_RECVERRORS 0x10 +#define CS8427_REG_RECVERRMASK 0x11 +#define CS8427_REG_CSDATABUF 0x12 +#define CS8427_REG_UDATABUF 0x13 +#define CS8427_REG_QSUBCODE 0x14 /* 0x14-0x1d (10 bytes) */ +#define CS8427_REG_OMCKRMCKRATIO 0x1e +#define CS8427_REG_CORU_DATABUF 0x20 +#define CS8427_REG_ID_AND_VER 0x7f + +/* CS8427_REG_CONTROL1 bits */ +#define CS8427_SWCLK (1<<7) /* 0 = RMCK default, 1 = OMCK output on RMCK pin */ +#define CS8427_VSET (1<<6) /* 0 = valid PCM data, 1 = invalid PCM data */ +#define CS8427_MUTESAO (1<<5) /* mute control for the serial audio output port, 0 = disabled, 1 = enabled */ +#define CS8427_MUTEAES (1<<4) /* mute control for the AES transmitter output, 0 = disabled, 1 = enabled */ +#define CS8427_INTMASK (3<<1) /* interrupt output pin setup mask */ +#define CS8427_INTACTHIGH (0<<1) /* active high */ +#define CS8427_INTACTLOW (1<<1) /* active low */ +#define CS8427_INTOPENDRAIN (2<<1) /* open drain, active low */ +#define CS8427_TCBLDIR (1<<0) /* 0 = TCBL is an input, 1 = TCBL is an output */ + +/* CS8427_REQ_CONTROL2 bits */ +#define CS8427_HOLDMASK (3<<5) /* action when a receiver error occurs */ +#define CS8427_HOLDLASTSAMPLE (0<<5) /* hold the last valid sample */ +#define CS8427_HOLDZERO (1<<5) /* replace the current audio sample with zero (mute) */ +#define CS8427_HOLDNOCHANGE (2<<5) /* do not change the received audio sample */ +#define CS8427_RMCKF (1<<4) /* 0 = 256*Fsi, 1 = 128*Fsi */ +#define CS8427_MMR (1<<3) /* AES3 receiver operation, 0 = stereo, 1 = mono */ +#define CS8427_MMT (1<<2) /* AES3 transmitter operation, 0 = stereo, 1 = mono */ +#define CS8427_MMTCS (1<<1) /* 0 = use A + B CS data, 1 = use MMTLR CS data */ +#define CS8427_MMTLR (1<<0) /* 0 = use A CS data, 1 = use B CS data */ + +/* CS8427_REG_DATAFLOW */ +#define CS8427_TXOFF (1<<6) /* AES3 transmitter Output, 0 = normal operation, 1 = off (0V) */ +#define CS8427_AESBP (1<<5) /* AES3 hardware bypass mode, 0 = normal, 1 = bypass (RX->TX) */ +#define CS8427_TXDMASK (3<<3) /* AES3 Transmitter Data Source Mask */ +#define CS8427_TXDSERIAL (1<<3) /* TXD - serial audio input port */ +#define CS8427_TXAES3DRECEIVER (2<<3) /* TXD - AES3 receiver */ +#define CS8427_SPDMASK (3<<1) /* Serial Audio Output Port Data Source Mask */ +#define CS8427_SPDSERIAL (1<<1) /* SPD - serial audio input port */ +#define CS8427_SPDAES3RECEIVER (2<<1) /* SPD - AES3 receiver */ + +/* CS8427_REG_CLOCKSOURCE */ +#define CS8427_RUN (1<<6) /* 0 = clock off, 1 = clock on */ +#define CS8427_CLKMASK (3<<4) /* OMCK frequency mask */ +#define CS8427_CLK256 (0<<4) /* 256*Fso */ +#define CS8427_CLK384 (1<<4) /* 384*Fso */ +#define CS8427_CLK512 (2<<4) /* 512*Fso */ +#define CS8427_OUTC (1<<3) /* Output Time Base, 0 = OMCK, 1 = recovered input clock */ +#define CS8427_INC (1<<2) /* Input Time Base Clock Source, 0 = recoverd input clock, 1 = OMCK input pin */ +#define CS8427_RXDMASK (3<<0) /* Recovered Input Clock Source Mask */ +#define CS8427_RXDILRCK (0<<0) /* 256*Fsi from ILRCK pin */ +#define CS8427_RXDAES3INPUT (1<<0) /* 256*Fsi from AES3 input */ +#define CS8427_EXTCLOCKRESET (2<<0) /* bypass PLL, 256*Fsi clock, synchronous reset */ +#define CS8427_EXTCLOCK (3<<0) /* bypass PLL, 256*Fsi clock */ + +/* CS8427_REG_SERIALINPUT */ +#define CS8427_SIMS (1<<7) /* 0 = slave, 1 = master mode */ +#define CS8427_SISF (1<<6) /* ISCLK freq, 0 = 64*Fsi, 1 = 128*Fsi */ +#define CS8427_SIRESMASK (3<<4) /* Resolution of the input data for right justified formats */ +#define CS8427_SIRES24 (0<<4) /* SIRES 24-bit */ +#define CS8427_SIRES20 (1<<4) /* SIRES 20-bit */ +#define CS8427_SIRES16 (2<<4) /* SIRES 16-bit */ +#define CS8427_SIJUST (1<<3) /* Justification of SDIN data relative to ILRCK, 0 = left-justified, 1 = right-justified */ +#define CS8427_SIDEL (1<<2) /* Delay of SDIN data relative to ILRCK for left-justified data formats, 0 = first ISCLK period, 1 = second ISCLK period */ +#define CS8427_SISPOL (1<<1) /* ICLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */ +#define CS8427_SILRPOL (1<<0) /* ILRCK clock polarity, 0 = SDIN data left channel when ILRCK is high, 1 = SDIN right when ILRCK is high */ + +/* CS8427_REG_SERIALOUTPUT */ +#define CS8427_SOMS (1<<7) /* 0 = slave, 1 = master mode */ +#define CS8427_SOSF (1<<6) /* OSCLK freq, 0 = 64*Fso, 1 = 128*Fso */ +#define CS8427_SORESMASK (3<<4) /* Resolution of the output data on SDOUT and AES3 output */ +#define CS8427_SORES24 (0<<4) /* SIRES 24-bit */ +#define CS8427_SORES20 (1<<4) /* SIRES 20-bit */ +#define CS8427_SORES16 (2<<4) /* SIRES 16-bit */ +#define CS8427_SORESDIRECT (2<<4) /* SIRES direct copy from AES3 receiver */ +#define CS8427_SOJUST (1<<3) /* Justification of SDOUT data relative to OLRCK, 0 = left-justified, 1 = right-justified */ +#define CS8427_SODEL (1<<2) /* Delay of SDOUT data relative to OLRCK for left-justified data formats, 0 = first OSCLK period, 1 = second OSCLK period */ +#define CS8427_SOSPOL (1<<1) /* OSCLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */ +#define CS8427_SOLRPOL (1<<0) /* OLRCK clock polarity, 0 = SDOUT data left channel when OLRCK is high, 1 = SDOUT right when OLRCK is high */ + +/* CS8427_REG_INT1STATUS */ +#define CS8427_TSLIP (1<<7) /* AES3 transmitter source data slip interrupt */ +#define CS8427_OSLIP (1<<6) /* Serial audio output port data slip interrupt */ +#define CS8427_DETC (1<<2) /* D to E C-buffer transfer interrupt */ +#define CS8427_EFTC (1<<1) /* E to F C-buffer transfer interrupt */ +#define CS8427_RERR (1<<0) /* A receiver error has occurred */ + +/* CS8427_REG_INT2STATUS */ +#define CS8427_DETU (1<<3) /* D to E U-buffer transfer interrupt */ +#define CS8427_EFTU (1<<2) /* E to F U-buffer transfer interrupt */ +#define CS8427_QCH (1<<1) /* A new block of Q-subcode data is available for reading */ + +/* CS8427_REG_INT1MODEMSB && CS8427_REG_INT1MODELSB */ +/* bits are defined in CS8427_REG_INT1STATUS */ +/* CS8427_REG_INT2MODEMSB && CS8427_REG_INT2MODELSB */ +/* bits are defined in CS8427_REG_INT2STATUS */ +#define CS8427_INTMODERISINGMSB 0 +#define CS8427_INTMODERESINGLSB 0 +#define CS8427_INTMODEFALLINGMSB 0 +#define CS8427_INTMODEFALLINGLSB 1 +#define CS8427_INTMODELEVELMSB 1 +#define CS8427_INTMODELEVELLSB 0 + +/* CS8427_REG_RECVCSDATA */ +#define CS8427_AUXMASK (15<<4) /* auxiliary data field width */ +#define CS8427_AUXSHIFT 4 +#define CS8427_PRO (1<<3) /* Channel status block format indicator */ +#define CS8427_AUDIO (1<<2) /* Audio indicator (0 = audio, 1 = nonaudio */ +#define CS8427_COPY (1<<1) /* 0 = copyright asserted, 1 = copyright not asserted */ +#define CS8427_ORIG (1<<0) /* SCMS generation indicator, 0 = 1st generation or highter, 1 = original */ + +/* CS8427_REG_RECVERRORS */ +/* CS8427_REG_RECVERRMASK for CS8427_RERR */ +#define CS8427_QCRC (1<<6) /* Q-subcode data CRC error indicator */ +#define CS8427_CCRC (1<<5) /* Chancnel Status Block Cyclick Redundancy Check Bit */ +#define CS8427_UNLOCK (1<<4) /* PLL lock status bit */ +#define CS8427_V (1<<3) /* 0 = valid data */ +#define CS8427_CONF (1<<2) /* Confidence bit */ +#define CS8427_BIP (1<<1) /* Bi-phase error bit */ +#define CS8427_PAR (1<<0) /* Parity error */ + +/* CS8427_REG_CSDATABUF */ +#define CS8427_BSEL (1<<5) /* 0 = CS data, 1 = U data */ +#define CS8427_CBMR (1<<4) /* 0 = overwrite first 5 bytes for CS D to E buffer, 1 = prevent */ +#define CS8427_DETCI (1<<3) /* D to E CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_EFTCI (1<<2) /* E to F CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_CAM (1<<1) /* CS data buffer control port access mode bit, 0 = one byte, 1 = two byte */ +#define CS8427_CHS (1<<0) /* Channel select bit, 0 = Channel A, 1 = Channel B */ + +/* CS8427_REG_UDATABUF */ +#define CS8427_UD (1<<4) /* User data pin (U) direction, 0 = input, 1 = output */ +#define CS8427_UBMMASK (3<<2) /* Operating mode of the AES3 U bit manager */ +#define CS8427_UBMZEROS (0<<2) /* transmit all zeros mode */ +#define CS8427_UBMBLOCK (1<<2) /* block mode */ +#define CS8427_DETUI (1<<1) /* D to E U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_EFTUI (1<<1) /* E to F U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ + +/* CS8427_REG_ID_AND_VER */ +#define CS8427_IDMASK (15<<4) +#define CS8427_IDSHIFT 4 +#define CS8427_VERMASK (15<<0) +#define CS8427_VERSHIFT 0 +#define CS8427_VER8427A 0x71 + +int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr); +int snd_cs8427_create(snd_i2c_bus_t *bus, unsigned char addr, snd_i2c_device_t **r_cs8427); +int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, snd_pcm_substream_t *playback_substream, snd_pcm_substream_t *capture_substream); +int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active); +int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate); + +#endif /* __SOUND_CS8427_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/driver.h linux/include/sound/driver.h --- linux-2.4.21-rc1.orig/include/sound/driver.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/driver.h 2003-04-29 05:42:08.000000000 -0600 @@ -0,0 +1,67 @@ +#ifndef __SOUND_DRIVER_H +#define __SOUND_DRIVER_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef ALSA_BUILD +#include "config.h" +#endif + +#include +#include + +#define SNDRV_CARDS 8 /* number of supported soundcards - don't change - minor numbers */ + +#ifndef CONFIG_SND_MAJOR /* standard configuration */ +#define CONFIG_SND_MAJOR 116 +#endif + +#ifndef CONFIG_SND_DEBUG +#undef CONFIG_SND_DEBUG_MEMORY +#endif + +/*#ifdef ALSA_BUILD*/ +#include "adriver.h" +/*#endif*/ + +#include + +/* + * ========================================================================== + */ + +#ifdef CONFIG_SND_DEBUG_MEMORY +#include +#include +void *snd_wrapper_kmalloc(size_t, int); +#undef kmalloc +void snd_wrapper_kfree(const void *); +#undef kfree +void *snd_wrapper_vmalloc(size_t); +#undef vmalloc +void snd_wrapper_vfree(void *); +#undef vfree +#endif + +#include "sndmagic.h" + +#endif /* __SOUND_DRIVER_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/emu10k1.h linux/include/sound/emu10k1.h --- linux-2.4.21-rc1.orig/include/sound/emu10k1.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/emu10k1.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,1360 @@ +#ifndef __SOUND_EMU10K1_H +#define __SOUND_EMU10K1_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Creative Labs, Inc. + * Definitions for EMU10K1 (SB Live!) chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include + +#ifndef PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#endif +#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#endif + +/* ------------------- DEFINES -------------------- */ + +#define EMUPAGESIZE 4096 +#define MAXREQVOICES 8 +#define MAXPAGES 8192 +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 + +#define EMU10K1_DMA_MASK 0x1fffffffUL +#define AUDIGY_DMA_MASK 0xffffffffUL + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) + +// Audigy specify registers are prefixed with 'A_' + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ +#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ + /* channel number of the register to be */ + /* accessed. For non per-channel registers the */ + /* value should be set to zero. */ +#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ +#define A_PTR_ADDRESS_MASK 0x0fff0000 + +#define DATA 0x04 /* Indexed register set data register */ + +#define IPR 0x08 /* Global interrupt pending register */ + /* Clear pending interrupts by writing a 1 to */ + /* the relevant bits and zero to the other bits */ + +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ +#define IPR_A_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ + +#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ +#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ +#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */ +#define IPR_PCIERROR 0x00200000 /* PCI bus error */ +#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */ +#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */ +#define IPR_MUTE 0x00040000 /* Mute button pressed */ +#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */ +#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */ +#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */ +#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */ +#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */ +#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */ +#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */ +#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */ +#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */ +#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */ +#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */ +#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */ +#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ + /* Highest set channel in CLIPL or CLIPH. When */ + /* IP is written with CL set, the bit in CLIPL */ + /* or CLIPH corresponding to the CIN value */ + /* written will be cleared. */ + +#define INTE 0x0c /* Interrupt enable register */ +#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ +#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */ +#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */ +#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */ +#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */ +#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */ +#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */ +#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */ +#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */ +#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */ +#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */ +#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */ +#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */ +#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */ +#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */ +#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */ +#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */ +#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */ + +#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */ + /* NOTE: There is no reason to use this under */ + /* Linux, and it will cause odd hardware */ + /* behavior and possibly random segfaults and */ + /* lockups if enabled. */ + +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define INTE_A_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_A_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ + + +#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ + /* NOTE: This bit must always be enabled */ +#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */ +#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */ +#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */ +#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */ +#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */ +#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */ +#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */ +#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */ +#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */ +#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */ +#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */ +#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ + +#define WC 0x10 /* Wall Clock register */ +#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ +#define WC_SAMPLECOUNTER 0x14060010 +#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ + /* NOTE: Each channel takes 1/64th of a sample */ + /* period to be serviced. */ + +#define HCFG 0x14 /* Hardware config register */ + /* NOTE: There is no reason to use the legacy */ + /* SoundBlaster emulation stuff described below */ + /* under Linux, and all kinds of weird hardware */ + /* behavior can result if you try. Don't. */ +#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number */ +#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU */ +#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB */ +#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD */ +#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC */ +#define HCFG_LEGACYFUNC_MDMA 0xa0000000 /* Legacy MDMA */ +#define HCFG_LEGACYFUNC_SPCI 0xc0000000 /* Legacy SPCI */ +#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA */ +#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. */ +#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read */ +#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte */ +#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */ + /* NOTE: The rest of the bits in this register */ + /* _are_ relevant under Linux. */ +#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ +#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ +#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ +#define HCFG_GPINPUT0 0x00004000 /* External pin112 */ +#define HCFG_GPINPUT1 0x00002000 /* External pin110 */ +#define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ +#define HCFG_GPOUT0 0x00001000 /* External pin? (spdif enable on 5.1) */ +#define HCFG_GPOUT1 0x00000800 /* External pin? (IR) */ +#define HCFG_GPOUT2 0x00000400 /* External pin? (IR) */ +#define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ +#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ + /* 1 = Force all 3 async digital inputs to use */ + /* the same async sample rate tracker (ZVIDEO) */ +#define HCFG_AC3ENABLE_MASK 0x000000e0 /* AC3 async input control - Not implemented */ +#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ +#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ +#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* they are not rate-locked to the external */ + /* async audio source */ +#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 +#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ + /* NOTE: This is a 'cheap' way to implement a */ + /* master mute function on the mute button, and */ + /* in general should not be used unless a more */ + /* sophisticated master mute function has not */ + /* been written. */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + /* Should be set to 1 when the EMU10K1 is */ + /* completely initialized. */ + +//For Audigy, MPU port move to 0x70-0x74 ptr register + +#define MUDATA 0x18 /* MPU401 data register (8 bits) */ + +#define MUCMD 0x19 /* MPU401 command register (8 bits) */ +#define MUCMD_RESET 0xff /* RESET command */ +#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */ + /* NOTE: All other commands are ignored */ + +#define MUSTAT MUCMD /* MPU401 status register (8 bits) */ +#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ +#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ + +#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ +#define A_GPINPUT_MASK 0xff00 +#define A_GPOUTPUT_MASK 0x00ff +#define A_IOCFG_GPOUT0 0x0044 /* analog/digital? */ +#define A_IOCFG_GPOUT1 0x0002 /* IR */ +#define A_IOCFG_GPOUT2 0x0001 /* IR */ + +#define TIMER 0x1a /* Timer terminal count register */ + /* NOTE: After the rate is changed, a maximum */ + /* of 1024 sample periods should be allowed */ + /* before the new rate is guaranteed accurate. */ +#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ + /* 0 == 1024 periods, [1..4] are not useful */ +#define TIMER_RATE 0x0a00001a + +#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ + +#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ +#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ +#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ + +/************************************************************************************************/ +/* PCI function 1 registers, address = + PCIBASE1 */ +/************************************************************************************************/ + +#define JOYSTICK1 0x00 /* Analog joystick port register */ +#define JOYSTICK2 0x01 /* Analog joystick port register */ +#define JOYSTICK3 0x02 /* Analog joystick port register */ +#define JOYSTICK4 0x03 /* Analog joystick port register */ +#define JOYSTICK5 0x04 /* Analog joystick port register */ +#define JOYSTICK6 0x05 /* Analog joystick port register */ +#define JOYSTICK7 0x06 /* Analog joystick port register */ +#define JOYSTICK8 0x07 /* Analog joystick port register */ + +/* When writing, any write causes JOYSTICK_COMPARATOR output enable to be pulsed on write. */ +/* When reading, use these bitfields: */ +#define JOYSTICK_BUTTONS 0x0f /* Joystick button data */ +#define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ + + +/********************************************************************************************************/ +/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ +/********************************************************************************************************/ + +#define CPF 0x00 /* Current pitch and fraction register */ +#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */ +#define CPF_CURRENTPITCH 0x10100000 +#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ +#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */ +#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */ + +#define PTRX 0x01 /* Pitch target and send A/B amounts register */ +#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */ +#define PTRX_PITCHTARGET 0x10100001 +#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */ +#define PTRX_FXSENDAMOUNT_A 0x08080001 +#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */ +#define PTRX_FXSENDAMOUNT_B 0x08000001 + +#define CVCF 0x02 /* Current volume and filter cutoff register */ +#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */ +#define CVCF_CURRENTVOL 0x10100002 +#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */ +#define CVCF_CURRENTFILTER 0x10000002 + +#define VTFT 0x03 /* Volume target and filter cutoff target register */ +#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ +#define VTFT_VOLUMETARGET 0x10100003 +#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ +#define VTFT_FILTERTARGET 0x10000003 + +#define Z1 0x05 /* Filter delay memory 1 register */ + +#define Z2 0x04 /* Filter delay memory 2 register */ + +#define PSST 0x06 /* Send C amount and loop start address register */ +#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */ + +#define PSST_FXSENDAMOUNT_C 0x08180006 + +#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ +#define PSST_LOOPSTARTADDR 0x18000006 + +#define DSL 0x07 /* Send D amount and loop start address register */ +#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ + +#define DSL_FXSENDAMOUNT_D 0x08180007 + +#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */ +#define DSL_LOOPENDADDR 0x18000007 + +#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ +#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ +#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ + /* 1 == full band, 7 == lowpass */ + /* ROM 0 is used when pitch shifting downward or less */ + /* then 3 semitones upward. Increasingly higher ROM */ + /* numbers are used, typically in steps of 3 semitones, */ + /* as upward pitch shifting is performed. */ +#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */ +#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */ +#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */ +#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */ +#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */ +#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */ +#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ +#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ +#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ +#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ +#define CCCA_CURRADDR 0x18000008 + +#define CCR 0x09 /* Cache control register */ +#define CCR_CACHEINVALIDSIZE 0x07190009 +#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ +#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ +#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ +#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ +#define CCR_READADDRESS 0x06100009 +#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ + /* NOTE: This is valid only if CACHELOOPFLAG is set */ +#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ + +#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ + /* NOTE: This register is normally not used */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ + +#define FXRT 0x0b /* Effects send routing register */ + /* NOTE: It is illegal to assign the same routing to */ + /* two effects sends. */ +#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ +#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ +#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ +#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ + +#define MAPA 0x0c /* Cache map A */ + +#define MAPB 0x0d /* Cache map B */ + +#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ +#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ + +#define ENVVOL 0x10 /* Volume envelope register */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ +#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ +#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ + +#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ + /* this channel and from writing to pitch, filter and */ + /* volume targets. */ +#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL1 0x13 /* Modulation LFO value */ +#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ENVVAL 0x14 /* Modulation envelope register */ +#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ +#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ +#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ + +#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL2 0x17 /* Vibrato LFO register */ +#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define IP 0x18 /* Initial pitch register */ +#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */ + /* 4 bits of octave, 12 bits of fractional octave */ +#define IP_UNITY 0x0000e000 /* Unity pitch shift */ + +#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ +#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ + /* 6 most significant bits are semitones */ + /* 2 least significant bits are fractions */ +#define IFATN_FILTERCUTOFF 0x08080019 +#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ +#define IFATN_ATTENUATION 0x08000019 + + +#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ +#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ + /* Signed 2's complement, +/- one octave peak extremes */ +#define PEFE_PITCHAMOUNT 0x0808001a +#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ + /* Signed 2's complement, +/- six octaves peak extremes */ +#define PEFE_FILTERAMOUNT 0x0800001a +#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ +#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ + /* Signed 2's complement, +/- three octave extremes */ + + +#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ +#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ + /* Signed 2's complement, with +/- 12dB extremes */ + +#define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */ + /* ??Hz steps, maximum of ?? Hz. */ +#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ +#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */ + /* 0.039Hz steps, maximum of 9.85 Hz. */ + +#define TEMPENV 0x1e /* Tempory envelope register */ +#define TEMPENV_MASK 0x0000ffff /* 16-bit value */ + /* NOTE: All channels contain internal variables; do */ + /* not write to these locations. */ + +#define CD0 0x20 /* Cache data 0 register */ +#define CD1 0x21 /* Cache data 1 register */ +#define CD2 0x22 /* Cache data 2 register */ +#define CD3 0x23 /* Cache data 3 register */ +#define CD4 0x24 /* Cache data 4 register */ +#define CD5 0x25 /* Cache data 5 register */ +#define CD6 0x26 /* Cache data 6 register */ +#define CD7 0x27 /* Cache data 7 register */ +#define CD8 0x28 /* Cache data 8 register */ +#define CD9 0x29 /* Cache data 9 register */ +#define CDA 0x2a /* Cache data A register */ +#define CDB 0x2b /* Cache data B register */ +#define CDC 0x2c /* Cache data C register */ +#define CDD 0x2d /* Cache data D register */ +#define CDE 0x2e /* Cache data E register */ +#define CDF 0x2f /* Cache data F register */ + +#define PTB 0x40 /* Page table base register */ +#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */ + +#define TCB 0x41 /* Tank cache base register */ +#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based TRAM */ + +#define ADCCR 0x42 /* ADC sample rate/stereo control register */ +#define ADCCR_RCHANENABLE 0x00000010 /* Enables right channel for writing to the host */ +#define ADCCR_LCHANENABLE 0x00000008 /* Enables left channel for writing to the host */ + /* NOTE: To guarantee phase coherency, both channels */ + /* must be disabled prior to enabling both channels. */ +#define A_ADCCR_RCHANENABLE 0x00000020 +#define A_ADCCR_LCHANENABLE 0x00000010 + +#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ +#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ +#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ +#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */ +#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */ +#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ +#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ +#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ +#define A_ADCCR_SAMPLERATE_12 0x00000006 /* 12kHz sample rate */ +#define A_ADCCR_SAMPLERATE_11 0x00000007 /* 11.025kHz sample rate */ +#define A_ADCCR_SAMPLERATE_8 0x00000008 /* 8kHz sample rate */ + +#define FXWC 0x43 /* FX output write channels register */ + /* When set, each bit enables the writing of the */ + /* corresponding FX output channel into host memory */ +#define FXWC_DEFAULTROUTE_C (1<<0) /* left emu out? */ +#define FXWC_DEFAULTROUTE_B (1<<1) /* right emu out? */ +#define FXWC_DEFAULTROUTE_A (1<<12) +#define FXWC_DEFAULTROUTE_D (1<<13) +#define FXWC_ADCLEFT (1<<18) +#define FXWC_CDROMSPDIFLEFT (1<<18) +#define FXWC_ADCRIGHT (1<<19) +#define FXWC_CDROMSPDIFRIGHT (1<<19) +#define FXWC_MIC (1<<20) +#define FXWC_ZOOMLEFT (1<<20) +#define FXWC_ZOOMRIGHT (1<<21) +#define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */ +#define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */ + +#define TCBS 0x44 /* Tank cache buffer size register */ +#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ +#define TCBS_BUFFSIZE_16K 0x00000000 +#define TCBS_BUFFSIZE_32K 0x00000001 +#define TCBS_BUFFSIZE_64K 0x00000002 +#define TCBS_BUFFSIZE_128K 0x00000003 +#define TCBS_BUFFSIZE_256K 0x00000004 +#define TCBS_BUFFSIZE_512K 0x00000005 +#define TCBS_BUFFSIZE_1024K 0x00000006 +#define TCBS_BUFFSIZE_2048K 0x00000007 + +#define MICBA 0x45 /* AC97 microphone buffer address register */ +#define MICBA_MASK 0xfffff000 /* 20 bit base address */ + +#define ADCBA 0x46 /* ADC buffer address register */ +#define ADCBA_MASK 0xfffff000 /* 20 bit base address */ + +#define FXBA 0x47 /* FX Buffer Address */ +#define FXBA_MASK 0xfffff000 /* 20 bit base address */ + +#define MICBS 0x49 /* Microphone buffer size register */ + +#define ADCBS 0x4a /* ADC buffer size register */ + +#define FXBS 0x4b /* FX buffer size register */ + +/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */ +#define ADCBS_BUFSIZE_NONE 0x00000000 +#define ADCBS_BUFSIZE_384 0x00000001 +#define ADCBS_BUFSIZE_448 0x00000002 +#define ADCBS_BUFSIZE_512 0x00000003 +#define ADCBS_BUFSIZE_640 0x00000004 +#define ADCBS_BUFSIZE_768 0x00000005 +#define ADCBS_BUFSIZE_896 0x00000006 +#define ADCBS_BUFSIZE_1024 0x00000007 +#define ADCBS_BUFSIZE_1280 0x00000008 +#define ADCBS_BUFSIZE_1536 0x00000009 +#define ADCBS_BUFSIZE_1792 0x0000000a +#define ADCBS_BUFSIZE_2048 0x0000000b +#define ADCBS_BUFSIZE_2560 0x0000000c +#define ADCBS_BUFSIZE_3072 0x0000000d +#define ADCBS_BUFSIZE_3584 0x0000000e +#define ADCBS_BUFSIZE_4096 0x0000000f +#define ADCBS_BUFSIZE_5120 0x00000010 +#define ADCBS_BUFSIZE_6144 0x00000011 +#define ADCBS_BUFSIZE_7168 0x00000012 +#define ADCBS_BUFSIZE_8192 0x00000013 +#define ADCBS_BUFSIZE_10240 0x00000014 +#define ADCBS_BUFSIZE_12288 0x00000015 +#define ADCBS_BUFSIZE_14366 0x00000016 +#define ADCBS_BUFSIZE_16384 0x00000017 +#define ADCBS_BUFSIZE_20480 0x00000018 +#define ADCBS_BUFSIZE_24576 0x00000019 +#define ADCBS_BUFSIZE_28672 0x0000001a +#define ADCBS_BUFSIZE_32768 0x0000001b +#define ADCBS_BUFSIZE_40960 0x0000001c +#define ADCBS_BUFSIZE_49152 0x0000001d +#define ADCBS_BUFSIZE_57344 0x0000001e +#define ADCBS_BUFSIZE_65536 0x0000001f + + +#define CDCS 0x50 /* CD-ROM digital channel status register */ + +#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ + +#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define A_DBG 0x53 +#define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */ +#define A_DBG_ZC 0x40000000 /* zero tram counter */ +#define A_DBG_STEP_ADDR 0x000003ff +#define A_DBG_SATURATION_OCCURED 0x20000000 +#define A_DBG_SATURATION_ADDR 0x0ffc0000 + +#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ + +#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ + +#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */ + +#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ +#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ +#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ +#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ +#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ +#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ +#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ +#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ +#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ +#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ +#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ +#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ +#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ +#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ +#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ +#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ +#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ +#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ +#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ +#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ +#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ +#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ +#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ + +/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ +#define CLIEL 0x58 /* Channel loop interrupt enable low register */ + +#define CLIEH 0x59 /* Channel loop interrupt enable high register */ + +#define CLIPL 0x5a /* Channel loop interrupt pending low register */ + +#define CLIPH 0x5b /* Channel loop interrupt pending high register */ + +#define SOLEL 0x5c /* Stop on loop enable low register */ + +#define SOLEH 0x5d /* Stop on loop enable high register */ + +#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ +#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ + +#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ + +#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ + +#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ + +#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ + /* NOTE: This one has no SPDIFLOCKED field */ + /* Assumes sample lock */ + +/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ +#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ +#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ +#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ + +/* Note that these values can vary +/- by a small amount */ +#define SRCS_SPDIFRATE_44 0x0003acd9 +#define SRCS_SPDIFRATE_48 0x00040000 +#define SRCS_SPDIFRATE_96 0x00080000 + +#define MICIDX 0x63 /* Microphone recording buffer index register */ +#define MICIDX_MASK 0x0000ffff /* 16-bit value */ +#define MICIDX_IDX 0x10000063 + +#define ADCIDX 0x64 /* ADC recording buffer index register */ +#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ +#define ADCIDX_IDX 0x10000064 + +#define A_ADCIDX 0x63 +#define A_ADCIDX_IDX 0x10000063 + +#define FXIDX 0x65 /* FX recording buffer index register */ +#define FXIDX_MASK 0x0000ffff /* 16-bit value */ +#define FXIDX_IDX 0x10000065 + +/* This is the MPU port on the card (via the game port) */ +#define A_MUDATA1 0x70 +#define A_MUCMD1 0x71 +#define A_MUSTAT1 A_MUCMD1 + +/* This is the MPU port on the Audigy Drive */ +#define A_MUDATA2 0x72 +#define A_MUCMD2 0x73 +#define A_MUSTAT2 A_MUCMD2 + +/* The next two are the Audigy equivalent of FXWC */ +/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */ +/* Each bit selects a channel for recording */ +#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ +#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ + +#define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ +#define A_SPDIF_48000 0x00000080 +#define A_SPDIF_44100 0x00000000 +#define A_SPDIF_96000 0x00000040 + +#define A_FXRT2 0x7c +#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */ +#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send F */ +#define A_FXRT_CHANNELG 0x003f0000 /* Effects send bus number for channel's effects send G */ +#define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send H */ + +#define A_SENDAMOUNTS 0x7d +#define A_FXSENDAMOUNT_E_MASK 0xFF000000 +#define A_FXSENDAMOUNT_F_MASK 0x00FF0000 +#define A_FXSENDAMOUNT_G_MASK 0x0000FF00 +#define A_FXSENDAMOUNT_H_MASK 0x000000FF + +/* The send amounts for this one are the same as used with the emu10k1 */ +#define A_FXRT1 0x7e +#define A_FXRT_CHANNELA 0x0000003f +#define A_FXRT_CHANNELB 0x00003f00 +#define A_FXRT_CHANNELC 0x003f0000 +#define A_FXRT_CHANNELD 0x3f000000 + + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ +#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ + +/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ +/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ +/* locations are for external TRAM. */ +#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ +#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ + +/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ +#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ + +#define MICROCODEBASE 0x400 /* Microcode data base address */ + +/* Each DSP microcode instruction is mapped into 2 doublewords */ +/* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */ +#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ +#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ +#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ +#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ +#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ + + +/* Audigy Soundcard have a different instruction format */ +#define A_MICROCODEBASE 0x600 +#define A_LOWORD_OPY_MASK 0x000007ff +#define A_LOWORD_OPX_MASK 0x007ff000 +#define A_HIWORD_OPCODE_MASK 0x0f000000 +#define A_HIWORD_RESULT_MASK 0x007ff000 +#define A_HIWORD_OPA_MASK 0x000007ff + + +/* ------------------- STRUCTURES -------------------- */ + +typedef struct _snd_emu10k1 emu10k1_t; +typedef struct _snd_emu10k1_voice emu10k1_voice_t; +typedef struct _snd_emu10k1_pcm emu10k1_pcm_t; + +typedef enum { + EMU10K1_PCM, + EMU10K1_SYNTH, + EMU10K1_MIDI +} emu10k1_voice_type_t; + +struct _snd_emu10k1_voice { + emu10k1_t *emu; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice); + + emu10k1_pcm_t *epcm; +}; + +typedef enum { + PLAYBACK_EMUVOICE, + CAPTURE_AC97ADC, + CAPTURE_AC97MIC, + CAPTURE_EFX +} snd_emu10k1_pcm_type_t; + +struct _snd_emu10k1_pcm { + emu10k1_t *emu; + snd_emu10k1_pcm_type_t type; + snd_pcm_substream_t *substream; + emu10k1_voice_t *voices[2]; + emu10k1_voice_t *extra; + unsigned short running; + unsigned short first_ptr; + snd_util_memblk_t *memblk; + unsigned int start_addr; + unsigned int ccca_start_addr; + unsigned int capture_ipr; /* interrupt acknowledge mask */ + unsigned int capture_inte; /* interrupt enable mask */ + unsigned int capture_ba_reg; /* buffer address register */ + unsigned int capture_bs_reg; /* buffer size register */ + unsigned int capture_idx_reg; /* buffer index register */ + unsigned int capture_cr_val; /* control value */ + unsigned int capture_cr_val2; /* control value2 (for audigy) */ + unsigned int capture_bs_val; /* buffer size value */ + unsigned int capture_bufsize; /* buffer size in bytes */ +}; + +typedef struct { + unsigned char send_routing[3][8]; + unsigned char send_volume[3][8]; + unsigned short attn[3]; + snd_kcontrol_t *ctl_send_routing; + snd_kcontrol_t *ctl_send_volume; + snd_kcontrol_t *ctl_attn; + emu10k1_pcm_t *epcm; +} emu10k1_pcm_mixer_t; + +#define snd_emu10k1_compose_send_routing(route) \ +((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16) + +#define snd_emu10k1_compose_audigy_fxrt1(route) \ +(((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 12)) << 24) + +#define snd_emu10k1_compose_audigy_fxrt2(route) \ +(((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 12)) << 24) + +typedef struct snd_emu10k1_memblk { + snd_util_memblk_t mem; + /* private part */ + short first_page, last_page, pages, mapped_page; + unsigned int map_locked; + struct list_head mapped_link; + struct list_head mapped_order_link; +} emu10k1_memblk_t; + +#define snd_emu10k1_memblk_offset(blk) (((blk)->mapped_page << PAGE_SHIFT) | ((blk)->mem.offset & (PAGE_SIZE - 1))) + +#define EMU10K1_MAX_TRAM_BLOCKS_PER_CODE 16 + +typedef struct { + struct list_head list; /* list link container */ + unsigned int vcount; + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GRP_TRANSLATION*) */ + snd_kcontrol_t *kcontrol; +} snd_emu10k1_fx8010_ctl_t; + +typedef void (snd_fx8010_irq_handler_t)(emu10k1_t *emu, void *private_data); + +typedef struct _snd_emu10k1_fx8010_irq { + struct _snd_emu10k1_fx8010_irq *next; + snd_fx8010_irq_handler_t *handler; + unsigned char gpr_running; + void *private_data; +} snd_emu10k1_fx8010_irq_t; + +typedef struct { + unsigned int valid: 1, + opened: 1, + active: 1; + unsigned int channels; /* 16-bit channels count */ + unsigned int tram_start; /* initial ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ring buffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char etram[32]; /* external TRAM address & data */ + unsigned int sw_data, hw_data; + unsigned int sw_io, hw_io; + unsigned int sw_ready, hw_ready; + unsigned int appl_ptr; + unsigned int tram_pos; + unsigned int tram_shift; + snd_emu10k1_fx8010_irq_t *irq; +} snd_emu10k1_fx8010_pcm_t; + +typedef struct { + unsigned short fxbus_mask; /* used FX buses (bitmask) */ + unsigned short extin_mask; /* used external inputs (bitmask) */ + unsigned short extout_mask; /* used external outputs (bitmask) */ + unsigned short pad1; + unsigned int itram_size; /* internal TRAM size in samples */ + unsigned int etram_size; /* external TRAM size in samples */ + void *etram_pages; /* allocated pages for external TRAM */ + dma_addr_t etram_pages_dmaaddr; + unsigned int dbg; /* FX debugger register */ + unsigned char name[128]; + int gpr_size; /* size of allocated GPR controls */ + int gpr_count; /* count of used kcontrols */ + struct list_head gpr_ctl; /* GPR controls */ + struct semaphore lock; + snd_emu10k1_fx8010_pcm_t pcm[8]; + spinlock_t irq_lock; + snd_emu10k1_fx8010_irq_t *irq_handlers; +} snd_emu10k1_fx8010_t; + +#define emu10k1_gpr_ctl(n) list_entry(n, snd_emu10k1_fx8010_ctl_t, list) + +typedef struct { + struct _snd_emu10k1 *emu; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream_input; + snd_rawmidi_substream_t *substream_output; + unsigned int midi_mode; + spinlock_t input_lock; + spinlock_t output_lock; + spinlock_t open_lock; + int tx_enable, rx_enable; + int port; + int ipr_tx, ipr_rx; + void (*interrupt)(emu10k1_t *emu, unsigned int status); +} emu10k1_midi_t; + +struct _snd_emu10k1 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + int APS: 1, /* APS flag */ + tos_link: 1; /* tos link detected */ + unsigned int audigy; /* is Audigy? */ + unsigned int revision; /* chip revision */ + unsigned int serial; /* serial number */ + unsigned short model; /* subsystem id */ + unsigned int card_type; /* EMU10K1_CARD_* */ + unsigned int ecard_ctrl; /* ecard control bits */ + unsigned long dma_mask; /* PCI DMA mask */ + int max_cache_pages; /* max memory size / PAGE_SIZE */ + void *silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; + volatile u32 *ptb_pages; /* page table pages */ + dma_addr_t ptb_pages_dmaaddr; + snd_util_memhdr_t *memhdr; /* page allocation list */ + emu10k1_memblk_t *reserved_page; /* reserved page */ + + struct list_head mapped_link_head; + struct list_head mapped_order_link_head; + void **page_ptr_table; + unsigned long *page_addr_table; + spinlock_t memblk_lock; + + unsigned int spdif_bits[3]; /* s/pdif out setup */ + + snd_emu10k1_fx8010_t fx8010; /* FX8010 info */ + int gpr_base; + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + snd_pcm_t *pcm_efx; + snd_pcm_t *pcm_fx8010; + + spinlock_t synth_lock; + void *synth; + int (*get_synth_voice)(emu10k1_t *emu); + + spinlock_t reg_lock; + spinlock_t emu_lock; + spinlock_t voice_lock; + struct semaphore ptb_lock; + + emu10k1_voice_t voices[64]; + emu10k1_pcm_mixer_t pcm_mixer[32]; + + void (*hwvol_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_mic_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_efx_interrupt)(emu10k1_t *emu, unsigned int status); + void (*timer_interrupt)(emu10k1_t *emu); + void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status); + void (*dsp_interrupt)(emu10k1_t *emu); + + snd_pcm_substream_t *pcm_capture_substream; + snd_pcm_substream_t *pcm_capture_mic_substream; + snd_pcm_substream_t *pcm_capture_efx_substream; + + emu10k1_midi_t midi; + emu10k1_midi_t midi2; /* for audigy */ + + unsigned int efx_voices_mask[2]; +}; + +int snd_emu10k1_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu); + +int snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_mixer(emu10k1_t * emu); +int snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep); + +void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* initialization */ +void snd_emu10k1_voice_init(emu10k1_t * emu, int voice); +int snd_emu10k1_init_efx(emu10k1_t *emu); +void snd_emu10k1_free_efx(emu10k1_t *emu); +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size); + +/* I/O functions */ +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn); +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data); +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data); +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc); +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait); +static inline unsigned int snd_emu10k1_wc(emu10k1_t *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; } +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data); +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate); +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value); + +/* memory allocation */ +snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream); +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size); +int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk); +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size); +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size); +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk); + +/* voice allocation */ +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice); +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice); + +/* MIDI uart */ +int snd_emu10k1_midi(emu10k1_t * emu); +int snd_emu10k1_audigy_midi(emu10k1_t * emu); + +/* proc interface */ +int snd_emu10k1_proc_init(emu10k1_t * emu); + +#endif /* __KERNEL__ */ + +/* + * ---- FX8010 ---- + */ + +#define EMU10K1_CARD_CREATIVE 0x00000000 +#define EMU10K1_CARD_EMUAPS 0x00000001 + +#define EMU10K1_FX8010_PCM_COUNT 8 + +/* instruction set */ +#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ +#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ +#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ +#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ +#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ +#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ +#define iACC3 0x06 /* R = A + X + Y ; saturation */ +#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ +#define iANDXOR 0x08 /* R = (A & X) ^ Y */ +#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ +#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ +#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ +#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ +#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ +#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ +#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ + +/* GPRs */ +#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ +#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ +#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ +#define C_00000000 0x40 +#define C_00000001 0x41 +#define C_00000002 0x42 +#define C_00000003 0x43 +#define C_00000004 0x44 +#define C_00000008 0x45 +#define C_00000010 0x46 +#define C_00000020 0x47 +#define C_00000100 0x48 +#define C_00010000 0x49 +#define C_00080000 0x4a +#define C_10000000 0x4b +#define C_20000000 0x4c +#define C_40000000 0x4d +#define C_80000000 0x4e +#define C_7fffffff 0x4f +#define C_ffffffff 0x50 +#define C_fffffffe 0x51 +#define C_c0000000 0x52 +#define C_4f1bbcdc 0x53 +#define C_5a7ef9db 0x54 +#define C_00100000 0x55 /* ?? */ +#define GPR_ACCU 0x56 /* ACCUM, accumulator */ +#define GPR_COND 0x57 /* CCR, condition register */ +#define GPR_NOISE0 0x58 /* noise source */ +#define GPR_NOISE1 0x59 /* noise source */ +#define GPR_IRQ 0x5a /* IRQ register */ +#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ +#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ +#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ +#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ + +#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f? */ +#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x1f? */ +#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f? */ +#define A_GPR(x) (A_FXGPREGBASE + (x)) + +/* cc_reg constants */ +#define CC_REG_NORMALIZED C_00000001 +#define CC_REG_BORROW C_00000002 +#define CC_REG_MINUS C_00000004 +#define CC_REG_ZERO C_00000008 +#define CC_REG_SATURATE C_00000010 +#define CC_REG_NONZERO C_00000100 + +/* FX buses */ +#define FXBUS_PCM_LEFT 0x00 +#define FXBUS_PCM_RIGHT 0x01 +#define FXBUS_PCM_LEFT_REAR 0x02 +#define FXBUS_PCM_RIGHT_REAR 0x03 +#define FXBUS_MIDI_LEFT 0x04 +#define FXBUS_MIDI_RIGHT 0x05 +#define FXBUS_PCM_CENTER 0x06 +#define FXBUS_PCM_LFE 0x07 +#define FXBUS_MIDI_REVERB 0x0c +#define FXBUS_MIDI_CHORUS 0x0d + +/* Inputs */ +#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ +#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ +#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ +#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ +#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ +#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ +#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ +#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ +#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ +#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ +#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ +#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ + +/* Outputs */ +#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ +#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ +#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ +#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ +#define EXTOUT_CENTER 0x04 /* SB Live 5.1 - center */ +#define EXTOUT_LFE 0x05 /* SB Live 5.1 - LFE */ +#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ +#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ +#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ +#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ +#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ +#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ +#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ +#define EXTOUT_ACENTER 0x11 /* Analog Center */ +#define EXTOUT_ALFE 0x12 /* Analog LFE */ + +/* Audigy Inputs */ +#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ +#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ +#define A_EXTIN_OPT_SPDIF_L 0x04 /* audigy drive Optical SPDIF - left */ +#define A_EXTIN_OPT_SPDIF_R 0x05 /* right */ +#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ +#define A_EXTIN_LINE2_R 0x09 /* right */ +#define A_EXTIN_RCA_SPDIF_L 0x0a /* audigy drive RCA SPDIF - left */ +#define A_EXTIN_RCA_SPDIF_R 0x0b /* right */ +#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ +#define A_EXTIN_AUX2_R 0x0d /* - right */ + +/* Audigiy Outputs */ +#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ +#define A_EXTOUT_FRONT_R 0x01 /* right */ +#define A_EXTOUT_CENTER 0x02 /* digital front center */ +#define A_EXTOUT_LFE 0x03 /* digital front lfe */ +#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ +#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ +#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ +#define A_EXTOUT_REAR_R 0x07 /* right */ +#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ +#define A_EXTOUT_AFRONT_R 0x09 /* right */ +#define A_EXTOUT_ACENTER 0x0a /* analog center */ +#define A_EXTOUT_ALFE 0x0b /* analog LFE */ +/* 0x0c ?? */ +/* 0x0d ?? */ +#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ +#define A_EXTOUT_AREAR_R 0x0f /* right */ +#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ +#define A_EXTOUT_AC97_R 0x11 /* right */ +#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ +#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ + +/* Audigy constants */ +#define A_C_00000000 0xc0 +#define A_C_00000001 0xc1 +#define A_C_00000002 0xc2 +#define A_C_00000003 0xc3 +#define A_C_00000004 0xc4 +#define A_C_00000008 0xc5 +#define A_C_00000010 0xc6 +#define A_C_00000020 0xc7 +#define A_C_00000100 0xc8 +#define A_C_00010000 0xc9 +#define A_C_00000800 0xca +#define A_C_10000000 0xcb +#define A_C_20000000 0xcc +#define A_C_40000000 0xcd +#define A_C_80000000 0xce +#define A_C_7fffffff 0xcf +#define A_C_ffffffff 0xd0 +#define A_C_fffffffe 0xd1 +#define A_C_c0000000 0xd2 +#define A_C_4f1bbcdc 0xd3 +#define A_C_5a7ef9db 0xd4 +#define A_C_00100000 0xd5 +/* 0xd6 = 0x7fffffff (?) ACCUM? */ +/* 0xd7 = 0x0000000 CCR */ +/* 0xd8 = noise1 */ +/* 0xd9 = noise2 */ + +/* definitions for debug register */ +#define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ +#define EMU10K1_DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define EMU10K1_DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define EMU10K1_DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define EMU10K1_DBG_STEP 0x00004000 /* start single step */ +#define EMU10K1_DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define EMU10K1_DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ + +/* tank memory address line */ +#ifndef __KERNEL__ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ +#endif + +typedef struct { + unsigned int card; /* card type */ + unsigned int internal_tram_size; /* in samples */ + unsigned int external_tram_size; /* in samples */ + char fxbus_names[16][32]; /* names of FXBUSes */ + char extin_names[16][32]; /* names of external inputs */ + char extout_names[32][32]; /* names of external outputs */ + unsigned int gpr_controls; /* count of GPR controls */ +} emu10k1_fx8010_info_t; + +#define EMU10K1_GPR_TRANSLATION_NONE 0 +#define EMU10K1_GPR_TRANSLATION_TABLE100 1 +#define EMU10K1_GRP_TRANSLATION_BASS 2 +#define EMU10K1_GRP_TRANSLATION_TREBLE 3 +#define EMU10K1_GPR_TRANSLATION_ONOFF 4 + +typedef struct { + snd_ctl_elem_id_t id; /* full control ID definition */ + unsigned int vcount; /* visible count */ + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; /* initial values */ + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GRP_TRANSLATION*) */ +} emu10k1_fx8010_control_gpr_t; + +typedef struct { + char name[128]; + + unsigned long gpr_valid[0x100/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */ + unsigned int gpr_map[0x100]; /* initializers */ + + unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */ + emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */ + + unsigned int gpr_del_control_count; /* count of GPR controls to remove */ + snd_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */ + + unsigned int gpr_list_control_count; /* count of GPR controls to list */ + unsigned int gpr_list_control_total; /* total count of GPR controls */ + emu10k1_fx8010_control_gpr_t *gpr_list_controls; /* listed GPR controls */ + + unsigned long tram_valid[0xa0/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */ + unsigned int tram_data_map[0xa0]; /* data initializers */ + unsigned int tram_addr_map[0xa0]; /* map initializers */ + + unsigned long code_valid[512/(sizeof(unsigned long)*8)]; /* bitmask of valid instructions */ + unsigned int code[512][2]; /* one instruction - 64 bits */ +} emu10k1_fx8010_code_t; + +typedef struct { + unsigned int address; /* 31.bit == 1 -> external TRAM */ + unsigned int size; /* size in samples (4 bytes) */ + unsigned int *samples; /* pointer to samples (20-bit) */ + /* NULL->clear memory */ +} emu10k1_fx8010_tram_t; + +typedef struct { + unsigned int substream; /* substream number */ + unsigned int res1; /* reserved */ + unsigned int channels; /* 16-bit channels count, zero = remove this substream */ + unsigned int tram_start; /* ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ringbuffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char pad; /* reserved */ + unsigned char etram[32]; /* external TRAM address & data (one per channel) */ + unsigned int res2; /* reserved */ +} emu10k1_fx8010_pcm_t; + +#define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, emu10k1_fx8010_info_t) +#define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOW ('H', 0x12, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP _IOW ('H', 0x20, int) +#define SNDRV_EMU10K1_IOCTL_TRAM_POKE _IOW ('H', 0x21, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOR ('H', 0x22, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80) +#define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81) +#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82) +#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP _IOW ('H', 0x83, int) +#define SNDRV_EMU10K1_IOCTL_DBG_READ _IOR ('H', 0x84, int) + +#endif /* __SOUND_EMU10K1_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/emu10k1_synth.h linux/include/sound/emu10k1_synth.h --- linux-2.4.21-rc1.orig/include/sound/emu10k1_synth.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/emu10k1_synth.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,39 @@ +#ifndef __EMU10K1_SYNTH_H +#define __EMU10K1_SYNTH_H +/* + * Defines for the Emu10k1 WaveTable synth + * + * Copyright (C) 2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1.h" +#include "emux_synth.h" + +/* sequencer device id */ +#define SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH "emu10k1-synth" + +/* argument for snd_seq_device_new */ +typedef struct snd_emu10k1_synth_arg { + emu10k1_t *hwptr; /* chip */ + int index; /* sequencer client index */ + int seq_ports; /* number of sequencer ports to be created */ + int max_voices; /* maximum number of voices for wavetable */ +} snd_emu10k1_synth_arg_t; + +#define EMU10K1_MAX_MEMSIZE (32 * 1024 * 1024) /* 32MB */ + +#endif diff -urN linux-2.4.21-rc1.orig/include/sound/emu8000.h linux/include/sound/emu8000.h --- linux-2.4.21-rc1.orig/include/sound/emu8000.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/emu8000.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,120 @@ +#ifndef __SOUND_EMU8000_H +#define __SOUND_EMU8000_H +/* + * Defines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emux_synth.h" +#include "seq_kernel.h" + +/* + * Hardware parameters. + */ +#define EMU8000_MAX_DRAM (28 * 1024 * 1024) /* Max on-board mem is 28Mb ???*/ +#define EMU8000_DRAM_OFFSET 0x200000 /* Beginning of on board ram */ +#define EMU8000_CHANNELS 32 /* Number of hardware channels */ +#define EMU8000_DRAM_VOICES 30 /* number of normal voices */ + +/* Flags to set a dma channel to read or write */ +#define EMU8000_RAM_READ 0 +#define EMU8000_RAM_WRITE 1 +#define EMU8000_RAM_CLOSE 2 +#define EMU8000_RAM_MODE_MASK 0x03 +#define EMU8000_RAM_RIGHT 0x10 /* use 'right' DMA channel */ + +enum { + EMU8000_CONTROL_BASS = 0, + EMU8000_CONTROL_TREBLE, + EMU8000_CONTROL_CHORUS_MODE, + EMU8000_CONTROL_REVERB_MODE, + EMU8000_CONTROL_FM_CHORUS_DEPTH, + EMU8000_CONTROL_FM_REVERB_DEPTH, + EMU8000_NUM_CONTROLS, +}; + +/* + * Structure to hold all state information for the emu8000 driver. + * + * Note 1: The chip supports 32 channels in hardware this is max_channels + * some of the channels may be used for other things so max_channels is + * the number in use for wave voices. + */ +typedef struct snd_emu8000 { + + snd_emux_t *emu; + + int index; /* sequencer client index */ + int seq_ports; /* number of sequencer ports */ + int fm_chorus_depth; /* FM OPL3 chorus depth */ + int fm_reverb_depth; /* FM OPL3 reverb depth */ + + int mem_size; /* memory size */ + unsigned long port1; /* Port usually base+0 */ + unsigned long port2; /* Port usually at base+0x400 */ + unsigned long port3; /* Port usually at base+0x800 */ + struct resource *res_port1; + struct resource *res_port2; + struct resource *res_port3; + unsigned short last_reg;/* Last register command */ + spinlock_t reg_lock; + + int dram_checked; + + snd_card_t *card; /* The card that this belongs to */ + + int chorus_mode; + int reverb_mode; + int bass_level; + int treble_level; + + snd_util_memhdr_t *memhdr; + + spinlock_t control_lock; + snd_kcontrol_t *controls[EMU8000_NUM_CONTROLS]; + + snd_pcm_t *pcm; /* pcm on emu8000 wavetable */ + +} emu8000_t; + +/* sequencer device id */ +#define SNDRV_SEQ_DEV_ID_EMU8000 "emu8000-synth" + + +/* exported functions */ +int snd_emu8000_new(snd_card_t *card, int device, long port, int seq_ports, snd_seq_device_t **ret); +void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, + unsigned int val); +unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, + unsigned int reg); +void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, + unsigned int val); +unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, + unsigned int reg); +void snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode); + +void snd_emu8000_init_fm(emu8000_t *emu); + +void snd_emu8000_update_chorus_mode(emu8000_t *emu); +void snd_emu8000_update_reverb_mode(emu8000_t *emu); +void snd_emu8000_update_equalizer(emu8000_t *emu); +int snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len); +int snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len); + +#endif /* __SOUND_EMU8000_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/emu8000_reg.h linux/include/sound/emu8000_reg.h --- linux-2.4.21-rc1.orig/include/sound/emu8000_reg.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/emu8000_reg.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,207 @@ +#ifndef __SOUND_EMU8000_REG_H +#define __SOUND_EMU8000_REG_H +/* + * Register operations for the EMU8000 + * + * Copyright (C) 1999 Steve Ratcliffe + * + * Based on awe_wave.c by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Data port addresses relative to the EMU base. + */ +#define EMU8000_DATA0(e) ((e)->port1) +#define EMU8000_DATA1(e) ((e)->port2) +#define EMU8000_DATA2(e) ((e)->port2+2) +#define EMU8000_DATA3(e) ((e)->port3) +#define EMU8000_PTR(e) ((e)->port3+2) + +/* + * Make a command from a register and channel. + */ +#define EMU8000_CMD(reg, chan) ((reg)<<5 | (chan)) + +/* + * Commands to read and write the EMU8000 registers. + * These macros should be used for all register accesses. + */ +#define EMU8000_CPF_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_PTRX_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan))) +#define EMU8000_CVCF_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_VTFT_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_PSST_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_CSL_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_CCCA_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_HWCF4_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9)) +#define EMU8000_HWCF5_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10)) +#define EMU8000_HWCF6_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13)) +#define EMU8000_SMALR_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20)) +#define EMU8000_SMARR_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21)) +#define EMU8000_SMALW_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22)) +#define EMU8000_SMARW_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23)) +#define EMU8000_SMLD_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26)) +#define EMU8000_SMRD_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26)) +#define EMU8000_WC_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27)) +#define EMU8000_HWCF1_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29)) +#define EMU8000_HWCF2_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30)) +#define EMU8000_HWCF3_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31)) +#define EMU8000_INIT1_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_INIT2_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_INIT3_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_INIT4_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_ENVVOL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_DCYSUSV_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan))) +#define EMU8000_ENVVAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_DCYSUS_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_ATKHLDV_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_LFO1VAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan))) +#define EMU8000_ATKHLD_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_LFO2VAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_IP_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_IFATN_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan))) +#define EMU8000_PEFE_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_FMMOD_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_TREMFRQ_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_FM2FRQ2_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan))) + + +#define EMU8000_CPF_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_PTRX_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)), (val)) +#define EMU8000_CVCF_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_VTFT_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_PSST_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_CSL_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_CCCA_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_HWCF4_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9), (val)) +#define EMU8000_HWCF5_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10), (val)) +#define EMU8000_HWCF6_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13), (val)) +/* this register is not documented */ +#define EMU8000_HWCF7_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 14), (val)) +#define EMU8000_SMALR_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20), (val)) +#define EMU8000_SMARR_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21), (val)) +#define EMU8000_SMALW_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22), (val)) +#define EMU8000_SMARW_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23), (val)) +#define EMU8000_SMLD_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26), (val)) +#define EMU8000_SMRD_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26), (val)) +#define EMU8000_WC_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27), (val)) +#define EMU8000_HWCF1_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29), (val)) +#define EMU8000_HWCF2_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30), (val)) +#define EMU8000_HWCF3_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31), (val)) +#define EMU8000_INIT1_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_INIT2_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_INIT3_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_INIT4_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_ENVVOL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_DCYSUSV_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)), (val)) +#define EMU8000_ENVVAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_DCYSUS_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_ATKHLDV_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_LFO1VAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)), (val)) +#define EMU8000_ATKHLD_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_LFO2VAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_IP_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_IFATN_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)), (val)) +#define EMU8000_PEFE_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_FMMOD_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_TREMFRQ_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_FM2FRQ2_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)), (val)) + +#define EMU8000_0080_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_00A0_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(5, (chan)), (val)) + +#endif /* __SOUND_EMU8000_REG_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/emux_legacy.h linux/include/sound/emux_legacy.h --- linux-2.4.21-rc1.orig/include/sound/emux_legacy.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/emux_legacy.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,146 @@ +#ifndef __SOUND_EMUX_LEGACY_H +#define __SOUND_EMUX_LEGACY_H + +/* + * Copyright (c) 1999-2000 Takashi Iwai + * + * Definitions of OSS compatible headers for Emu8000 device informations + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_oss_legacy.h" + +/* + * awe hardware controls + */ + +#define _EMUX_OSS_DEBUG_MODE 0x00 +#define _EMUX_OSS_REVERB_MODE 0x01 +#define _EMUX_OSS_CHORUS_MODE 0x02 +#define _EMUX_OSS_REMOVE_LAST_SAMPLES 0x03 +#define _EMUX_OSS_INITIALIZE_CHIP 0x04 +#define _EMUX_OSS_SEND_EFFECT 0x05 +#define _EMUX_OSS_TERMINATE_CHANNEL 0x06 +#define _EMUX_OSS_TERMINATE_ALL 0x07 +#define _EMUX_OSS_INITIAL_VOLUME 0x08 +#define _EMUX_OSS_INITIAL_ATTEN _EMUX_OSS_INITIAL_VOLUME +#define _EMUX_OSS_RESET_CHANNEL 0x09 +#define _EMUX_OSS_CHANNEL_MODE 0x0a +#define _EMUX_OSS_DRUM_CHANNELS 0x0b +#define _EMUX_OSS_MISC_MODE 0x0c +#define _EMUX_OSS_RELEASE_ALL 0x0d +#define _EMUX_OSS_NOTEOFF_ALL 0x0e +#define _EMUX_OSS_CHN_PRESSURE 0x0f +#define _EMUX_OSS_EQUALIZER 0x11 + +#define _EMUX_OSS_MODE_FLAG 0x80 +#define _EMUX_OSS_COOKED_FLAG 0x40 /* not supported */ +#define _EMUX_OSS_MODE_VALUE_MASK 0x3F + + +/* + * mode type definitions + */ +enum { +/* 0*/ EMUX_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ EMUX_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ EMUX_MD_VERSION, /* read only */ +/* 3*/ EMUX_MD_EXCLUSIVE_SOUND, /* 0/1: exclusive note on (default=1) */ +/* 4*/ EMUX_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ EMUX_MD_GUS_BANK, /* bank number for GUS patches (default=0) */ +/* 6*/ EMUX_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ EMUX_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ EMUX_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ EMUX_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */ +/*10*/ EMUX_MD_DEF_PRESET, /* integer: default preset number (def=0) */ +/*11*/ EMUX_MD_DEF_BANK, /* integer: default bank number (def=0) */ +/*12*/ EMUX_MD_DEF_DRUM, /* integer: default drumset number (def=0) */ +/*13*/ EMUX_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */ +/*14*/ EMUX_MD_NEW_VOLUME_CALC, /* 0/1: volume calculation mode (def=1) */ +/*15*/ EMUX_MD_CHORUS_MODE, /* integer: chorus mode (def=2) */ +/*16*/ EMUX_MD_REVERB_MODE, /* integer: chorus mode (def=4) */ +/*17*/ EMUX_MD_BASS_LEVEL, /* integer: bass level (def=5) */ +/*18*/ EMUX_MD_TREBLE_LEVEL, /* integer: treble level (def=9) */ +/*19*/ EMUX_MD_DEBUG_MODE, /* integer: debug level (def=0) */ +/*20*/ EMUX_MD_PAN_EXCHANGE, /* 0/1: exchange panning direction (def=0) */ + EMUX_MD_END, +}; + + +/* + * effect parameters + */ +enum { + +/* modulation envelope parameters */ +/* 0*/ EMUX_FX_ENV1_DELAY, /* WORD: ENVVAL */ +/* 1*/ EMUX_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */ +/* 2*/ EMUX_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */ +/* 3*/ EMUX_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */ +/* 4*/ EMUX_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */ +/* 5*/ EMUX_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */ +/* 6*/ EMUX_FX_ENV1_PITCH, /* BYTE: up PEFE */ +/* 7*/ EMUX_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */ + +/* volume envelope parameters */ +/* 8*/ EMUX_FX_ENV2_DELAY, /* WORD: ENVVOL */ +/* 9*/ EMUX_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */ +/*10*/ EMUX_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */ +/*11*/ EMUX_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */ +/*12*/ EMUX_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */ +/*13*/ EMUX_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */ + +/* LFO1 (tremolo & vibrato) parameters */ +/*14*/ EMUX_FX_LFO1_DELAY, /* WORD: LFO1VAL */ +/*15*/ EMUX_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */ +/*16*/ EMUX_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */ +/*17*/ EMUX_FX_LFO1_PITCH, /* BYTE: up FMMOD */ +/*18*/ EMUX_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */ + +/* LFO2 (vibrato) parameters */ +/*19*/ EMUX_FX_LFO2_DELAY, /* WORD: LFO2VAL */ +/*20*/ EMUX_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */ +/*21*/ EMUX_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */ + +/* Other overall effect parameters */ +/*22*/ EMUX_FX_INIT_PITCH, /* SHORT: pitch offset */ +/*23*/ EMUX_FX_CHORUS, /* BYTE: chorus effects send (0-255) */ +/*24*/ EMUX_FX_REVERB, /* BYTE: reverb effects send (0-255) */ +/*25*/ EMUX_FX_CUTOFF, /* BYTE: up IFATN */ +/*26*/ EMUX_FX_FILTERQ, /* BYTE: up CCCA */ + +/* Sample / loop offset changes */ +/*27*/ EMUX_FX_SAMPLE_START, /* SHORT: offset */ +/*28*/ EMUX_FX_LOOP_START, /* SHORT: offset */ +/*29*/ EMUX_FX_LOOP_END, /* SHORT: offset */ +/*30*/ EMUX_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ +/*31*/ EMUX_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ +/*32*/ EMUX_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ EMUX_FX_ATTEN, /* BYTE: lo IFATN */ + + EMUX_FX_END, +}; +/* number of effects */ +#define EMUX_NUM_EFFECTS EMUX_FX_END + +/* effect flag values */ +#define EMUX_FX_FLAG_OFF 0 +#define EMUX_FX_FLAG_SET 1 +#define EMUX_FX_FLAG_ADD 2 + + +#endif /* __SOUND_EMUX_LEGACY_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/emux_synth.h linux/include/sound/emux_synth.h --- linux-2.4.21-rc1.orig/include/sound/emux_synth.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/emux_synth.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,240 @@ +#ifndef __SOUND_EMUX_SYNTH_H +#define __SOUND_EMUX_SYNTH_H + +/* + * Defines for the Emu-series WaveTable chip + * + * Copyright (C) 2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_kernel.h" +#include "seq_device.h" +#include "soundfont.h" +#include "seq_midi_emul.h" +#ifdef CONFIG_SND_SEQUENCER_OSS +#include "seq_oss.h" +#endif +#include "emux_legacy.h" +#include "seq_virmidi.h" + +/* + * compile flags + */ +#define SNDRV_EMUX_USE_RAW_EFFECT + + +/* + * typedefs + */ +typedef struct snd_emux_effect_table snd_emux_effect_table_t; +typedef struct snd_emux_port snd_emux_port_t; +typedef struct snd_emux_voice snd_emux_voice_t; +typedef struct snd_emux snd_emux_t; + + +/* + * operators + */ +typedef struct snd_emux_operators { + struct module *owner; + snd_emux_voice_t *(*get_voice)(snd_emux_t *emu, snd_emux_port_t *port); + int (*prepare)(snd_emux_voice_t *vp); + void (*trigger)(snd_emux_voice_t *vp); + void (*release)(snd_emux_voice_t *vp); + void (*update)(snd_emux_voice_t *vp, int update); + void (*terminate)(snd_emux_voice_t *vp); + void (*free_voice)(snd_emux_voice_t *vp); + void (*reset)(snd_emux_t *emu, int ch); + /* the first parameters are snd_emux_t */ + int (*sample_new)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count); + int (*sample_free)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); + void (*sample_reset)(snd_emux_t *emu); + int (*load_fx)(snd_emux_t *emu, int type, int arg, const void *data, long count); + void (*sysex)(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +#ifdef CONFIG_SND_SEQUENCER_OSS + int (*oss_ioctl)(snd_emux_t *emu, int cmd, int p1, int p2); +#endif +} snd_emux_operators_t; + + +/* + * constant values + */ +#define SNDRV_EMUX_MAX_PORTS 32 /* max # of sequencer ports */ +#define SNDRV_EMUX_MAX_VOICES 64 /* max # of voices */ +#define SNDRV_EMUX_MAX_MULTI_VOICES 16 /* max # of playable voices + * simultineously + */ + +/* + * flags + */ +#define SNDRV_EMUX_ACCEPT_ROM (1<<0) + +/* + * emuX wavetable + */ +struct snd_emux { + + snd_card_t *card; /* assigned card */ + + /* following should be initialized before registration */ + int max_voices; /* Number of voices */ + int mem_size; /* memory size (in byte) */ + int num_ports; /* number of ports to be created */ + int pitch_shift; /* pitch shift value (for Emu10k1) */ + snd_emux_operators_t ops; /* operators */ + void *hw; /* hardware */ + unsigned long flags; /* other conditions */ + int midi_ports; /* number of virtual midi devices */ + int midi_devidx; /* device offset of virtual midi */ + unsigned int linear_panning: 1; /* panning is linear (sbawe = 1, emu10k1 = 0) */ + + /* private */ + int num_voices; /* current number of voices */ + snd_sf_list_t *sflist; /* root of SoundFont list */ + snd_emux_voice_t *voices; /* Voices (EMU 'channel') */ + int use_time; /* allocation counter */ + spinlock_t voice_lock; /* Lock for voice access */ + struct semaphore register_mutex; + int client; /* For the sequencer client */ + int ports[SNDRV_EMUX_MAX_PORTS]; /* The ports for this device */ + int used; /* use counter */ + char *name; /* name of the device (internal) */ + snd_rawmidi_t **vmidi; + struct timer_list tlist; /* for pending note-offs */ + int timer_active; + + snd_util_memhdr_t *memhdr; /* memory chunk information */ + +#ifdef CONFIG_PROC_FS + snd_info_entry_t *proc; +#endif + +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_seq_device_t *oss_synth; +#endif +}; + + +/* + * sequencer port information + */ +struct snd_emux_port { + + snd_midi_channel_set_t chset; + snd_emux_t *emu; + + char port_mode; /* operation mode */ + int volume_atten; /* emuX raw attenuation */ + unsigned long drum_flags; /* drum bitmaps */ + int ctrls[EMUX_MD_END]; /* control parameters */ +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_effect_table_t *effect; +#endif +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_seq_oss_arg_t *oss_arg; +#endif +}; + +/* port_mode */ +#define SNDRV_EMUX_PORT_MODE_MIDI 0 /* normal MIDI port */ +#define SNDRV_EMUX_PORT_MODE_OSS_SYNTH 1 /* OSS synth port */ +#define SNDRV_EMUX_PORT_MODE_OSS_MIDI 2 /* OSS multi channel synth port */ + +/* + * A structure to keep track of each hardware voice + */ +struct snd_emux_voice { + int ch; /* Hardware channel number */ + + int state; /* status */ +#define SNDRV_EMUX_ST_OFF 0x00 /* Not playing, and inactive */ +#define SNDRV_EMUX_ST_ON 0x01 /* Note on */ +#define SNDRV_EMUX_ST_RELEASED (0x02|SNDRV_EMUX_ST_ON) /* Note released */ +#define SNDRV_EMUX_ST_SUSTAINED (0x04|SNDRV_EMUX_ST_ON) /* Note sustained */ +#define SNDRV_EMUX_ST_STANDBY (0x08|SNDRV_EMUX_ST_ON) /* Waiting to be triggered */ +#define SNDRV_EMUX_ST_PENDING (0x10|SNDRV_EMUX_ST_ON) /* Note will be released */ +#define SNDRV_EMUX_ST_LOCKED 0x100 /* Not accessible */ + + unsigned int time; /* An allocation time */ + unsigned char note; /* Note currently assigned to this voice */ + unsigned char key; + unsigned char velocity; /* Velocity of current note */ + + snd_sf_zone_t *zone; /* Zone assigned to this note */ + void *block; /* sample block pointer (optional) */ + snd_midi_channel_t *chan; /* Midi channel for this note */ + snd_emux_port_t *port; /* associated port */ + snd_emux_t *emu; /* assigned root info */ + void *hw; /* hardware pointer (emu8000_t or emu10k1_t) */ + unsigned long ontime; /* jiffies at note triggered */ + + /* Emu8k/Emu10k1 registers */ + soundfont_voice_info_t reg; + + /* additional registers */ + int avol; /* volume attenuation */ + int acutoff; /* cutoff target */ + int apitch; /* pitch offset */ + int apan; /* pan/aux pair */ + int aaux; + int ptarget; /* pitch target */ + int vtarget; /* volume target */ + int ftarget; /* filter target */ + +}; + +/* + * update flags (can be combined) + */ +#define SNDRV_EMUX_UPDATE_VOLUME (1<<0) +#define SNDRV_EMUX_UPDATE_PITCH (1<<1) +#define SNDRV_EMUX_UPDATE_PAN (1<<2) +#define SNDRV_EMUX_UPDATE_FMMOD (1<<3) +#define SNDRV_EMUX_UPDATE_TREMFREQ (1<<4) +#define SNDRV_EMUX_UPDATE_FM2FRQ2 (1<<5) +#define SNDRV_EMUX_UPDATE_Q (1<<6) + + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +/* + * effect table + */ +struct snd_emux_effect_table { + /* Emu8000 specific effects */ + short val[EMUX_NUM_EFFECTS]; + unsigned char flag[EMUX_NUM_EFFECTS]; +}; +#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ + + +/* + * prototypes - interface to Emu10k1 and Emu8k routines + */ +int snd_emux_new(snd_emux_t **remu); +int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name); +int snd_emux_free(snd_emux_t *emu); + +/* + * exported functions + */ +void snd_emux_terminate_all(snd_emux_t *emu); +void snd_emux_lock_voice(snd_emux_t *emu, int voice); +void snd_emux_unlock_voice(snd_emux_t *emu, int voice); + +#endif /* __SOUND_EMUX_SYNTH_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/es1688.h linux/include/sound/es1688.h --- linux-2.4.21-rc1.orig/include/sound/es1688.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/es1688.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,125 @@ +#ifndef __SOUND_ES1688_H +#define __SOUND_ES1688_H + +/* + * Header file for ES488/ES1688 + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" + +#define ES1688_HW_AUTO 0x0000 +#define ES1688_HW_688 0x0001 +#define ES1688_HW_1688 0x0002 + +struct _snd_es1688 { + unsigned long port; /* port of ESS chip */ + struct resource *res_port; + unsigned long mpu_port; /* MPU-401 port of ESS chip */ + int irq; /* IRQ number of ESS chip */ + int mpu_irq; /* MPU IRQ */ + int dma8; /* 8-bit DMA */ + unsigned short version; /* version of ESS chip */ + unsigned short hardware; /* see to ES1688_HW_XXXX */ + + unsigned short trigger_value; + unsigned char pad; + unsigned int dma_size; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + spinlock_t reg_lock; + spinlock_t mixer_lock; +}; + +typedef struct _snd_es1688 es1688_t; + +/* I/O ports */ + +#define ES1688P(codec, x) ((codec)->port + e_s_s_ESS1688##x) + +#define e_s_s_ESS1688RESET 0x6 +#define e_s_s_ESS1688READ 0xa +#define e_s_s_ESS1688WRITE 0xc +#define e_s_s_ESS1688COMMAND 0xc +#define e_s_s_ESS1688STATUS 0xc +#define e_s_s_ESS1688DATA_AVAIL 0xe +#define e_s_s_ESS1688DATA_AVAIL_16 0xf +#define e_s_s_ESS1688MIXER_ADDR 0x4 +#define e_s_s_ESS1688MIXER_DATA 0x5 +#define e_s_s_ESS1688OPL3_LEFT 0x0 +#define e_s_s_ESS1688OPL3_RIGHT 0x2 +#define e_s_s_ESS1688OPL3_BOTH 0x8 +#define e_s_s_ESS1688ENABLE0 0x0 +#define e_s_s_ESS1688ENABLE1 0x9 +#define e_s_s_ESS1688ENABLE2 0xb +#define e_s_s_ESS1688INIT1 0x7 + +#define ES1688_DSP_CMD_DMAOFF 0xd0 +#define ES1688_DSP_CMD_SPKON 0xd1 +#define ES1688_DSP_CMD_SPKOFF 0xd3 +#define ES1688_DSP_CMD_DMAON 0xd4 + +#define ES1688_PCM_DEV 0x14 +#define ES1688_MIC_DEV 0x1a +#define ES1688_REC_DEV 0x1c +#define ES1688_MASTER_DEV 0x32 +#define ES1688_FM_DEV 0x36 +#define ES1688_CD_DEV 0x38 +#define ES1688_AUX_DEV 0x3a +#define ES1688_SPEAKER_DEV 0x3c +#define ES1688_LINE_DEV 0x3e +#define ES1688_RECLEV_DEV 0xb4 + +#define ES1688_MIXS_MASK 0x17 +#define ES1688_MIXS_MIC 0x00 +#define ES1688_MIXS_MIC_MASTER 0x01 +#define ES1688_MIXS_CD 0x02 +#define ES1688_MIXS_AOUT 0x03 +#define ES1688_MIXS_MIC1 0x04 +#define ES1688_MIXS_REC_MIX 0x05 +#define ES1688_MIXS_LINE 0x06 +#define ES1688_MIXS_MASTER 0x07 +#define ES1688_MIXS_MUTE 0x10 + +/* + + */ + +void snd_es1688_mixer_write(es1688_t *chip, unsigned char reg, unsigned char data); +unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg); + +void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +int snd_es1688_create(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + int irq, + int mpu_irq, + int dma8, + unsigned short hardware, + es1688_t ** rchip); +int snd_es1688_pcm(es1688_t *chip, int device, snd_pcm_t ** rpcm); +int snd_es1688_mixer(es1688_t *chip); + +#endif /* __SOUND_ES1688_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/gus.h linux/include/sound/gus.h --- linux-2.4.21-rc1.orig/include/sound/gus.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/gus.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,722 @@ +#ifndef __SOUND_GUS_H +#define __SOUND_GUS_H + +/* + * Global structures used for GUS part of ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "timer.h" +#include "seq_midi_emul.h" +#include "seq_device.h" +#include "ainstr_iw.h" +#include "ainstr_gf1.h" +#include "ainstr_simple.h" +#include + +#define SNDRV_SEQ_DEV_ID_GUS "gus-synth" + +/* IO ports */ + +#define GUSP(gus, x) ((gus)->gf1.port + SNDRV_g_u_s_##x) + +#define SNDRV_g_u_s_MIDICTRL (0x320-0x220) +#define SNDRV_g_u_s_MIDISTAT (0x320-0x220) +#define SNDRV_g_u_s_MIDIDATA (0x321-0x220) + +#define SNDRV_g_u_s_GF1PAGE (0x322-0x220) +#define SNDRV_g_u_s_GF1REGSEL (0x323-0x220) +#define SNDRV_g_u_s_GF1DATALOW (0x324-0x220) +#define SNDRV_g_u_s_GF1DATAHIGH (0x325-0x220) +#define SNDRV_g_u_s_IRQSTAT (0x226-0x220) +#define SNDRV_g_u_s_TIMERCNTRL (0x228-0x220) +#define SNDRV_g_u_s_TIMERDATA (0x229-0x220) +#define SNDRV_g_u_s_DRAM (0x327-0x220) +#define SNDRV_g_u_s_MIXCNTRLREG (0x220-0x220) +#define SNDRV_g_u_s_IRQDMACNTRLREG (0x22b-0x220) +#define SNDRV_g_u_s_REGCNTRLS (0x22f-0x220) +#define SNDRV_g_u_s_BOARDVERSION (0x726-0x220) +#define SNDRV_g_u_s_MIXCNTRLPORT (0x726-0x220) +#define SNDRV_g_u_s_IVER (0x325-0x220) +#define SNDRV_g_u_s_MIXDATAPORT (0x326-0x220) +#define SNDRV_g_u_s_MAXCNTRLPORT (0x326-0x220) + +/* GF1 registers */ + +/* global registers */ +#define SNDRV_GF1_GB_ACTIVE_VOICES 0x0e +#define SNDRV_GF1_GB_VOICES_IRQ 0x0f +#define SNDRV_GF1_GB_GLOBAL_MODE 0x19 +#define SNDRV_GF1_GW_LFO_BASE 0x1a +#define SNDRV_GF1_GB_VOICES_IRQ_READ 0x1f +#define SNDRV_GF1_GB_DRAM_DMA_CONTROL 0x41 +#define SNDRV_GF1_GW_DRAM_DMA_LOW 0x42 +#define SNDRV_GF1_GW_DRAM_IO_LOW 0x43 +#define SNDRV_GF1_GB_DRAM_IO_HIGH 0x44 +#define SNDRV_GF1_GB_SOUND_BLASTER_CONTROL 0x45 +#define SNDRV_GF1_GB_ADLIB_TIMER_1 0x46 +#define SNDRV_GF1_GB_ADLIB_TIMER_2 0x47 +#define SNDRV_GF1_GB_RECORD_RATE 0x48 +#define SNDRV_GF1_GB_REC_DMA_CONTROL 0x49 +#define SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL 0x4b +#define SNDRV_GF1_GB_RESET 0x4c +#define SNDRV_GF1_GB_DRAM_DMA_HIGH 0x50 +#define SNDRV_GF1_GW_DRAM_IO16 0x51 +#define SNDRV_GF1_GW_MEMORY_CONFIG 0x52 +#define SNDRV_GF1_GB_MEMORY_CONTROL 0x53 +#define SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR 0x54 +#define SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR 0x55 +#define SNDRV_GF1_GW_FIFO_SIZE 0x56 +#define SNDRV_GF1_GW_INTERLEAVE 0x57 +#define SNDRV_GF1_GB_COMPATIBILITY 0x59 +#define SNDRV_GF1_GB_DECODE_CONTROL 0x5a +#define SNDRV_GF1_GB_VERSION_NUMBER 0x5b +#define SNDRV_GF1_GB_MPU401_CONTROL_A 0x5c +#define SNDRV_GF1_GB_MPU401_CONTROL_B 0x5d +#define SNDRV_GF1_GB_EMULATION_IRQ 0x60 +/* voice specific registers */ +#define SNDRV_GF1_VB_ADDRESS_CONTROL 0x00 +#define SNDRV_GF1_VW_FREQUENCY 0x01 +#define SNDRV_GF1_VW_START_HIGH 0x02 +#define SNDRV_GF1_VW_START_LOW 0x03 +#define SNDRV_GF1_VA_START SNDRV_GF1_VW_START_HIGH +#define SNDRV_GF1_VW_END_HIGH 0x04 +#define SNDRV_GF1_VW_END_LOW 0x05 +#define SNDRV_GF1_VA_END SNDRV_GF1_VW_END_HIGH +#define SNDRV_GF1_VB_VOLUME_RATE 0x06 +#define SNDRV_GF1_VB_VOLUME_START 0x07 +#define SNDRV_GF1_VB_VOLUME_END 0x08 +#define SNDRV_GF1_VW_VOLUME 0x09 +#define SNDRV_GF1_VW_CURRENT_HIGH 0x0a +#define SNDRV_GF1_VW_CURRENT_LOW 0x0b +#define SNDRV_GF1_VA_CURRENT SNDRV_GF1_VW_CURRENT_HIGH +#define SNDRV_GF1_VB_PAN 0x0c +#define SNDRV_GF1_VW_OFFSET_RIGHT 0x0c +#define SNDRV_GF1_VB_VOLUME_CONTROL 0x0d +#define SNDRV_GF1_VB_UPPER_ADDRESS 0x10 +#define SNDRV_GF1_VW_EFFECT_HIGH 0x11 +#define SNDRV_GF1_VW_EFFECT_LOW 0x12 +#define SNDRV_GF1_VA_EFFECT SNDRV_GF1_VW_EFFECT_HIGH +#define SNDRV_GF1_VW_OFFSET_LEFT 0x13 +#define SNDRV_GF1_VB_ACCUMULATOR 0x14 +#define SNDRV_GF1_VB_MODE 0x15 +#define SNDRV_GF1_VW_EFFECT_VOLUME 0x16 +#define SNDRV_GF1_VB_FREQUENCY_LFO 0x17 +#define SNDRV_GF1_VB_VOLUME_LFO 0x18 +#define SNDRV_GF1_VW_OFFSET_RIGHT_FINAL 0x1b +#define SNDRV_GF1_VW_OFFSET_LEFT_FINAL 0x1c +#define SNDRV_GF1_VW_EFFECT_VOLUME_FINAL 0x1d + +/* ICS registers */ + +#define SNDRV_ICS_MIC_DEV 0 +#define SNDRV_ICS_LINE_DEV 1 +#define SNDRV_ICS_CD_DEV 2 +#define SNDRV_ICS_GF1_DEV 3 +#define SNDRV_ICS_NONE_DEV 4 +#define SNDRV_ICS_MASTER_DEV 5 + +/* LFO */ + +#define SNDRV_LFO_TREMOLO 0 +#define SNDRV_LFO_VIBRATO 1 + +/* misc */ + +#define SNDRV_GF1_DMA_UNSIGNED 0x80 +#define SNDRV_GF1_DMA_16BIT 0x40 +#define SNDRV_GF1_DMA_IRQ 0x20 +#define SNDRV_GF1_DMA_WIDTH16 0x04 +#define SNDRV_GF1_DMA_READ 0x02 /* read from GUS's DRAM */ +#define SNDRV_GF1_DMA_ENABLE 0x01 + +/* ramp ranges */ + +#define SNDRV_GF1_ATTEN(x) (snd_gf1_atten_table[x]) +#define SNDRV_GF1_MIN_VOLUME 1800 +#define SNDRV_GF1_MAX_VOLUME 4095 +#define SNDRV_GF1_MIN_OFFSET (SNDRV_GF1_MIN_VOLUME>>4) +#define SNDRV_GF1_MAX_OFFSET 255 +#define SNDRV_GF1_MAX_TDEPTH 90 + +/* defines for memory manager */ + +#define SNDRV_GF1_MEM_BLOCK_16BIT 0x0001 + +#define SNDRV_GF1_MEM_OWNER_DRIVER 0x0001 +#define SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE 0x0002 +#define SNDRV_GF1_MEM_OWNER_WAVE_GF1 0x0003 +#define SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF 0x0004 + +/* constants for interrupt handlers */ + +#define SNDRV_GF1_HANDLER_MIDI_OUT 0x00010000 +#define SNDRV_GF1_HANDLER_MIDI_IN 0x00020000 +#define SNDRV_GF1_HANDLER_TIMER1 0x00040000 +#define SNDRV_GF1_HANDLER_TIMER2 0x00080000 +#define SNDRV_GF1_HANDLER_VOICE 0x00100000 +#define SNDRV_GF1_HANDLER_DMA_WRITE 0x00200000 +#define SNDRV_GF1_HANDLER_DMA_READ 0x00400000 +#define SNDRV_GF1_HANDLER_ALL (0xffff0000&~SNDRV_GF1_HANDLER_VOICE) + +/* constants for DMA flags */ + +#define SNDRV_GF1_DMA_TRIGGER 1 + +/* --- */ + +struct _snd_gus_card; +typedef struct _snd_gus_card snd_gus_card_t; + +/* GF1 specific structure */ + +typedef struct _snd_gf1_bank_info { + unsigned int address; + unsigned int size; +} snd_gf1_bank_info_t; + +typedef struct _snd_gf1_mem_block { + unsigned short flags; /* flags - SNDRV_GF1_MEM_BLOCK_XXXX */ + unsigned short owner; /* owner - SNDRV_GF1_MEM_OWNER_XXXX */ + unsigned int share; /* share count */ + unsigned int share_id[4]; /* share ID */ + unsigned int ptr; + unsigned int size; + char *name; + struct _snd_gf1_mem_block *next; + struct _snd_gf1_mem_block *prev; +} snd_gf1_mem_block_t; + +typedef struct _snd_gf1_mem { + snd_gf1_bank_info_t banks_8[4]; + snd_gf1_bank_info_t banks_16[4]; + snd_gf1_mem_block_t *first; + snd_gf1_mem_block_t *last; + struct semaphore memory_mutex; +} snd_gf1_mem_t; + +typedef struct snd_gf1_dma_block { + void *buffer; /* buffer in computer's RAM */ + unsigned long buf_addr; /* buffer address */ + unsigned int addr; /* address in onboard memory */ + unsigned int count; /* count in bytes */ + unsigned int cmd; /* DMA command (format) */ + void (*ack)(snd_gus_card_t * gus, void *private_data); + void *private_data; + struct snd_gf1_dma_block *next; +} snd_gf1_dma_block_t; + +typedef struct { + snd_midi_channel_set_t * chset; + snd_gus_card_t * gus; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + int midi_has_voices: 1; +} snd_gus_port_t; + +typedef struct _snd_gus_voice snd_gus_voice_t; + +typedef struct { + void (*sample_start)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); + void (*sample_stop)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); + void (*sample_freq)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); + void (*sample_volume)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); + void (*sample_loop)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); + void (*sample_pos)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); + void (*sample_private1)(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); +} snd_gus_sample_ops_t; + +#define SNDRV_GF1_VOICE_TYPE_PCM 0 +#define SNDRV_GF1_VOICE_TYPE_SYNTH 1 +#define SNDRV_GF1_VOICE_TYPE_MIDI 2 + +#define SNDRV_GF1_VFLG_RUNNING (1<<0) +#define SNDRV_GF1_VFLG_EFFECT_TIMER1 (1<<1) +#define SNDRV_GF1_VFLG_PAN (1<<2) + +typedef enum { + VENV_BEFORE, + VENV_ATTACK, + VENV_SUSTAIN, + VENV_RELEASE, + VENV_DONE, + VENV_VOLUME +} snd_gus_volume_state_t; + +struct _snd_gus_voice { + int number; + int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + unsigned char pad; + +#ifdef CONFIG_SND_DEBUG + unsigned int interrupt_stat_wave; + unsigned int interrupt_stat_volume; +#endif + void (*handler_wave) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*handler_volume) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*handler_effect) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*volume_change) (snd_gus_card_t * gus); + + snd_gus_sample_ops_t *sample_ops; + + snd_seq_instr_t instr; + + /* running status / registers */ + + snd_seq_ev_volume_t sample_volume; + + unsigned short fc_register; + unsigned short fc_lfo; + unsigned short gf1_volume; + unsigned char control; + unsigned char mode; + unsigned char gf1_pan; + unsigned char effect_accumulator; + unsigned char volume_control; + unsigned char venv_value_next; + snd_gus_volume_state_t venv_state; + snd_gus_volume_state_t venv_state_prev; + unsigned short vlo; + unsigned short vro; + unsigned short gf1_effect_volume; + + /* --- */ + + void *private_data; + void (*private_free)(snd_gus_voice_t *voice); +}; + +struct _snd_gf1 { + + unsigned int enh_mode:1, /* enhanced mode (GFA1) */ + hw_lfo:1, /* use hardware LFO */ + sw_lfo:1, /* use software LFO */ + effect:1; /* use effect voices */ + + unsigned long port; /* port of GF1 chip */ + struct resource *res_port1; + struct resource *res_port2; + int irq; /* IRQ number */ + int dma1; /* DMA1 number */ + int dma2; /* DMA2 number */ + unsigned int memory; /* GUS's DRAM size in bytes */ + unsigned int rom_memory; /* GUS's ROM size in bytes */ + unsigned int rom_present; /* bitmask */ + unsigned int rom_banks; /* GUS's ROM banks */ + + snd_gf1_mem_t mem_alloc; + + /* registers */ + unsigned short reg_page; + unsigned short reg_regsel; + unsigned short reg_data8; + unsigned short reg_data16; + unsigned short reg_irqstat; + unsigned short reg_dram; + unsigned short reg_timerctrl; + unsigned short reg_timerdata; + unsigned char ics_regs[6][2]; + /* --------- */ + + unsigned char active_voices; /* active voices */ + unsigned char active_voice; /* selected voice (GF1PAGE register) */ + + snd_gus_voice_t voices[32]; /* GF1 voices */ + + unsigned int default_voice_address; + + unsigned short playback_freq; /* GF1 playback (mixing) frequency */ + unsigned short mode; /* see to SNDRV_GF1_MODE_XXXX */ + unsigned char volume_ramp; + unsigned char smooth_pan; + unsigned char full_range_pan; + unsigned char pad0; + + unsigned char *lfos; + + /* interrupt handlers */ + + void (*interrupt_handler_midi_out) (snd_gus_card_t * gus); + void (*interrupt_handler_midi_in) (snd_gus_card_t * gus); + void (*interrupt_handler_timer1) (snd_gus_card_t * gus); + void (*interrupt_handler_timer2) (snd_gus_card_t * gus); + void (*interrupt_handler_dma_write) (snd_gus_card_t * gus); + void (*interrupt_handler_dma_read) (snd_gus_card_t * gus); + +#ifdef CONFIG_SND_DEBUG + unsigned int interrupt_stat_midi_out; + unsigned int interrupt_stat_midi_in; + unsigned int interrupt_stat_timer1; + unsigned int interrupt_stat_timer2; + unsigned int interrupt_stat_dma_write; + unsigned int interrupt_stat_dma_read; + unsigned int interrupt_stat_voice_lost; +#endif + + /* synthesizer */ + + int seq_client; + snd_gus_port_t seq_ports[4]; + snd_seq_kinstr_list_t *ilist; + snd_iwffff_ops_t iwffff_ops; + snd_gf1_ops_t gf1_ops; + snd_simple_ops_t simple_ops; + + /* timer */ + + unsigned short timer_enabled; + snd_timer_t *timer1; + snd_timer_t *timer2; + + /* midi */ + + unsigned short uart_cmd; + unsigned int uart_framing; + unsigned int uart_overrun; + + /* dma operations */ + + unsigned int dma_flags; + unsigned int dma_shared; + snd_gf1_dma_block_t *dma_data_pcm; + snd_gf1_dma_block_t *dma_data_pcm_last; + snd_gf1_dma_block_t *dma_data_synth; + snd_gf1_dma_block_t *dma_data_synth_last; + void (*dma_ack)(snd_gus_card_t * gus, void *private_data); + void *dma_private_data; + + /* pcm */ + int pcm_channels; + int pcm_alloc_voices; + unsigned short pcm_volume_level_left; + unsigned short pcm_volume_level_right; + unsigned short pcm_volume_level_left1; + unsigned short pcm_volume_level_right1; + + unsigned char pcm_rcntrl_reg; + unsigned char pad_end; +}; + +/* main structure for GUS card */ + +struct _snd_gus_card { + snd_card_t *card; + + unsigned int + initialized: 1, /* resources were initialized */ + equal_irq:1, /* GF1 and CODEC shares IRQ (GUS MAX only) */ + equal_dma:1, /* if dma channels are equal (not valid for daughter board) */ + ics_flag:1, /* have we ICS mixer chip */ + ics_flipped:1, /* ICS mixer have flipped some channels? */ + codec_flag:1, /* have we CODEC chip? */ + max_flag:1, /* have we GUS MAX card? */ + max_ctrl_flag:1, /* have we original GUS MAX card? */ + daughter_flag:1, /* have we daughter board? */ + interwave:1, /* hey - we have InterWave card */ + ess_flag:1, /* ESS chip found... GUS Extreme */ + ace_flag:1, /* GUS ACE detected */ + uart_enable:1; /* enable MIDI UART */ + unsigned short revision; /* revision of chip */ + unsigned short max_cntrl_val; /* GUS MAX control value */ + unsigned short mix_cntrl_reg; /* mixer control register */ + unsigned short joystick_dac; /* joystick DAC level */ + int timer_dev; /* timer device */ + + struct _snd_gf1 gf1; /* gf1 specific variables */ + snd_pcm_t *pcm; + snd_pcm_substream_t *pcm_cap_substream; + unsigned int c_dma_size; + unsigned int c_period_size; + unsigned int c_pos; + + snd_rawmidi_t *midi_uart; + snd_rawmidi_substream_t *midi_substream_output; + snd_rawmidi_substream_t *midi_substream_input; + + snd_seq_device_t *seq_dev; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + spinlock_t active_voice_lock; + spinlock_t event_lock; + spinlock_t dma_lock; + spinlock_t pcm_volume_level_lock; + spinlock_t uart_cmd_lock; + struct semaphore dma_mutex; + struct semaphore register_mutex; +}; + +/* I/O functions for GF1/InterWave chip - gus_io.c */ + +static inline void snd_gf1_select_voice(snd_gus_card_t * gus, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->active_voice_lock, flags); + if (voice != gus->gf1.active_voice) { + gus->gf1.active_voice = voice; + outb(voice, GUSP(gus, GF1PAGE)); + } + spin_unlock_irqrestore(&gus->active_voice_lock, flags); +} + +static inline void snd_gf1_uart_cmd(snd_gus_card_t * gus, unsigned char b) +{ + outb(gus->gf1.uart_cmd = b, GUSP(gus, MIDICTRL)); +} + +static inline unsigned char snd_gf1_uart_stat(snd_gus_card_t * gus) +{ + return inb(GUSP(gus, MIDISTAT)); +} + +static inline void snd_gf1_uart_put(snd_gus_card_t * gus, unsigned char b) +{ + outb(b, GUSP(gus, MIDIDATA)); +} + +static inline unsigned char snd_gf1_uart_get(snd_gus_card_t * gus) +{ + return inb(GUSP(gus, MIDIDATA)); +} + +extern void snd_gf1_delay(snd_gus_card_t * gus); + +extern void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg); + +extern void snd_gf1_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned char snd_gf1_read8(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_look8(gus, reg | 0x80); +} +extern void snd_gf1_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data); +extern unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned short snd_gf1_read16(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_look16(gus, reg | 0x80); +} +extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data); +extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data); +extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count); +extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit); +extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit); +extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg); +extern void snd_gf1_i_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg); +extern void snd_gf1_i_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data); +extern inline unsigned char snd_gf1_i_read8(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_i_look8(gus, reg | 0x80); +} +extern unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_i_look16(gus, reg | 0x80); +} +extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit); +extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit); + +extern void snd_gf1_select_active_voices(snd_gus_card_t * gus); + +/* gus_lfo.c */ + +struct _SND_IW_LFO_PROGRAM { + unsigned short freq_and_control; + unsigned char depth_final; + unsigned char depth_inc; + unsigned short twave; + unsigned short depth; +}; + +#if 0 +extern void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice); +#endif +extern void snd_gf1_lfo_init(snd_gus_card_t * gus); +extern void snd_gf1_lfo_done(snd_gus_card_t * gus); +extern void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, struct _SND_IW_LFO_PROGRAM *program); +extern void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type); +extern void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type); +extern void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, int lfo_type, int freq); +extern void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, int lfo_type, int depth); +extern void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, int freq, int current_depth, int depth, int sweep, int shape); +extern void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type); +#if 0 +extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *command); +#endif + +/* gus_mem.c */ + +void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup); +int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block); +snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, + unsigned int address); +snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, + unsigned int *share_id); +snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, + char *name, int size, int w_16, + int align, unsigned int *share_id); +int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address); +int snd_gf1_mem_free_owner(snd_gf1_mem_t * alloc, int owner); +int snd_gf1_mem_init(snd_gus_card_t * gus); +int snd_gf1_mem_done(snd_gus_card_t * gus); + +/* gus_mem_proc.c */ + +int snd_gf1_mem_proc_init(snd_gus_card_t * gus); + +/* gus_dma.c */ + +void snd_gf1_dma_program(snd_gus_card_t * gus, unsigned int addr, + unsigned long buf_addr, unsigned int count, + unsigned int cmd); +void snd_gf1_dma_ack(snd_gus_card_t * gus); +int snd_gf1_dma_init(snd_gus_card_t * gus); +int snd_gf1_dma_done(snd_gus_card_t * gus); +int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, + snd_gf1_dma_block_t * block, + int atomic, + int synth); + +/* gus_volume.c */ + +unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol); +unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol); +unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, + unsigned short start, + unsigned short end, + unsigned int us); +unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2); +unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens); +unsigned short snd_gf1_compute_freq(unsigned int freq, + unsigned int rate, + unsigned short mix_rate); + +/* gus_reset.c */ + +void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what); +void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice); +void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice); +void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max); +void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max); +snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port); +void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice); +int snd_gf1_start(snd_gus_card_t * gus); +int snd_gf1_stop(snd_gus_card_t * gus); + +/* gus_mixer.c */ + +int snd_gf1_new_mixer(snd_gus_card_t * gus); + +/* gus_pcm.c */ + +int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm); + +#ifdef CONFIG_SND_DEBUG +extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus); +extern void snd_gf1_print_global_registers(snd_gus_card_t * gus); +extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus); +extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit); +#endif + +/* gus.c */ + +int snd_gus_use_inc(snd_gus_card_t * gus); +void snd_gus_use_dec(snd_gus_card_t * gus); +int snd_gus_create(snd_card_t * card, + unsigned long port, + int irq, int dma1, int dma2, + int timer_dev, + int voices, + int pcm_channels, + int effect, + snd_gus_card_t ** rgus); +int snd_gus_initialize(snd_gus_card_t * gus); + +/* gus_irq.c */ + +void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_SND_DEBUG +void snd_gus_irq_profile_init(snd_gus_card_t *gus); +#endif + +/* gus_uart.c */ + +int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t **rrawmidi); + +#if 0 +extern void snd_engine_instrument_register(unsigned short mode, + struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds, + struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds, + struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds); +extern int snd_engine_instrument_register_ask(unsigned short mode); +#endif + +/* gus_dram.c */ +int snd_gus_dram_write(snd_gus_card_t *gus, char *ptr, + unsigned int addr, unsigned int size); +int snd_gus_dram_read(snd_gus_card_t *gus, char *ptr, + unsigned int addr, unsigned int size, int rom); + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + +/* gus_sample.c */ +void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p); + +/* gus_simple.c */ +void snd_gf1_simple_init(snd_gus_voice_t *voice); + +/* gus_instr.c */ +int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, + int atomic); +int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, + int atomic); +int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); +int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); +int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, + int atomic); + +#endif /* CONFIG_SND_SEQUENCER */ + +#endif /* __SOUND_GUS_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/hwdep.h linux/include/sound/hwdep.h --- linux-2.4.21-rc1.orig/include/sound/hwdep.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/hwdep.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,72 @@ +#ifndef __SOUND_HWDEP_H +#define __SOUND_HWDEP_H + +/* + * Hardware dependent layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +typedef enum sndrv_hwdep_iface snd_hwdep_iface_t; +typedef struct sndrv_hwdep_info snd_hwdep_info_t; +typedef struct sndrv_hwdep_dsp_status snd_hwdep_dsp_status_t; +typedef struct sndrv_hwdep_dsp_image snd_hwdep_dsp_image_t; + +typedef struct _snd_hwdep_ops { + long long (*llseek) (snd_hwdep_t *hw, struct file * file, long long offset, int orig); + long (*read) (snd_hwdep_t * hw, char *buf, long count, loff_t *offset); + long (*write) (snd_hwdep_t * hw, const char *buf, long count, loff_t *offset); + int (*open) (snd_hwdep_t * hw, struct file * file); + int (*release) (snd_hwdep_t * hw, struct file * file); + unsigned int (*poll) (snd_hwdep_t * hw, struct file * file, poll_table * wait); + int (*ioctl) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg); + int (*mmap) (snd_hwdep_t * hw, struct file * file, struct vm_area_struct * vma); + int (*dsp_status) (snd_hwdep_t * hw, snd_hwdep_dsp_status_t * status); + int (*dsp_load) (snd_hwdep_t * hw, snd_hwdep_dsp_image_t * image); +} snd_hwdep_ops_t; + +struct _snd_hwdep { + snd_card_t *card; + int device; + char id[32]; + char name[80]; + int iface; + +#ifdef CONFIG_SND_OSSEMUL + char oss_dev[32]; + int oss_type; + int ossreg; +#endif + + snd_hwdep_ops_t ops; + wait_queue_head_t open_wait; + void *private_data; + void (*private_free) (snd_hwdep_t *hwdep); + + struct semaphore open_mutex; + int used; + unsigned int dsp_loaded; + unsigned int exclusive: 1; +}; + +extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep); + +#endif /* __SOUND_HWDEP_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/i2c.h linux/include/sound/i2c.h --- linux-2.4.21-rc1.orig/include/sound/i2c.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/i2c.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,102 @@ +#ifndef __SOUND_I2C_H +#define __SOUND_I2C_H + +/* + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +typedef struct _snd_i2c_device snd_i2c_device_t; +typedef struct _snd_i2c_bus snd_i2c_bus_t; + +#define SND_I2C_DEVICE_ADDRTEN (1<<0) /* 10-bit I2C address */ + +struct _snd_i2c_device { + struct list_head list; + snd_i2c_bus_t *bus; /* I2C bus */ + char name[32]; /* some useful device name */ + unsigned short flags; /* device flags */ + unsigned short addr; /* device address (might be 10-bit) */ + unsigned long private_value; + void *private_data; + void (*private_free)(snd_i2c_device_t *device); +}; + +#define snd_i2c_device(n) list_entry(n, snd_i2c_device_t, list) + +typedef struct _snd_i2c_bit_ops { + void (*start)(snd_i2c_bus_t *bus); /* transfer start */ + void (*stop)(snd_i2c_bus_t *bus); /* transfer stop */ + void (*direction)(snd_i2c_bus_t *bus, int clock, int data); /* set line direction (0 = write, 1 = read) */ + void (*setlines)(snd_i2c_bus_t *bus, int clock, int data); + int (*getclock)(snd_i2c_bus_t *bus); + int (*getdata)(snd_i2c_bus_t *bus, int ack); +} snd_i2c_bit_ops_t; + +typedef struct _snd_i2c_ops { + int (*sendbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count); + int (*readbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count); + int (*probeaddr)(snd_i2c_bus_t *bus, unsigned short addr); +} snd_i2c_ops_t; + +struct _snd_i2c_bus { + snd_card_t *card; /* card which I2C belongs to */ + char name[32]; /* some useful label */ + + spinlock_t lock; + + snd_i2c_bus_t *master; /* master bus when SCK/SCL is shared */ + struct list_head buses; /* master: slave buses sharing SCK/SCL, slave: link list */ + + struct list_head devices; /* attached devices to this bus */ + + union { + snd_i2c_bit_ops_t *bit; + void *ops; + } hw_ops; /* lowlevel operations */ + snd_i2c_ops_t *ops; /* midlevel operations */ + + unsigned long private_value; + void *private_data; + void (*private_free)(snd_i2c_bus_t *bus); +}; + +#define snd_i2c_slave_bus(n) list_entry(n, snd_i2c_bus_t, buses) + +int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c); +int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice); +int snd_i2c_device_free(snd_i2c_device_t *device); + +static inline void snd_i2c_lock(snd_i2c_bus_t *bus) { + if (bus->master) + spin_lock(&bus->master->lock); + else + spin_lock(&bus->lock); +} +static inline void snd_i2c_unlock(snd_i2c_bus_t *bus) { + if (bus->master) + spin_unlock(&bus->master->lock); + else + spin_unlock(&bus->lock); +} + +int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr); + +#endif /* __SOUND_I2C_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/info.h linux/include/sound/info.h --- linux-2.4.21-rc1.orig/include/sound/info.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/info.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,210 @@ +#ifndef __SOUND_INFO_H +#define __SOUND_INFO_H + +/* + * Header file for info interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +/* buffer for information */ +struct snd_info_buffer { + char *buffer; /* pointer to begin of buffer */ + char *curr; /* current position in buffer */ + unsigned long size; /* current size */ + unsigned long len; /* total length of buffer */ + int stop; /* stop flag */ + int error; /* error code */ +}; + +typedef struct snd_info_buffer snd_info_buffer_t; + +#define SNDRV_INFO_CONTENT_TEXT 0 +#define SNDRV_INFO_CONTENT_DATA 1 +#define SNDRV_INFO_CONTENT_DEVICE 2 + +struct snd_info_entry; + +struct snd_info_entry_text { + unsigned long read_size; + unsigned long write_size; + void (*read) (snd_info_entry_t *entry, snd_info_buffer_t * buffer); + void (*write) (snd_info_entry_t *entry, snd_info_buffer_t * buffer); +}; + +struct snd_info_entry_ops { + int (*open) (snd_info_entry_t *entry, + unsigned short mode, void **file_private_data); + int (*release) (snd_info_entry_t * entry, + unsigned short mode, void *file_private_data); + long (*read) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, char *buf, long count); + long (*write) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, const char *buf, long count); + long long (*llseek) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, long long offset, int orig); + unsigned int (*poll) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, poll_table * wait); + int (*ioctl) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, unsigned int cmd, unsigned long arg); + int (*mmap) (snd_info_entry_t *entry, void *file_private_data, + struct inode * inode, struct file * file, + struct vm_area_struct * vma); +}; + +struct snd_info_entry_device { + unsigned short major; + unsigned short minor; +}; + +struct snd_info_entry { + const char *name; + mode_t mode; + long size; + unsigned short content; + union { + struct snd_info_entry_text text; + struct snd_info_entry_ops *ops; + struct snd_info_entry_device device; + } c; + snd_info_entry_t *parent; + snd_card_t *card; + struct module *module; + void *private_data; + void (*private_free)(snd_info_entry_t *entry); + struct proc_dir_entry *p; + struct semaphore access; +}; + +extern int snd_info_check_reserved_words(const char *str); + +#ifdef CONFIG_SND_OSSEMUL +extern int snd_info_minor_register(void); +extern int snd_info_minor_unregister(void); +#endif + + +#ifdef CONFIG_PROC_FS + +extern snd_info_entry_t *snd_seq_root; +#ifdef CONFIG_SND_OSSEMUL +extern snd_info_entry_t *snd_oss_root; +#else +#define snd_oss_root NULL +#endif + +int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3))); +int snd_info_init(void); +int snd_info_done(void); + +int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len); +char *snd_info_get_str(char *dest, char *src, int len); +snd_info_entry_t *snd_info_create_module_entry(struct module * module, + const char *name, + snd_info_entry_t * parent); +snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, + const char *name, + snd_info_entry_t * parent); +void snd_info_free_entry(snd_info_entry_t * entry); +snd_info_entry_t *snd_info_create_device(const char *name, + unsigned int number, + unsigned int mode); +void snd_info_free_device(snd_info_entry_t * entry); +int snd_info_store_text(snd_info_entry_t * entry); +int snd_info_restore_text(snd_info_entry_t * entry); + +int snd_info_card_create(snd_card_t * card); +int snd_info_card_register(snd_card_t * card); +int snd_info_card_free(snd_card_t * card); +int snd_info_register(snd_info_entry_t * entry); +int snd_info_unregister(snd_info_entry_t * entry); + +struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent); +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de); + +/* for card drivers */ +int snd_card_proc_new(snd_card_t *card, const char *name, snd_info_entry_t **entryp); + +inline static void snd_info_set_text_ops(snd_info_entry_t *entry, + void *private_data, + void (*read)(snd_info_entry_t *, snd_info_buffer_t *)) +{ + entry->private_data = private_data; + entry->c.text.read_size = 1024; + entry->c.text.read = read; +} + + +#else + +#define snd_seq_root NULL +#define snd_oss_root NULL + +static inline int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { return 0; } +static inline int snd_info_init(void) { return 0; } +static inline int snd_info_done(void) { return 0; } + +static inline int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) { return 0; } +static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; } +static inline snd_info_entry_t *snd_info_create_module_entry(struct module * module, const char *name, snd_info_entry_t * parent) { return NULL; } +static inline snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, const char *name, snd_info_entry_t * parent) { return NULL; } +static inline void snd_info_free_entry(snd_info_entry_t * entry) { ; } +static inline snd_info_entry_t *snd_info_create_device(const char *name, + unsigned int number, + unsigned int mode) { return NULL; } +static inline void snd_info_free_device(snd_info_entry_t * entry) { ; } + +static inline int snd_info_card_create(snd_card_t * card) { return 0; } +static inline int snd_info_card_register(snd_card_t * card) { return 0; } +static inline int snd_info_card_free(snd_card_t * card) { return 0; } +static inline int snd_info_register(snd_info_entry_t * entry) { return 0; } +static inline int snd_info_unregister(snd_info_entry_t * entry) { return 0; } + +static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return NULL; } +static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) { ; } + +#define snd_card_proc_new(card,name,entryp) 0 /* always success */ +#define snd_info_set_text_ops(entry,private_data,read) /*NOP*/ + +#endif + +/* + * OSS info part + */ + +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) + +#define SNDRV_OSS_INFO_DEV_AUDIO 0 +#define SNDRV_OSS_INFO_DEV_SYNTH 1 +#define SNDRV_OSS_INFO_DEV_MIDI 2 +#define SNDRV_OSS_INFO_DEV_TIMERS 4 +#define SNDRV_OSS_INFO_DEV_MIXERS 5 + +#define SNDRV_OSS_INFO_DEV_COUNT 6 + +extern int snd_oss_info_register(int dev, int num, char *string); +#define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL) + +#endif /* CONFIG_SND_OSSEMUL && CONFIG_PROC_FS */ + +#endif /* __SOUND_INFO_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/initval.h linux/include/sound/initval.h --- linux-2.4.21-rc1.orig/include/sound/initval.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/initval.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,165 @@ +#ifndef __SOUND_INITVAL_H +#define __SOUND_INITVAL_H + +/* + * Init values for soundcard modules + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef MODULE_GENERIC_STRING +#ifdef MODULE +#define MODULE_GENERIC_STRING(name, string) \ +static const char __module_generic_string_##name [] \ + __attribute__ ((unused, __section__(".modstring"))) = #name "=" string; +#else +#define MODULE_GENERIC_STRING(name, string) +#endif +#endif + +#define MODULE_CLASSES(val) MODULE_GENERIC_STRING(info_classes, val) +#define MODULE_DEVICES(val) MODULE_GENERIC_STRING(info_devices, val) +#define MODULE_PARM_SYNTAX(id, val) MODULE_GENERIC_STRING(info_parm_##id, val) + +#define SNDRV_AUTO_PORT 0xffff +#define SNDRV_AUTO_IRQ 0xffff +#define SNDRV_AUTO_DMA 0xffff +#define SNDRV_AUTO_DMA_SIZE (0x7fffffff) + +#define SNDRV_DEFAULT_IDX1 (-1) +#define SNDRV_DEFAULT_STR1 NULL +#define SNDRV_DEFAULT_ENABLE1 1 +#define SNDRV_DEFAULT_PORT1 SNDRV_AUTO_PORT +#define SNDRV_DEFAULT_IRQ1 SNDRV_AUTO_IRQ +#define SNDRV_DEFAULT_DMA1 SNDRV_AUTO_DMA +#define SNDRV_DEFAULT_DMA_SIZE1 SNDRV_AUTO_DMA_SIZE +#define SNDRV_DEFAULT_PTR1 SNDRV_DEFAULT_STR1 + +#define SNDRV_DEFAULT_IDX { [0 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_STR { [0 ... (SNDRV_CARDS-1)] = NULL } +#define SNDRV_DEFAULT_ENABLE { 1, [1 ... (SNDRV_CARDS-1)] = 0 } +#define SNDRV_DEFAULT_ENABLE_PNP { [0 ... (SNDRV_CARDS-1)] = 1 } +#ifdef __ISAPNP__ +#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE_PNP +#else +#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE +#endif +#define SNDRV_DEFAULT_PORT { SNDRV_AUTO_PORT, [1 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_IRQ { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_IRQ } +#define SNDRV_DEFAULT_DMA { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA } +#define SNDRV_DEFAULT_DMA_SIZE { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE } +#define SNDRV_DEFAULT_PTR SNDRV_DEFAULT_STR + +#define SNDRV_BOOLEAN_TRUE_DESC "allows:{{0,Disabled},{1,Enabled}},default:1,dialog:check" +#define SNDRV_BOOLEAN_FALSE_DESC "allows:{{0,Disabled},{1,Enabled}},default:0,dialog:check" + +#define SNDRV_ENABLED "enable:(enable)" + +#define SNDRV_INDEX_DESC SNDRV_ENABLED ",allows:{{0,7}},unique,skill:required,dialog:list" +#define SNDRV_ID_DESC SNDRV_ENABLED ",unique" +#define SNDRV_ENABLE_DESC SNDRV_BOOLEAN_FALSE_DESC +#define SNDRV_ISAPNP_DESC SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC +#define SNDRV_DMA8_DESC SNDRV_ENABLED ",allows:{{0,1},{3}},dialog:list" +#define SNDRV_DMA16_DESC SNDRV_ENABLED ",allows:{{5,7}},dialog:list" +#define SNDRV_DMA_DESC SNDRV_ENABLED ",allows:{{0,1},{3},{5,7}},dialog:list" +#define SNDRV_IRQ_DESC SNDRV_ENABLED ",allows:{{5},{7},{9},{10,12},{14,15}},dialog:list" +#define SNDRV_DMA_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" +#define SNDRV_DMA8_SIZE_DESC SNDRV_ENABLED ",allows:{{4, 64}},default:64,skill:advanced" +#define SNDRV_DMA16_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" +#define SNDRV_PORT12_DESC SNDRV_ENABLED ",allows:{{0,0x3fff}},base:16" +#define SNDRV_PORT_DESC SNDRV_ENABLED ",allows:{{0,0xffff}},base:16" + +#ifdef SNDRV_LEGACY_AUTO_PROBE +static int snd_legacy_auto_probe(unsigned long *ports, int (*probe)(unsigned long port)) +{ + int result = 0; /* number of detected cards */ + + while ((signed long)*ports != -1) { + if (probe(*ports) >= 0) + result++; + ports++; + } + return result; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_IRQ +#include + +static void snd_legacy_empty_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int snd_legacy_find_free_irq(int *irq_table) +{ + while (*irq_table != -1) { + if (!request_irq(*irq_table, snd_legacy_empty_irq_handler, + SA_INTERRUPT, "ALSA Test IRQ", (void *) irq_table)) { + free_irq(*irq_table, (void *) irq_table); + return *irq_table; + } + irq_table++; + } + return -1; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_DMA +static int snd_legacy_find_free_dma(int *dma_table) +{ + while (*dma_table != -1) { + if (!request_dma(*dma_table, "ALSA Test DMA")) { + free_dma(*dma_table); + return *dma_table; + } + dma_table++; + } + return -1; +} +#endif + +#if defined(SNDRV_GET_ID) && !defined(MODULE) +#include +#include +static int __init get_id(char **str, char **dst) +{ + char *s, *d; + + if (!(*str) || !(**str)) + return 0; + for (s = *str; isalpha(*s) || isdigit(*s) || *s == '_'; s++); + if (s != *str) { + *dst = (char *)kmalloc((s - *str) + 1, GFP_KERNEL); + s = *str; d = *dst; + while (isalpha(*s) || isdigit(*s) || *s == '_') { + if (d != NULL) + *d++ = *s; + s++; + } + if (d != NULL) + *d = '\0'; + } + *str = s; + if (*s == ',') { + (*str)++; + return 2; + } + return 1; +} +#endif + +#endif /* __SOUND_INITVAL_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/memalloc.h linux/include/sound/memalloc.h --- linux-2.4.21-rc1.orig/include/sound/memalloc.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/memalloc.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,193 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Takashi Iwai + * + * Generic memory allocators + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_MEMALLOC_H +#define __SOUND_MEMALLOC_H + +#include +#ifdef CONFIG_SBUS +#include +#endif + +/* + * buffer device info + */ +struct snd_dma_device { + int type; /* SNDRV_MEM_TYPE_XXX */ + union { + struct pci_dev *pci; /* for PCI and PCI-SG types */ + unsigned int flags; /* GFP_XXX for continous and ISA types */ +#ifdef CONFIG_SBUS + struct sbus_dev *sbus; /* for SBUS type */ +#endif + } dev; + unsigned int id; /* a unique ID */ +}; + +/* + * buffer types + */ +#define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */ +#define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ +#define SNDRV_DMA_TYPE_ISA 2 /* ISA continuous */ +#define SNDRV_DMA_TYPE_PCI 3 /* PCI continuous */ +#define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */ +#define SNDRV_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */ + +#ifdef CONFIG_PCI +/* + * compose a snd_dma_device struct for the PCI device + */ +static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id) +{ + memset(dev, 0, sizeof(*dev)); + dev->type = SNDRV_DMA_TYPE_PCI; + dev->dev.pci = pci; + dev->id = id; +} +#endif + + +/* + * info for buffer allocation + */ +struct snd_dma_buffer { + unsigned char *area; /* virtual pointer */ + dma_addr_t addr; /* physical address */ + size_t bytes; /* buffer size in bytes */ + void *private_data; /* private for allocator; don't touch */ +}; + +/* allocate/release a buffer */ +int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, struct snd_dma_buffer *dmab); +void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + +/* buffer-preservation managements */ +size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); +int snd_dma_free_reserved(const struct snd_dma_device *dev); +int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + + +/* + * Generic memory allocators + */ + +/* + * continuous pages + */ +void *snd_malloc_pages(size_t size, unsigned int gfp_flags); +void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size); +void snd_free_pages(void *ptr, size_t size); + +#ifdef CONFIG_PCI +/* + * PCI continuous pages + */ +void *snd_malloc_pci_pages(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr); +void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr, size_t *res_size); +void snd_free_pci_pages(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t dma_addr); +/* one page allocation */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr); +#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr) +#endif + +#ifdef CONFIG_SBUS +/* + * SBUS continuous pages + */ +void *snd_malloc_sbus_pages(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr); +void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr, size_t *res_size); +void snd_free_sbus_pages(struct sbus_dev *sdev, size_t size, void *ptr, dma_addr_t dma_addr); +#endif + +#ifdef CONFIG_ISA +/* + * ISA continuous pages + */ +void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr); +void *snd_malloc_isa_pages_fallback(size_t size, dma_addr_t *dma_addr, size_t *res_size); +void snd_free_isa_pages(size_t size, void *ptr, dma_addr_t addr); +#ifdef CONFIG_PCI +#define snd_malloc_isa_pages(size, dma_addr) snd_malloc_pci_pages(NULL, size, dma_addr) +#define snd_malloc_isa_pages_fallback(size, dma_addr, res_size) snd_malloc_pci_pages_fallback(NULL, size, dma_addr, res_size) +#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pci_pages(NULL, size, ptr, dma_addr) +#else /* !CONFIG_PCI */ +#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size) +#endif /* CONFIG_PCI */ +#endif /* CONFIG_ISA */ + +#ifdef CONFIG_PCI +/* + * Scatter-Gather PCI pages + */ +struct snd_sg_page { + void *buf; + dma_addr_t addr; +}; + +struct snd_sg_buf { + int size; /* allocated byte size */ + int pages; /* allocated pages */ + int tblsize; /* allocated table size */ + struct snd_sg_page *table; /* address table */ + struct page **page_table; /* page table (for vmap/vunmap) */ + struct pci_dev *pci; +}; + +void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab); +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); + +/* + * return the pages matching with the given byte size + */ +static inline unsigned int snd_sgbuf_aligned_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + +/* + * return the physical address at the corresponding offset + */ +static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset) +{ + return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE; +} +#endif /* CONFIG_PCI */ + + +/* + * wrappers + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) +#ifdef CONFIG_PCI +#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__) +#define HACK_PCI_ALLOC_CONSISTENT +/* a hack for 2.4/5 kernels for better allocation of large buffers */ +void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle); +#endif /* arch */ +#endif /* CONFIG_PCI */ +#endif /* LINUX >= 2.4.0 */ + + +#endif /* __SOUND_MEMALLOC_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/minors.h linux/include/sound/minors.h --- linux-2.4.21-rc1.orig/include/sound/minors.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/minors.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,86 @@ +#ifndef __SOUND_MINORS_H +#define __SOUND_MINORS_H + +/* + * MINOR numbers + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MINOR_DEVICES 32 +#define SNDRV_MINOR_CARD(minor) ((minor) >> 5) +#define SNDRV_MINOR_DEVICE(minor) ((minor) & 0x001f) +#define SNDRV_MINOR(card, dev) (((card) << 5) | (dev)) + +#define SNDRV_MINOR_CONTROL 0 /* 0 - 0 */ +#define SNDRV_MINOR_SEQUENCER 1 +#define SNDRV_MINOR_TIMER (1+32) +#define SNDRV_MINOR_HWDEP 4 /* 4 - 7 */ +#define SNDRV_MINOR_HWDEPS 4 +#define SNDRV_MINOR_RAWMIDI 8 /* 8 - 15 */ +#define SNDRV_MINOR_RAWMIDIS 8 +#define SNDRV_MINOR_PCM_PLAYBACK 16 /* 16 - 23 */ +#define SNDRV_MINOR_PCM_CAPTURE 24 /* 24 - 31 */ +#define SNDRV_MINOR_PCMS 8 + +#define SNDRV_DEVICE_TYPE_CONTROL SNDRV_MINOR_CONTROL +#define SNDRV_DEVICE_TYPE_HWDEP SNDRV_MINOR_HWDEP +#define SNDRV_DEVICE_TYPE_MIXER SNDRV_MINOR_MIXER +#define SNDRV_DEVICE_TYPE_RAWMIDI SNDRV_MINOR_RAWMIDI +#define SNDRV_DEVICE_TYPE_PCM_PLAYBACK SNDRV_MINOR_PCM_PLAYBACK +#define SNDRV_DEVICE_TYPE_PCM_PLOOP SNDRV_MINOR_PCM_PLOOP +#define SNDRV_DEVICE_TYPE_PCM_CAPTURE SNDRV_MINOR_PCM_CAPTURE +#define SNDRV_DEVICE_TYPE_PCM_CLOOP SNDRV_MINOR_PCM_CLOOP +#define SNDRV_DEVICE_TYPE_SEQUENCER SNDRV_MINOR_SEQUENCER +#define SNDRV_DEVICE_TYPE_TIMER SNDRV_MINOR_TIMER + +#ifdef CONFIG_SND_OSSEMUL + +#define SNDRV_MINOR_OSS_DEVICES 16 +#define SNDRV_MINOR_OSS_CARD(minor) ((minor) >> 4) +#define SNDRV_MINOR_OSS_DEVICE(minor) ((minor) & 0x000f) +#define SNDRV_MINOR_OSS(card, dev) (((card) << 4) | (dev)) + +#define SNDRV_MINOR_OSS_MIXER 0 /* /dev/mixer - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_SEQUENCER 1 /* /dev/sequencer - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_MIDI 2 /* /dev/midi - native midi interface - OSS 3.XX compatible - UART */ +#define SNDRV_MINOR_OSS_PCM 3 /* alias */ +#define SNDRV_MINOR_OSS_PCM_8 3 /* /dev/dsp - 8bit PCM - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_AUDIO 4 /* /dev/audio - SunSparc compatible */ +#define SNDRV_MINOR_OSS_PCM_16 5 /* /dev/dsp16 - 16bit PCM - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_SNDSTAT 6 /* /dev/sndstat - for compatibility with OSS */ +#define SNDRV_MINOR_OSS_RESERVED7 7 /* reserved for future use */ +#define SNDRV_MINOR_OSS_MUSIC 8 /* /dev/music - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_DMMIDI 9 /* /dev/dmmidi0 - this device can have another minor # with OSS */ +#define SNDRV_MINOR_OSS_DMFM 10 /* /dev/dmfm0 - this device can have another minor # with OSS */ +#define SNDRV_MINOR_OSS_MIXER1 11 /* alternate mixer */ +#define SNDRV_MINOR_OSS_PCM1 12 /* alternate PCM (GF-A-1) */ +#define SNDRV_MINOR_OSS_MIDI1 13 /* alternate midi - SYNTH */ +#define SNDRV_MINOR_OSS_DMMIDI1 14 /* alternate dmmidi - SYNTH */ +#define SNDRV_MINOR_OSS_RESERVED15 15 /* reserved for future use */ + +#define SNDRV_OSS_DEVICE_TYPE_MIXER 0 +#define SNDRV_OSS_DEVICE_TYPE_SEQUENCER 1 +#define SNDRV_OSS_DEVICE_TYPE_PCM 2 +#define SNDRV_OSS_DEVICE_TYPE_MIDI 3 +#define SNDRV_OSS_DEVICE_TYPE_DMFM 4 +#define SNDRV_OSS_DEVICE_TYPE_SNDSTAT 5 +#define SNDRV_OSS_DEVICE_TYPE_MUSIC 6 + +#endif + +#endif /* __SOUND_MINORS_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/mixer_oss.h linux/include/sound/mixer_oss.h --- linux-2.4.21-rc1.orig/include/sound/mixer_oss.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/mixer_oss.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,76 @@ +#ifndef __SOUND_MIXER_OSS_H +#define __SOUND_MIXER_OSS_H + +/* + * OSS MIXER API + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + +typedef struct _snd_oss_mixer_slot snd_mixer_oss_slot_t; +typedef struct _snd_oss_file snd_mixer_oss_file_t; + +typedef int (*snd_mixer_oss_get_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *left, int *right); +typedef int (*snd_mixer_oss_put_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int left, int right); +typedef int (*snd_mixer_oss_get_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *active); +typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int active); +typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, unsigned int *active_index); +typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, unsigned int active_index); + +#define SNDRV_OSS_MAX_MIXERS 32 + +struct _snd_oss_mixer_slot { + int number; + int stereo: 1; + snd_mixer_oss_get_volume_t get_volume; + snd_mixer_oss_put_volume_t put_volume; + snd_mixer_oss_get_recsrc_t get_recsrc; + snd_mixer_oss_put_recsrc_t put_recsrc; + unsigned long private_value; + void *private_data; + void (*private_free)(snd_mixer_oss_slot_t *slot); + int volume[2]; +}; + +struct _snd_oss_mixer { + snd_card_t *card; + char id[16]; + char name[32]; + snd_mixer_oss_slot_t slots[SNDRV_OSS_MAX_MIXERS]; /* OSS mixer slots */ + unsigned int mask_recsrc; /* exclusive recsrc mask */ + snd_mixer_oss_get_recsrce_t get_recsrc; + snd_mixer_oss_put_recsrce_t put_recsrc; + void *private_data_recsrc; + void (*private_free_recsrc)(snd_mixer_oss_t *mixer); + struct semaphore reg_mutex; + snd_info_entry_t *proc_entry; + int oss_dev_alloc; + /* --- */ + int oss_recsrc; +}; + +struct _snd_oss_file { + snd_card_t *card; + snd_mixer_oss_t *mixer; +}; + +#endif /* CONFIG_SND_MIXER_OSS */ + +#endif /* __SOUND_MIXER_OSS_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/mpu401.h linux/include/sound/mpu401.h --- linux-2.4.21-rc1.orig/include/sound/mpu401.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/mpu401.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,116 @@ +#ifndef __SOUND_MPU401_H +#define __SOUND_MPU401_H + +/* + * Header file for MPU-401 and compatible cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "rawmidi.h" + +#define MPU401_HW_MPU401 1 /* native MPU401 */ +#define MPU401_HW_SB 2 /* SoundBlaster MPU-401 UART */ +#define MPU401_HW_ES1688 3 /* AudioDrive ES1688 MPU-401 UART */ +#define MPU401_HW_OPL3SA2 4 /* Yamaha OPL3-SA2 */ +#define MPU401_HW_SONICVIBES 5 /* S3 SonicVibes */ +#define MPU401_HW_CS4232 6 /* CS4232 */ +#define MPU401_HW_ES18XX 7 /* AudioDrive ES18XX MPU-401 UART */ +#define MPU401_HW_FM801 8 /* ForteMedia FM801 */ +#define MPU401_HW_TRID4DWAVE 9 /* Trident 4DWave */ +#define MPU401_HW_AZT2320 10 /* Aztech AZT2320 */ +#define MPU401_HW_ALS100 11 /* Avance Logic ALS100 */ +#define MPU401_HW_ICE1712 12 /* Envy24 */ +#define MPU401_HW_VIA686A 13 /* VIA 82C686A */ +#define MPU401_HW_YMFPCI 14 /* YMF DS-XG PCI */ +#define MPU401_HW_CMIPCI 15 /* CMIPCI MPU-401 UART */ +#define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */ +#define MPU401_HW_INTEL8X0 17 /* Intel8x0 driver */ +#define MPU401_HW_PC98II 18 /* Roland PC98II */ +#define MPU401_HW_AUREAL 19 /* Aureal Vortex */ + +#define MPU401_MODE_BIT_INPUT 0 +#define MPU401_MODE_BIT_OUTPUT 1 +#define MPU401_MODE_BIT_INPUT_TRIGGER 2 +#define MPU401_MODE_BIT_OUTPUT_TRIGGER 3 +#define MPU401_MODE_BIT_RX_LOOP 4 +#define MPU401_MODE_BIT_TX_LOOP 5 + +#define MPU401_MODE_INPUT (1<cport +#define MPU401D(mpu) (mpu)->port + +/* + + */ + +void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +int snd_mpu401_uart_new(snd_card_t * card, + int device, + unsigned short hardware, + unsigned long port, + int integrated, + int irq, + int irq_flags, + snd_rawmidi_t ** rrawmidi); + +#endif /* __SOUND_MPU401_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/opl3.h linux/include/sound/opl3.h --- linux-2.4.21-rc1.orig/include/sound/opl3.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/opl3.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,333 @@ +#ifndef __SOUND_OPL3_H +#define __SOUND_OPL3_H + +/* + * Definitions of the OPL-3 registers. + * + * Copyright (c) by Jaroslav Kysela , + * Hannu Savolainen 1993-1996 + * + * + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exceptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "driver.h" +#include +#include "core.h" +#include "hwdep.h" +#include "timer.h" +#include "seq_midi_emul.h" +#ifdef CONFIG_SND_SEQUENCER_OSS +#include "seq_oss.h" +#include "seq_oss_legacy.h" +#endif +#include "seq_device.h" +#include "ainstr_fm.h" + +/* + * Register numbers for the global registers + */ + +#define OPL3_REG_TEST 0x01 +#define OPL3_ENABLE_WAVE_SELECT 0x20 + +#define OPL3_REG_TIMER1 0x02 +#define OPL3_REG_TIMER2 0x03 +#define OPL3_REG_TIMER_CONTROL 0x04 /* Left side */ +#define OPL3_IRQ_RESET 0x80 +#define OPL3_TIMER1_MASK 0x40 +#define OPL3_TIMER2_MASK 0x20 +#define OPL3_TIMER1_START 0x01 +#define OPL3_TIMER2_START 0x02 + +#define OPL3_REG_CONNECTION_SELECT 0x04 /* Right side */ +#define OPL3_LEFT_4OP_0 0x01 +#define OPL3_LEFT_4OP_1 0x02 +#define OPL3_LEFT_4OP_2 0x04 +#define OPL3_RIGHT_4OP_0 0x08 +#define OPL3_RIGHT_4OP_1 0x10 +#define OPL3_RIGHT_4OP_2 0x20 + +#define OPL3_REG_MODE 0x05 /* Right side */ +#define OPL3_OPL3_ENABLE 0x01 /* OPL3 mode */ +#define OPL3_OPL4_ENABLE 0x02 /* OPL4 mode */ + +#define OPL3_REG_KBD_SPLIT 0x08 /* Left side */ +#define OPL3_COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define OPL3_KEYBOARD_SPLIT 0x40 + +#define OPL3_REG_PERCUSSION 0xbd /* Left side only */ +#define OPL3_TREMOLO_DEPTH 0x80 +#define OPL3_VIBRATO_DEPTH 0x40 +#define OPL3_PERCUSSION_ENABLE 0x20 +#define OPL3_BASSDRUM_ON 0x10 +#define OPL3_SNAREDRUM_ON 0x08 +#define OPL3_TOMTOM_ON 0x04 +#define OPL3_CYMBAL_ON 0x02 +#define OPL3_HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define OPL3_REG_AM_VIB 0x20 +#define OPL3_TREMOLO_ON 0x80 +#define OPL3_VIBRATO_ON 0x40 +#define OPL3_SUSTAIN_ON 0x20 +#define OPL3_KSR 0x10 /* Key scaling rate */ +#define OPL3_MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define OPL3_REG_KSL_LEVEL 0x40 +#define OPL3_KSL_MASK 0xc0 /* Envelope scaling bits */ +#define OPL3_TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define OPL3_REG_ATTACK_DECAY 0x60 +#define OPL3_ATTACK_MASK 0xf0 +#define OPL3_DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define OPL3_REG_SUSTAIN_RELEASE 0x80 +#define OPL3_SUSTAIN_MASK 0xf0 +#define OPL3_RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define OPL3_REG_WAVE_SELECT 0xe0 +#define OPL3_WAVE_SELECT_MASK 0x07 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define OPL3_REG_FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define OPL3_REG_KEYON_BLOCK 0xb0 +#define OPL3_KEYON_BIT 0x20 +#define OPL3_BLOCKNUM_MASK 0x1c +#define OPL3_FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halves (gives 4 ways to connect the operators). + */ +#define OPL3_REG_FEEDBACK_CONNECTION 0xc0 +#define OPL3_FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define OPL3_CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define OPL3_STEREO_BITS 0x30 /* OPL-3 only */ +#define OPL3_VOICE_TO_LEFT 0x10 +#define OPL3_VOICE_TO_RIGHT 0x20 + +/* + + */ + +#define OPL3_LEFT 0x0000 +#define OPL3_RIGHT 0x0100 + +#define OPL3_HW_AUTO 0x0000 +#define OPL3_HW_OPL2 0x0200 +#define OPL3_HW_OPL3 0x0300 +#define OPL3_HW_OPL3_SV 0x0301 /* S3 SonicVibes */ +#define OPL3_HW_OPL3_CS 0x0302 /* CS4232/CS4236+ */ +#define OPL3_HW_OPL3_FM801 0x0303 /* FM801 */ +#define OPL3_HW_OPL3_CS4281 0x0304 /* CS4281 */ +#define OPL3_HW_OPL3_PC98 0x0305 /* PC9800 */ +#define OPL3_HW_OPL4 0x0400 +#define OPL3_HW_MASK 0xff00 + +#define MAX_OPL2_VOICES 9 +#define MAX_OPL3_VOICES 18 + +typedef struct snd_opl3 opl3_t; + +/* + * A structure to keep track of each hardware voice + */ +typedef struct snd_opl3_voice { + int state; /* status */ +#define SNDRV_OPL3_ST_OFF 0 /* Not playing */ +#define SNDRV_OPL3_ST_ON_2OP 1 /* 2op voice is allocated */ +#define SNDRV_OPL3_ST_ON_4OP 2 /* 4op voice is allocated */ +#define SNDRV_OPL3_ST_NOT_AVAIL -1 /* voice is not available */ + + unsigned int time; /* An allocation time */ + unsigned char note; /* Note currently assigned to this voice */ + + unsigned long note_off; /* note-off time */ + int note_off_check; /* check note-off time */ + + unsigned char keyon_reg; /* KON register shadow */ + + snd_midi_channel_t *chan; /* Midi channel for this note */ +} snd_opl3_voice_t; + +struct snd_opl3 { + unsigned long l_port; + unsigned long r_port; + struct resource *res_l_port; + struct resource *res_r_port; + unsigned short hardware; + /* hardware access */ + void (*command) (opl3_t * opl3, unsigned short cmd, unsigned char val); + unsigned short timer_enable; + int seq_dev_num; /* sequencer device number */ + snd_timer_t *timer1; + snd_timer_t *timer2; + spinlock_t timer_lock; + + spinlock_t reg_lock; + snd_card_t *card; /* The card that this belongs to */ + int used; /* usage flag - exclusive */ + unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ + unsigned char rhythm; /* percussion mode flag */ + unsigned char max_voices; /* max number of voices */ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#define SNDRV_OPL3_MODE_SYNTH 0 /* OSS - voices allocated by application */ +#define SNDRV_OPL3_MODE_SEQ 1 /* ALSA - driver handles voice allocation */ + int synth_mode; /* synth mode */ + int seq_client; + + snd_seq_device_t *seq_dev; /* sequencer device */ + snd_midi_channel_set_t * chset; + +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_seq_device_t *oss_seq_dev; /* OSS sequencer device */ + snd_midi_channel_set_t * oss_chset; +#endif + + snd_seq_kinstr_ops_t fm_ops; + snd_seq_kinstr_list_t *ilist; + + snd_opl3_voice_t voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */ + int use_time; /* allocation counter */ + + unsigned short connection_reg; /* connection reg shadow */ + unsigned char drum_reg; /* percussion reg shadow */ + + spinlock_t voice_lock; /* Lock for voice access */ + + struct timer_list tlist; /* timer for note-offs and effects */ + int sys_timer_status; /* system timer run status */ + spinlock_t sys_timer_lock; /* Lock for system timer access */ +#endif + struct semaphore access_mutex; /* locking */ +}; + +/* opl3.c */ +void snd_opl3_interrupt(snd_hwdep_t * hw); +int snd_opl3_create(snd_card_t * card, + unsigned long l_port, unsigned long r_port, + unsigned short hardware, + int integrated, + opl3_t ** opl3); +int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev); +int snd_opl3_hwdep_new(opl3_t * opl3, int device, int seq_device, + snd_hwdep_t ** rhwdep); + +/* opl3_synth */ +int snd_opl3_open(snd_hwdep_t * hw, struct file *file); +int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg); +int snd_opl3_release(snd_hwdep_t * hw, struct file *file); + +void snd_opl3_reset(opl3_t * opl3); + +#endif /* __SOUND_OPL3_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/pcm.h linux/include/sound/pcm.h --- linux-2.4.21-rc1.orig/include/sound/pcm.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/pcm.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,908 @@ +#ifndef __SOUND_PCM_H +#define __SOUND_PCM_H + +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +typedef sndrv_pcm_uframes_t snd_pcm_uframes_t; +typedef sndrv_pcm_sframes_t snd_pcm_sframes_t; +typedef enum sndrv_pcm_class snd_pcm_class_t; +typedef enum sndrv_pcm_subclass snd_pcm_subclass_t; +typedef enum sndrv_pcm_stream snd_pcm_stream_t; +typedef enum sndrv_pcm_access snd_pcm_access_t; +typedef enum sndrv_pcm_format snd_pcm_format_t; +typedef enum sndrv_pcm_subformat snd_pcm_subformat_t; +typedef enum sndrv_pcm_state snd_pcm_state_t; +typedef union sndrv_pcm_sync_id snd_pcm_sync_id_t; +typedef struct sndrv_pcm_info snd_pcm_info_t; +typedef enum sndrv_pcm_hw_param snd_pcm_hw_param_t; +typedef struct sndrv_pcm_hw_params snd_pcm_hw_params_t; +typedef enum sndrv_pcm_start snd_pcm_start_t; +typedef enum sndrv_pcm_xrun snd_pcm_xrun_t; +typedef enum sndrv_pcm_tstamp snd_pcm_tstamp_t; +typedef struct sndrv_pcm_sw_params snd_pcm_sw_params_t; +typedef struct sndrv_pcm_channel_info snd_pcm_channel_info_t; +typedef struct sndrv_pcm_status snd_pcm_status_t; +typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t; +typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t; +typedef struct sndrv_mask snd_mask_t; +typedef struct snd_sg_buf snd_pcm_sgbuf_t; + +#define _snd_pcm_substream_chip(substream) ((substream)->private_data) +#define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) +#define _snd_pcm_chip(pcm) ((pcm)->private_data) +#define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO) + +typedef struct _snd_pcm_file snd_pcm_file_t; +typedef struct _snd_pcm_runtime snd_pcm_runtime_t; + +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#include "pcm_oss.h" +#endif + +/* + * Hardware (lowlevel) section + */ + +typedef struct _snd_pcm_hardware { + unsigned int info; /* SNDRV_PCM_INFO_* */ + u64 formats; /* SNDRV_PCM_FMTBIT_* */ + unsigned int rates; /* SNDRV_PCM_RATE_* */ + unsigned int rate_min; /* min rate */ + unsigned int rate_max; /* max rate */ + unsigned int channels_min; /* min channels */ + unsigned int channels_max; /* max channels */ + size_t buffer_bytes_max; /* max buffer size */ + size_t period_bytes_min; /* min period size */ + size_t period_bytes_max; /* max period size */ + unsigned int periods_min; /* min # of periods */ + unsigned int periods_max; /* max # of periods */ + size_t fifo_size; /* fifo size in bytes */ +} snd_pcm_hardware_t; + +typedef struct _snd_pcm_ops { + int (*open)(snd_pcm_substream_t *substream); + int (*close)(snd_pcm_substream_t *substream); + int (*ioctl)(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg); + int (*hw_params)(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params); + int (*hw_free)(snd_pcm_substream_t *substream); + int (*prepare)(snd_pcm_substream_t * substream); + int (*trigger)(snd_pcm_substream_t * substream, int cmd); + snd_pcm_uframes_t (*pointer)(snd_pcm_substream_t * substream); + int (*copy)(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, + void *buf, snd_pcm_uframes_t count); + int (*silence)(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count); + struct page *(*page)(snd_pcm_substream_t *substream, unsigned long offset); + int (*ack)(snd_pcm_substream_t *substream); +} snd_pcm_ops_t; + +/* + * + */ + +#define SNDRV_PCM_DEVICES 8 + +#define SNDRV_PCM_IOCTL1_FALSE ((void *)0) +#define SNDRV_PCM_IOCTL1_TRUE ((void *)1) + +#define SNDRV_PCM_IOCTL1_RESET 0 +#define SNDRV_PCM_IOCTL1_INFO 1 +#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 +#define SNDRV_PCM_IOCTL1_GSTATE 3 + +#define SNDRV_PCM_TRIGGER_STOP 0 +#define SNDRV_PCM_TRIGGER_START 1 +#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 3 +#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4 +#define SNDRV_PCM_TRIGGER_SUSPEND 5 +#define SNDRV_PCM_TRIGGER_RESUME 6 + +/* If you change this don't forget to change rates[] table in pcm_native.c */ +#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ +#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ +#define SNDRV_PCM_RATE_11025 (1<<2) /* 11025Hz */ +#define SNDRV_PCM_RATE_16000 (1<<3) /* 16000Hz */ +#define SNDRV_PCM_RATE_22050 (1<<4) /* 22050Hz */ +#define SNDRV_PCM_RATE_32000 (1<<5) /* 32000Hz */ +#define SNDRV_PCM_RATE_44100 (1<<6) /* 44100Hz */ +#define SNDRV_PCM_RATE_48000 (1<<7) /* 48000Hz */ +#define SNDRV_PCM_RATE_64000 (1<<8) /* 64000Hz */ +#define SNDRV_PCM_RATE_88200 (1<<9) /* 88200Hz */ +#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ +#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ +#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ + +#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ +#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ + +#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\ + SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\ + SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100) +#define SNDRV_PCM_RATE_8000_48000 (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000) +#define SNDRV_PCM_RATE_8000_96000 (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\ + SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) +#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ + SNDRV_PCM_RATE_192000) +#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) +#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) +#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) +#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) +#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) +#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) +#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) +#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) +#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) +#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) +#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) +#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) +#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) +#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) +#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) +#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) +#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) +#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) + +#ifdef SNDRV_LITTLE_ENDIAN +#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE +#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_LE +#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_LE +#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_LE +#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_LE +#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_LE +#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_LE +#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_LE +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE +#endif +#ifdef SNDRV_BIG_ENDIAN +#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_BE +#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_BE +#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_BE +#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_BE +#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_BE +#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_BE +#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_BE +#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_BE +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE +#endif + +struct _snd_pcm_file { + snd_pcm_substream_t * substream; + struct _snd_pcm_file * next; +}; + +typedef struct _snd_pcm_hw_rule snd_pcm_hw_rule_t; + +typedef int (*snd_pcm_hw_rule_func_t)(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule); + +struct _snd_pcm_hw_rule { + unsigned int cond; + snd_pcm_hw_rule_func_t func; + int var; + int deps[4]; + void *private; +}; + +typedef struct _snd_pcm_hw_constraints { + snd_mask_t masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + snd_interval_t intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + unsigned int rules_num; + unsigned int rules_all; + snd_pcm_hw_rule_t *rules; +} snd_pcm_hw_constraints_t; + +static inline snd_mask_t *constrs_mask(snd_pcm_hw_constraints_t *constrs, + snd_pcm_hw_param_t var) +{ + return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline snd_interval_t *constrs_interval(snd_pcm_hw_constraints_t *constrs, + snd_pcm_hw_param_t var) +{ + return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +typedef struct { + unsigned int num; + unsigned int den_min, den_max, den_step; +} ratnum_t; + +typedef struct { + unsigned int num_min, num_max, num_step; + unsigned int den; +} ratden_t; + +typedef struct { + int nrats; + ratnum_t *rats; +} snd_pcm_hw_constraint_ratnums_t; + +typedef struct { + int nrats; + ratden_t *rats; +} snd_pcm_hw_constraint_ratdens_t; + +typedef struct { + unsigned int count; + unsigned int *list; + unsigned int mask; +} snd_pcm_hw_constraint_list_t; + +struct _snd_pcm_runtime { + /* -- Status -- */ + snd_pcm_substream_t *trigger_master; + snd_timestamp_t trigger_tstamp; /* trigger timestamp */ + int overrange; + snd_pcm_uframes_t avail_max; + snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ + snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ + + /* -- HW params -- */ + snd_pcm_access_t access; /* access mode */ + snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ + snd_pcm_subformat_t subformat; /* subformat */ + unsigned int rate; /* rate in Hz */ + unsigned int channels; /* channels */ + snd_pcm_uframes_t period_size; /* period size */ + unsigned int periods; /* periods */ + snd_pcm_uframes_t buffer_size; /* buffer size */ + unsigned int tick_time; /* tick time */ + snd_pcm_uframes_t min_align; /* Min alignment for the format */ + size_t byte_align; + unsigned int frame_bits; + unsigned int sample_bits; + unsigned int info; + unsigned int rate_num; + unsigned int rate_den; + + /* -- SW params -- */ + int tstamp_timespec; /* use timeval (0) or timespec (1) */ + snd_pcm_tstamp_t tstamp_mode; /* mmap timestamp is updated */ + unsigned int period_step; + unsigned int sleep_min; /* min ticks to sleep */ + snd_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ + snd_pcm_uframes_t start_threshold; + snd_pcm_uframes_t stop_threshold; + snd_pcm_uframes_t silence_threshold; /* Silence filling happens when + noise is nearest than this */ + snd_pcm_uframes_t silence_size; /* Silence filling size */ + snd_pcm_uframes_t boundary; /* pointers wrap point */ + + snd_pcm_uframes_t silenced_start; + snd_pcm_uframes_t silenced_size; + + snd_pcm_sync_id_t sync; /* hardware synchronization ID */ + + /* -- mmap -- */ + volatile snd_pcm_mmap_status_t *status; + volatile snd_pcm_mmap_control_t *control; + atomic_t mmap_count; + + /* -- locking / scheduling -- */ + spinlock_t lock; + wait_queue_head_t sleep; + struct timer_list tick_timer; + struct fasync_struct *fasync; + + /* -- private section -- */ + void *private_data; + void (*private_free)(snd_pcm_runtime_t *runtime); + + /* -- hardware description -- */ + snd_pcm_hardware_t hw; + snd_pcm_hw_constraints_t hw_constraints; + + /* -- interrupt callbacks -- */ + void (*transfer_ack_begin)(snd_pcm_substream_t *substream); + void (*transfer_ack_end)(snd_pcm_substream_t *substream); + + /* -- timer -- */ + unsigned int timer_resolution; /* timer resolution */ + + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area */ + dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ + size_t dma_bytes; /* size of DMA area */ + void *dma_private; /* private DMA data for the memory allocator */ + +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + /* -- OSS things -- */ + snd_pcm_oss_runtime_t oss; +#endif +}; + +struct _snd_pcm_substream { + snd_pcm_t *pcm; + snd_pcm_str_t *pstr; + void *private_data; /* copied from pcm->private_data */ + int number; + char name[32]; /* substream name */ + int stream; /* stream (direction) */ + size_t buffer_bytes_max; /* limit ring buffer size */ + struct snd_dma_device dma_device; + struct snd_dma_buffer dma_buffer; + size_t dma_max; + /* -- hardware operations -- */ + unsigned int open_flag: 1; /* lowlevel device has been opened */ + snd_pcm_ops_t *ops; + /* -- runtime information -- */ + snd_pcm_runtime_t *runtime; + /* -- timer section -- */ + snd_timer_t *timer; /* timer */ + int timer_running: 1; /* time is running */ + spinlock_t timer_lock; + /* -- next substream -- */ + snd_pcm_substream_t *next; + /* -- linked substreams -- */ + snd_pcm_substream_t *link_next; + snd_pcm_substream_t *link_prev; + snd_pcm_file_t *file; + struct file *ffile; +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + /* -- OSS things -- */ + snd_pcm_oss_substream_t oss; +#endif + snd_info_entry_t *proc_root; + snd_info_entry_t *proc_info_entry; + snd_info_entry_t *proc_hw_params_entry; + snd_info_entry_t *proc_sw_params_entry; + snd_info_entry_t *proc_status_entry; + snd_info_entry_t *proc_prealloc_entry; +}; + +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL)) +#else +#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL) +#endif + + +struct _snd_pcm_str { + int stream; /* stream (direction) */ + snd_pcm_t *pcm; + /* -- substreams -- */ + unsigned int substream_count; + unsigned int substream_opened; + snd_pcm_substream_t *substream; +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + /* -- OSS things -- */ + snd_pcm_oss_stream_t oss; +#endif + snd_pcm_file_t *files; + snd_minor_t *reg; + snd_info_entry_t *proc_root; + snd_info_entry_t *proc_info_entry; +}; + +struct _snd_pcm { + snd_card_t *card; + unsigned int device; /* device number */ + unsigned int info_flags; + unsigned short dev_class; + unsigned short dev_subclass; + char id[64]; + char name[80]; + snd_pcm_str_t streams[2]; + struct semaphore open_mutex; + wait_queue_head_t open_wait; + void *private_data; + void (*private_free) (snd_pcm_t *pcm); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + snd_pcm_oss_t oss; +#endif +}; + +typedef struct _snd_pcm_notify { + int (*n_register) (snd_pcm_t * pcm); + int (*n_disconnect) (snd_pcm_t * pcm); + int (*n_unregister) (snd_pcm_t * pcm); + struct list_head list; +} snd_pcm_notify_t; + +/* + * Registering + */ + +extern snd_pcm_t *snd_pcm_devices[]; +extern snd_minor_t snd_pcm_reg[2]; + +void snd_pcm_lock(int unlock); + +int snd_pcm_new(snd_card_t * card, char *id, int device, + int playback_count, int capture_count, + snd_pcm_t **rpcm); +int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count); + +int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree); + +/* + * Native I/O + */ + +int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info); +int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t *info); +int snd_pcm_status(snd_pcm_substream_t * substream, snd_pcm_status_t *status); +int snd_pcm_prepare(snd_pcm_substream_t *substream); +int snd_pcm_start(snd_pcm_substream_t *substream); +int snd_pcm_stop(snd_pcm_substream_t *substream, int status); +#ifdef CONFIG_PM +int snd_pcm_suspend(snd_pcm_substream_t *substream); +int snd_pcm_suspend_all(snd_pcm_t *pcm); +#endif +int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_open(struct inode *inode, struct file *file); +int snd_pcm_release(struct inode *inode, struct file *file); +unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait); +unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait); +int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, snd_pcm_substream_t **rsubstream); +void snd_pcm_release_substream(snd_pcm_substream_t *substream); +void snd_pcm_vma_notify_data(void *client, void *data); +int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, struct vm_area_struct *area); + +#if BITS_PER_LONG >= 64 + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + *rem = *n % div; + *n /= div; +} + +#elif defined(i386) + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + u_int32_t low, high; + low = *n & 0xffffffff; + high = *n >> 32; + if (high) { + u_int32_t high1 = high % div; + high /= div; + asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1)); + *n = (u_int64_t)high << 32 | low; + } else { + *n = low / div; + *rem = low % div; + } +} +#else + +static inline void divl(u_int32_t high, u_int32_t low, + u_int32_t div, + u_int32_t *q, u_int32_t *r) +{ + u_int64_t n = (u_int64_t)high << 32 | low; + u_int64_t d = (u_int64_t)div << 31; + u_int32_t q1 = 0; + int c = 32; + while (n > 0xffffffffU) { + q1 <<= 1; + if (n >= d) { + n -= d; + q1 |= 1; + } + d >>= 1; + c--; + } + q1 <<= c; + if (n) { + low = n; + *q = q1 | (low / div); + *r = low % div; + } else { + *r = 0; + *q = q1; + } + return; +} + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + u_int32_t low, high; + low = *n & 0xffffffff; + high = *n >> 32; + if (high) { + u_int32_t high1 = high % div; + u_int32_t low1 = low; + high /= div; + divl(high1, low1, div, &low, rem); + *n = (u_int64_t)high << 32 | low; + } else { + *n = low / div; + *rem = low % div; + } +} +#endif + +/* + * PCM library + */ + +static inline int snd_pcm_running(snd_pcm_substream_t *substream) +{ + return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING || + (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); +} + +static inline ssize_t bytes_to_samples(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * 8 / runtime->sample_bits; +} + +static inline snd_pcm_sframes_t bytes_to_frames(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * 8 / runtime->frame_bits; +} + +static inline ssize_t samples_to_bytes(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * runtime->sample_bits / 8; +} + +static inline ssize_t frames_to_bytes(snd_pcm_runtime_t *runtime, snd_pcm_sframes_t size) +{ + return size * runtime->frame_bits / 8; +} + +static inline int frame_aligned(snd_pcm_runtime_t *runtime, ssize_t bytes) +{ + return bytes % runtime->byte_align == 0; +} + +static inline size_t snd_pcm_lib_buffer_bytes(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return frames_to_bytes(runtime, runtime->buffer_size); +} + +static inline size_t snd_pcm_lib_period_bytes(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return frames_to_bytes(runtime, runtime->period_size); +} + +/* + * result is: 0 ... (boundary - 1) + */ +static inline snd_pcm_uframes_t snd_pcm_playback_avail(snd_pcm_runtime_t *runtime) +{ + snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr; + if (avail < 0) + avail += runtime->boundary; + else if ((snd_pcm_uframes_t) avail >= runtime->boundary) + avail -= runtime->boundary; + return avail; +} + +/* + * result is: 0 ... (boundary - 1) + */ +static inline snd_pcm_uframes_t snd_pcm_capture_avail(snd_pcm_runtime_t *runtime) +{ + snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr; + if (avail < 0) + avail += runtime->boundary; + return avail; +} + +static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(snd_pcm_runtime_t *runtime) +{ + return runtime->buffer_size - snd_pcm_playback_avail(runtime); +} + +static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(snd_pcm_runtime_t *runtime) +{ + return runtime->buffer_size - snd_pcm_capture_avail(runtime); +} + +static inline void snd_pcm_trigger_done(snd_pcm_substream_t *substream, + snd_pcm_substream_t *master) +{ + substream->runtime->trigger_master = master; +} + +static inline int hw_is_mask(int var) +{ + return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && + var <= SNDRV_PCM_HW_PARAM_LAST_MASK; +} + +static inline int hw_is_interval(int var) +{ + return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && + var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; +} + +static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +static inline const snd_mask_t *hw_param_mask_c(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (const snd_mask_t *)hw_param_mask((snd_pcm_hw_params_t*) params, var); +} + +static inline const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var); +} + +#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) +#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) +#define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) +#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min +#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min +#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min +#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8) +#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min +#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min +#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min +#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min + + +int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v); +void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c); +void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c); +void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, + unsigned int k, snd_interval_t *c); +void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, + const snd_interval_t *b, snd_interval_t *c); +int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask); +int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step); +int snd_interval_ratnum(snd_interval_t *i, + unsigned int rats_count, ratnum_t *rats, + unsigned int *nump, unsigned int *denp); +int snd_interval_ratden(snd_interval_t *i, + unsigned int rats_count, ratden_t *rats, + unsigned int *nump, unsigned int *denp); + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params); +void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var); +int snd_pcm_hw_param_min(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_max(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_setinteger(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var); +int snd_pcm_hw_param_first(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_last(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_set(snd_pcm_substream_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int dir); +int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); + +int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); + +int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream); +int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream); + +int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + u_int32_t mask); +int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + u_int64_t mask); +int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max); +int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var); +int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_list_t *l); +int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratnums_t *r); +int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratdens_t *r); +int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits); +int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step); +int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var); +int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, + unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...); + +int snd_pcm_format_signed(snd_pcm_format_t format); +int snd_pcm_format_unsigned(snd_pcm_format_t format); +int snd_pcm_format_linear(snd_pcm_format_t format); +int snd_pcm_format_little_endian(snd_pcm_format_t format); +int snd_pcm_format_big_endian(snd_pcm_format_t format); +int snd_pcm_format_width(snd_pcm_format_t format); /* in bits */ +int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ +u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format); +int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); +snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian); +ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); +const char *snd_pcm_format_name(snd_pcm_format_t format); +const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat); + +void snd_pcm_set_ops(snd_pcm_t * pcm, int direction, snd_pcm_ops_t *ops); +void snd_pcm_set_sync(snd_pcm_substream_t * substream); +int snd_pcm_lib_interleave_len(snd_pcm_substream_t *substream); +int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); +int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream); +int snd_pcm_playback_xrun_check(snd_pcm_substream_t *substream); +int snd_pcm_capture_xrun_check(snd_pcm_substream_t *substream); +int snd_pcm_playback_xrun_asap(snd_pcm_substream_t *substream); +int snd_pcm_capture_xrun_asap(snd_pcm_substream_t *substream); +void snd_pcm_playback_silence(snd_pcm_substream_t *substream, snd_pcm_uframes_t new_hw_ptr); +int snd_pcm_playback_ready(snd_pcm_substream_t *substream); +int snd_pcm_capture_ready(snd_pcm_substream_t *substream); +long snd_pcm_playback_ready_jiffies(snd_pcm_substream_t *substream); +long snd_pcm_capture_ready_jiffies(snd_pcm_substream_t *substream); +int snd_pcm_playback_data(snd_pcm_substream_t *substream); +int snd_pcm_playback_empty(snd_pcm_substream_t *substream); +int snd_pcm_capture_empty(snd_pcm_substream_t *substream); +void snd_pcm_tick_prepare(snd_pcm_substream_t *substream); +void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks); +void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream); +void snd_pcm_period_elapsed(snd_pcm_substream_t *substream); +snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, + const void *buf, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, + void *buf, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, + void **bufs, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, + void **bufs, snd_pcm_uframes_t frames); + +/* + * Timer interface + */ + +void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream); +void snd_pcm_timer_init(snd_pcm_substream_t * substream); +void snd_pcm_timer_done(snd_pcm_substream_t * substream); + +/* + * Memory + */ + +int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream); +int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm); +int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, + size_t size, size_t max, + unsigned int flags); +int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max, + unsigned int flags); +int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); +int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream); + +#ifdef CONFIG_ISA +int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max); +#endif +#ifdef CONFIG_PCI +int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, + size_t max); +int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, size_t max); +#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private) +#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size) +#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs) +struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); +#endif + +#ifdef CONFIG_SBUS +int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, + snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, + snd_pcm_t *pcm, + size_t size, + size_t max); +#endif + +static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) +{ + *max = dma < 4 ? 64 * 1024 : 128 * 1024; +} + +/* + * Misc + */ + +#define SNDRV_PCM_DEFAULT_CON_SPDIF (IEC958_AES0_CON_EMPHASIS_NONE|\ + (IEC958_AES1_CON_ORIGINAL<<8)|\ + (IEC958_AES1_CON_PCM_CODER<<8)|\ + (IEC958_AES3_CON_FS_48000<<24)) + +#endif /* __SOUND_PCM_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/pcm_oss.h linux/include/sound/pcm_oss.h --- linux-2.4.21-rc1.orig/include/sound/pcm_oss.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/pcm_oss.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,83 @@ +#ifndef __SOUND_PCM_OSS_H +#define __SOUND_PCM_OSS_H + +/* + * Digital Audio (PCM) - OSS compatibility abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +typedef struct _snd_pcm_plugin snd_pcm_plugin_t; +typedef struct _snd_pcm_oss_setup snd_pcm_oss_setup_t; + +struct _snd_pcm_oss_setup { + char *task_name; + unsigned int disable:1, + direct:1, + block:1, + nonblock:1; + unsigned int periods; + unsigned int period_size; + snd_pcm_oss_setup_t *next; +}; + +typedef struct _snd_pcm_oss_runtime { + int params: 1, /* format/parameter change */ + prepare: 1, /* need to prepare the operation */ + trigger: 1, /* trigger flag */ + sync_trigger: 1; /* sync trigger flag */ + int rate; /* requested rate */ + int format; /* requested OSS format */ + unsigned int channels; /* requested channels */ + unsigned int fragshift; + unsigned int maxfrags; + unsigned int subdivision; /* requested subdivision */ + size_t period_bytes; /* requested period size */ + unsigned int periods; + size_t buffer_bytes; /* requested period size */ + size_t bytes; /* total # bytes processed */ + size_t mmap_bytes; + char *buffer; /* vmallocated period */ + size_t buffer_used; /* used length from buffer */ + snd_pcm_plugin_t *plugin_first; + snd_pcm_plugin_t *plugin_last; + unsigned int prev_hw_ptr_interrupt; +} snd_pcm_oss_runtime_t; + +typedef struct _snd_pcm_oss_file { + snd_pcm_substream_t *streams[2]; +} snd_pcm_oss_file_t; + +typedef struct _snd_pcm_oss_substream { + int oss: 1; /* oss mode */ + snd_pcm_oss_setup_t *setup; /* active setup */ + snd_pcm_oss_file_t *file; +} snd_pcm_oss_substream_t; + +typedef struct _snd_pcm_oss_stream { + snd_pcm_oss_setup_t *setup_list; /* setup list */ + struct semaphore setup_mutex; + snd_info_entry_t *proc_entry; +} snd_pcm_oss_stream_t; + +typedef struct _snd_pcm_oss { + int reg; + unsigned int reg_mask; +} snd_pcm_oss_t; + +#endif /* __SOUND_PCM_OSS_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/pcm_params.h linux/include/sound/pcm_params.h --- linux-2.4.21-rc1.orig/include/sound/pcm_params.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/pcm_params.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,366 @@ +#ifndef __SOUND_PCM_PARAMS_H +#define __SOUND_PCM_PARAMS_H + +/* + * PCM params helpers + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val); +extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +extern unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +extern int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir); +extern int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var); +extern int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir); + +/* To share the same code we have alsa-lib */ +#define INLINE static inline +#define assert(a) (void)(a) + +#define SNDRV_MASK_BITS 64 /* we use so far 64bits only */ +#define SNDRV_MASK_SIZE (SNDRV_MASK_BITS / 32) +#define MASK_OFS(i) ((i) >> 5) +#define MASK_BIT(i) (1U << ((i) & 31)) + +INLINE unsigned int ld2(u_int32_t v) +{ + unsigned r = 0; + + if (v >= 0x10000) { + v >>= 16; + r += 16; + } + if (v >= 0x100) { + v >>= 8; + r += 8; + } + if (v >= 0x10) { + v >>= 4; + r += 4; + } + if (v >= 4) { + v >>= 2; + r += 2; + } + if (v >= 2) + r++; + return r; +} + +INLINE size_t snd_mask_sizeof(void) +{ + return sizeof(snd_mask_t); +} + +INLINE void snd_mask_none(snd_mask_t *mask) +{ + memset(mask, 0, sizeof(*mask)); +} + +INLINE void snd_mask_any(snd_mask_t *mask) +{ + memset(mask, 0xff, SNDRV_MASK_SIZE * sizeof(u_int32_t)); +} + +INLINE int snd_mask_empty(const snd_mask_t *mask) +{ + int i; + for (i = 0; i < SNDRV_MASK_SIZE; i++) + if (mask->bits[i]) + return 0; + return 1; +} + +INLINE unsigned int snd_mask_min(const snd_mask_t *mask) +{ + int i; + assert(!snd_mask_empty(mask)); + for (i = 0; i < SNDRV_MASK_SIZE; i++) { + if (mask->bits[i]) + return ffs(mask->bits[i]) - 1 + (i << 5); + } + return 0; +} + +INLINE unsigned int snd_mask_max(const snd_mask_t *mask) +{ + int i; + assert(!snd_mask_empty(mask)); + for (i = SNDRV_MASK_SIZE - 1; i >= 0; i--) { + if (mask->bits[i]) + return ld2(mask->bits[i]) + (i << 5); + } + return 0; +} + +INLINE void snd_mask_set(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SNDRV_MASK_BITS); + mask->bits[MASK_OFS(val)] |= MASK_BIT(val); +} + +INLINE void snd_mask_reset(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SNDRV_MASK_BITS); + mask->bits[MASK_OFS(val)] &= ~MASK_BIT(val); +} + +INLINE void snd_mask_set_range(snd_mask_t *mask, unsigned int from, unsigned int to) +{ + unsigned int i; + assert(to <= SNDRV_MASK_BITS && from <= to); + for (i = from; i <= to; i++) + mask->bits[MASK_OFS(i)] |= MASK_BIT(i); +} + +INLINE void snd_mask_reset_range(snd_mask_t *mask, unsigned int from, unsigned int to) +{ + unsigned int i; + assert(to <= SNDRV_MASK_BITS && from <= to); + for (i = from; i <= to; i++) + mask->bits[MASK_OFS(i)] &= ~MASK_BIT(i); +} + +INLINE void snd_mask_leave(snd_mask_t *mask, unsigned int val) +{ + unsigned int v; + assert(val <= SNDRV_MASK_BITS); + v = mask->bits[MASK_OFS(val)] & MASK_BIT(val); + snd_mask_none(mask); + mask->bits[MASK_OFS(val)] = v; +} + +INLINE void snd_mask_intersect(snd_mask_t *mask, const snd_mask_t *v) +{ + int i; + for (i = 0; i < SNDRV_MASK_SIZE; i++) + mask->bits[i] &= v->bits[i]; +} + +INLINE int snd_mask_eq(const snd_mask_t *mask, const snd_mask_t *v) +{ + return ! memcmp(mask, v, SNDRV_MASK_SIZE * sizeof(u_int32_t)); +} + +INLINE void snd_mask_copy(snd_mask_t *mask, const snd_mask_t *v) +{ + *mask = *v; +} + +INLINE int snd_mask_test(const snd_mask_t *mask, unsigned int val) +{ + assert(val <= SNDRV_MASK_BITS); + return mask->bits[MASK_OFS(val)] & MASK_BIT(val); +} + +INLINE int snd_mask_single(const snd_mask_t *mask) +{ + int i, c = 0; + assert(!snd_mask_empty(mask)); + for (i = 0; i < SNDRV_MASK_SIZE; i++) { + if (! mask->bits[i]) + continue; + if (mask->bits[i] & (mask->bits[i] - 1)) + return 0; + if (c) + return 0; + c++; + } + return 1; +} + +INLINE int snd_mask_refine(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_t old; + assert(!snd_mask_empty(mask)); + snd_mask_copy(&old, mask); + snd_mask_intersect(mask, v); + if (snd_mask_empty(mask)) + return -EINVAL; + return !snd_mask_eq(mask, &old); +} + +INLINE int snd_mask_refine_first(snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_single(mask)) + return 0; + snd_mask_leave(mask, snd_mask_min(mask)); + return 1; +} + +INLINE int snd_mask_refine_last(snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_single(mask)) + return 0; + snd_mask_leave(mask, snd_mask_max(mask)); + return 1; +} + +INLINE int snd_mask_refine_min(snd_mask_t *mask, unsigned int val) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_min(mask) >= val) + return 0; + snd_mask_reset_range(mask, 0, val - 1); + if (snd_mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int snd_mask_refine_max(snd_mask_t *mask, unsigned int val) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_max(mask) <= val) + return 0; + snd_mask_reset_range(mask, val + 1, SNDRV_MASK_BITS); + if (snd_mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int snd_mask_refine_set(snd_mask_t *mask, unsigned int val) +{ + int changed; + assert(!snd_mask_empty(mask)); + changed = !snd_mask_single(mask); + snd_mask_leave(mask, val); + if (snd_mask_empty(mask)) + return -EINVAL; + return changed; +} + +INLINE int snd_mask_value(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return snd_mask_min(mask); +} + +INLINE void snd_interval_any(snd_interval_t *i) +{ + i->min = 0; + i->openmin = 0; + i->max = UINT_MAX; + i->openmax = 0; + i->integer = 0; + i->empty = 0; +} + +INLINE void snd_interval_none(snd_interval_t *i) +{ + i->empty = 1; +} + +INLINE int snd_interval_checkempty(const snd_interval_t *i) +{ + return (i->min > i->max || + (i->min == i->max && (i->openmin || i->openmax))); +} + +INLINE int snd_interval_empty(const snd_interval_t *i) +{ + return i->empty; +} + +INLINE int snd_interval_single(const snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + return (i->min == i->max || + (i->min + 1 == i->max && i->openmax)); +} + +INLINE int snd_interval_value(const snd_interval_t *i) +{ + assert(snd_interval_single(i)); + return i->min; +} + +INLINE int snd_interval_min(const snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + return i->min; +} + +INLINE int snd_interval_max(const snd_interval_t *i) +{ + unsigned int v; + assert(!snd_interval_empty(i)); + v = i->max; + if (i->openmax) + v--; + return v; +} + +INLINE int snd_interval_test(const snd_interval_t *i, unsigned int val) +{ + return !((i->min > val || (i->min == val && i->openmin) || + i->max < val || (i->max == val && i->openmax))); +} + +INLINE void snd_interval_copy(snd_interval_t *d, const snd_interval_t *s) +{ + *d = *s; +} + +INLINE int snd_interval_setinteger(snd_interval_t *i) +{ + if (i->integer) + return 0; + if (i->openmin && i->openmax && i->min == i->max) + return -EINVAL; + i->integer = 1; + return 1; +} + +INLINE int snd_interval_eq(const snd_interval_t *i1, const snd_interval_t *i2) +{ + if (i1->empty) + return i2->empty; + if (i2->empty) + return i1->empty; + return i1->min == i2->min && i1->openmin == i2->openmin && + i1->max == i2->max && i1->openmax == i2->openmax; +} + +static inline unsigned int add(unsigned int a, unsigned int b) +{ + if (a >= UINT_MAX - b) + return UINT_MAX; + return a + b; +} + +static inline unsigned int sub(unsigned int a, unsigned int b) +{ + if (a > b) + return a - b; + return 0; +} + +#undef INLINE +#undef assert + +#endif /* __SOUND_PCM_PARAMS_H */ + diff -urN linux-2.4.21-rc1.orig/include/sound/rawmidi.h linux/include/sound/rawmidi.h --- linux-2.4.21-rc1.orig/include/sound/rawmidi.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/rawmidi.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,184 @@ +#ifndef __SOUND_RAWMIDI_H +#define __SOUND_RAWMIDI_H + +/* + * Abstract layer for MIDI v1.0 stream + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#include "seq_device.h" +#endif + +/* + * Raw MIDI interface + */ + +typedef enum sndrv_rawmidi_stream snd_rawmidi_stream_t; +typedef struct sndrv_rawmidi_info snd_rawmidi_info_t; +typedef struct sndrv_rawmidi_params snd_rawmidi_params_t; +typedef struct sndrv_rawmidi_status snd_rawmidi_status_t; + +#define SNDRV_RAWMIDI_DEVICES 8 + +#define SNDRV_RAWMIDI_LFLG_OUTPUT (1<<0) +#define SNDRV_RAWMIDI_LFLG_INPUT (1<<1) +#define SNDRV_RAWMIDI_LFLG_OPEN (3<<0) +#define SNDRV_RAWMIDI_LFLG_APPEND (1<<2) + +typedef struct _snd_rawmidi_runtime snd_rawmidi_runtime_t; +typedef struct _snd_rawmidi_substream snd_rawmidi_substream_t; +typedef struct _snd_rawmidi_str snd_rawmidi_str_t; + +typedef struct _snd_rawmidi_ops { + int (*open) (snd_rawmidi_substream_t * substream); + int (*close) (snd_rawmidi_substream_t * substream); + void (*trigger) (snd_rawmidi_substream_t * substream, int up); + void (*drain) (snd_rawmidi_substream_t * substream); +} snd_rawmidi_ops_t; + +typedef struct _snd_rawmidi_global_ops { + int (*dev_register) (snd_rawmidi_t * rmidi); + int (*dev_unregister) (snd_rawmidi_t * rmidi); +} snd_rawmidi_global_ops_t; + +struct _snd_rawmidi_runtime { + unsigned int trigger: 1, /* transfer is running */ + drain: 1, /* drain stage */ + oss: 1; /* OSS compatible mode */ + /* midi stream buffer */ + unsigned char *buffer; /* buffer for MIDI data */ + size_t buffer_size; /* size of buffer */ + size_t appl_ptr; /* application pointer */ + size_t hw_ptr; /* hardware pointer */ + size_t avail_min; /* min avail for wakeup */ + size_t avail; /* max used buffer for wakeup */ + size_t xruns; /* over/underruns counter */ + /* misc */ + spinlock_t lock; + wait_queue_head_t sleep; + /* event handler (room [output] or new bytes [input]) */ + void (*event)(snd_rawmidi_substream_t *substream); + /* private data */ + void *private_data; + void (*private_free)(snd_rawmidi_substream_t *substream); +}; + +struct _snd_rawmidi_substream { + struct list_head list; /* list of all substream for given stream */ + int stream; /* direction */ + int number; /* substream number */ + unsigned int opened: 1, /* open flag */ + append: 1, /* append flag (merge more streams) */ + active_sensing: 1; /* send active sensing when close */ + int use_count; /* use counter (for output) */ + size_t bytes; + snd_rawmidi_t *rmidi; + snd_rawmidi_str_t *pstr; + char name[32]; + snd_rawmidi_runtime_t *runtime; + /* hardware layer */ + snd_rawmidi_ops_t *ops; +}; + +typedef struct _snd_rawmidi_file { + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; +} snd_rawmidi_file_t; + +struct _snd_rawmidi_str { + unsigned int substream_count; + unsigned int substream_opened; + struct list_head substreams; +}; + +struct _snd_rawmidi { + snd_card_t *card; + + unsigned int device; /* device number */ + unsigned int info_flags; /* SNDRV_RAWMIDI_INFO_XXXX */ + char id[64]; + char name[80]; + +#ifdef CONFIG_SND_OSSEMUL + int ossreg; +#endif + + snd_rawmidi_global_ops_t *ops; + + snd_rawmidi_str_t streams[2]; + + void *private_data; + void (*private_free) (snd_rawmidi_t *rmidi); + + struct semaphore open_mutex; + wait_queue_head_t open_wait; + + snd_info_entry_t *dev; + snd_info_entry_t *proc_entry; + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + snd_seq_device_t *seq_dev; +#endif +}; + +/* main rawmidi functions */ + +int snd_rawmidi_new(snd_card_t * card, char *id, int device, + int output_count, int input_count, + snd_rawmidi_t ** rmidi); +void snd_rawmidi_set_ops(snd_rawmidi_t * rmidi, int stream, snd_rawmidi_ops_t * ops); + +/* control functions */ + +int snd_rawmidi_control_ioctl(snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, + unsigned long arg); + +/* callbacks */ + +void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream); +int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count); +void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream); +int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream); +int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); +int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count); +int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); + +/* main midi functions */ + +int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info); +int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, int mode, snd_rawmidi_file_t * rfile); +int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile); +int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params); +int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params); +int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream); +int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream); +int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream); +long snd_rawmidi_kernel_read(snd_rawmidi_substream_t * substream, unsigned char *buf, long count); +long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count); + +#endif /* __SOUND_RAWMIDI_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/sb.h linux/include/sound/sb.h --- linux-2.4.21-rc1.orig/include/sound/sb.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/sb.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,362 @@ +#ifndef __SOUND_SB_H +#define __SOUND_SB_H + +/* + * Header file for SoundBlaster cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include + +enum sb_hw_type { + SB_HW_AUTO, + SB_HW_10, + SB_HW_20, + SB_HW_201, + SB_HW_PRO, + SB_HW_16, + SB_HW_16CSP, /* SB16 with CSP chip */ + SB_HW_ALS100, /* Avance Logic ALS100 chip */ + SB_HW_ALS4000, /* Avance Logic ALS4000 chip */ + SB_HW_DT019X, /* Diamond Tech. DT-019X / Avance Logic ALS-007 */ +}; + +#define SB_OPEN_PCM 0x01 +#define SB_OPEN_MIDI_INPUT 0x02 +#define SB_OPEN_MIDI_OUTPUT 0x04 +#define SB_OPEN_MIDI_TRIGGER 0x08 + +#define SB_MODE_HALT 0x00 +#define SB_MODE_PLAYBACK_8 0x01 +#define SB_MODE_PLAYBACK_16 0x02 +#define SB_MODE_PLAYBACK (SB_MODE_PLAYBACK_8 | SB_MODE_PLAYBACK_16) +#define SB_MODE_CAPTURE_8 0x04 +#define SB_MODE_CAPTURE_16 0x08 +#define SB_MODE_CAPTURE (SB_MODE_CAPTURE_8 | SB_MODE_CAPTURE_16) + +#define SB_RATE_LOCK_PLAYBACK 0x10 +#define SB_RATE_LOCK_CAPTURE 0x20 +#define SB_RATE_LOCK (SB_RATE_LOCK_PLAYBACK | SB_RATE_LOCK_CAPTURE) + +#define SB_MPU_INPUT 1 + +struct _snd_sb { + unsigned long port; /* base port of DSP chip */ + struct resource *res_port; + unsigned long alt_port; /* alternate port (ALS4000) */ + struct resource *res_alt_port; + unsigned long mpu_port; /* MPU port for SB DSP 4.0+ */ + int irq; /* IRQ number of DSP chip */ + int dma8; /* 8-bit DMA */ + int dma16; /* 16-bit DMA */ + unsigned short version; /* version of DSP chip */ + enum sb_hw_type hardware; /* see to SB_HW_XXXX */ + + struct pci_dev *pci; /* ALS4000 */ + + unsigned int open; /* see to SB_OPEN_XXXX for sb8 */ + /* also SNDRV_SB_CSP_MODE_XXX for sb16_csp */ + unsigned int mode; /* current mode of stream */ + unsigned int force_mode16; /* force 16-bit mode of streams */ + unsigned int locked_rate; /* sb16 duplex */ + unsigned int playback_format; + unsigned int capture_format; + struct timer_list midi_timer; + unsigned int p_dma_size; + unsigned int p_period_size; + unsigned int c_dma_size; + unsigned int c_period_size; + + spinlock_t mixer_lock; + + char name[32]; + + void *csp; /* used only when CONFIG_SND_SB16_CSP is set */ + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_substream_input; + snd_rawmidi_substream_t *midi_substream_output; + void (*rmidi_callback)(int irq, void *dev_id, struct pt_regs *regs); + + spinlock_t reg_lock; + spinlock_t open_lock; + spinlock_t midi_input_lock; + + snd_info_entry_t *proc_entry; +}; + +typedef struct _snd_sb sb_t; + +/* I/O ports */ + +#define SBP(chip, x) ((chip)->port + s_b_SB_##x) +#define SBP1(port, x) ((port) + s_b_SB_##x) + +#define s_b_SB_RESET 0x6 +#define s_b_SB_READ 0xa +#define s_b_SB_WRITE 0xc +#define s_b_SB_COMMAND 0xc +#define s_b_SB_STATUS 0xc +#define s_b_SB_DATA_AVAIL 0xe +#define s_b_SB_DATA_AVAIL_16 0xf +#define s_b_SB_MIXER_ADDR 0x4 +#define s_b_SB_MIXER_DATA 0x5 +#define s_b_SB_OPL3_LEFT 0x0 +#define s_b_SB_OPL3_RIGHT 0x2 +#define s_b_SB_OPL3_BOTH 0x8 + +#define SB_DSP_OUTPUT 0x14 +#define SB_DSP_INPUT 0x24 +#define SB_DSP_BLOCK_SIZE 0x48 +#define SB_DSP_HI_OUTPUT 0x91 +#define SB_DSP_HI_INPUT 0x99 +#define SB_DSP_LO_OUTPUT_AUTO 0x1c +#define SB_DSP_LO_INPUT_AUTO 0x2c +#define SB_DSP_HI_OUTPUT_AUTO 0x90 +#define SB_DSP_HI_INPUT_AUTO 0x98 +#define SB_DSP_IMMED_INT 0xf2 +#define SB_DSP_GET_VERSION 0xe1 +#define SB_DSP_SPEAKER_ON 0xd1 +#define SB_DSP_SPEAKER_OFF 0xd3 +#define SB_DSP_DMA8_OFF 0xd0 +#define SB_DSP_DMA8_ON 0xd4 +#define SB_DSP_DMA8_EXIT 0xda +#define SB_DSP_DMA16_OFF 0xd5 +#define SB_DSP_DMA16_ON 0xd6 +#define SB_DSP_DMA16_EXIT 0xd9 +#define SB_DSP_SAMPLE_RATE 0x40 +#define SB_DSP_SAMPLE_RATE_OUT 0x41 +#define SB_DSP_SAMPLE_RATE_IN 0x42 +#define SB_DSP_MONO_8BIT 0xa0 +#define SB_DSP_MONO_16BIT 0xa4 +#define SB_DSP_STEREO_8BIT 0xa8 +#define SB_DSP_STEREO_16BIT 0xac + +#define SB_DSP_MIDI_INPUT_IRQ 0x31 +#define SB_DSP_MIDI_OUTPUT 0x38 + +#define SB_DSP4_OUT8_AI 0xc6 +#define SB_DSP4_IN8_AI 0xce +#define SB_DSP4_OUT16_AI 0xb6 +#define SB_DSP4_IN16_AI 0xbe +#define SB_DSP4_MODE_UNS_MONO 0x00 +#define SB_DSP4_MODE_SIGN_MONO 0x10 +#define SB_DSP4_MODE_UNS_STEREO 0x20 +#define SB_DSP4_MODE_SIGN_STEREO 0x30 + +#define SB_DSP4_OUTPUT 0x3c +#define SB_DSP4_INPUT_LEFT 0x3d +#define SB_DSP4_INPUT_RIGHT 0x3e + +/* registers for SB 2.0 mixer */ +#define SB_DSP20_MASTER_DEV 0x02 +#define SB_DSP20_PCM_DEV 0x0A +#define SB_DSP20_CD_DEV 0x08 +#define SB_DSP20_FM_DEV 0x06 + +/* registers for SB PRO mixer */ +#define SB_DSP_MASTER_DEV 0x22 +#define SB_DSP_PCM_DEV 0x04 +#define SB_DSP_LINE_DEV 0x2e +#define SB_DSP_CD_DEV 0x28 +#define SB_DSP_FM_DEV 0x26 +#define SB_DSP_MIC_DEV 0x0a +#define SB_DSP_CAPTURE_SOURCE 0x0c +#define SB_DSP_CAPTURE_FILT 0x0c +#define SB_DSP_PLAYBACK_FILT 0x0e +#define SB_DSP_STEREO_SW 0x0e + +#define SB_DSP_MIXS_MIC0 0x00 /* same as MIC */ +#define SB_DSP_MIXS_CD 0x01 +#define SB_DSP_MIXS_MIC 0x02 +#define SB_DSP_MIXS_LINE 0x03 + +/* registers (only for left channel) for SB 16 mixer */ +#define SB_DSP4_MASTER_DEV 0x30 +#define SB_DSP4_BASS_DEV 0x46 +#define SB_DSP4_TREBLE_DEV 0x44 +#define SB_DSP4_SYNTH_DEV 0x34 +#define SB_DSP4_PCM_DEV 0x32 +#define SB_DSP4_SPEAKER_DEV 0x3b +#define SB_DSP4_LINE_DEV 0x38 +#define SB_DSP4_MIC_DEV 0x3a +#define SB_DSP4_OUTPUT_SW 0x3c +#define SB_DSP4_CD_DEV 0x36 +#define SB_DSP4_IGAIN_DEV 0x3f +#define SB_DSP4_OGAIN_DEV 0x41 +#define SB_DSP4_MIC_AGC 0x43 + +/* additional registers for SB 16 mixer */ +#define SB_DSP4_IRQSETUP 0x80 +#define SB_DSP4_DMASETUP 0x81 +#define SB_DSP4_IRQSTATUS 0x82 +#define SB_DSP4_MPUSETUP 0x84 + +#define SB_DSP4_3DSE 0x90 + +/* Registers for DT-019x / ALS-007 mixer */ +#define SB_DT019X_MASTER_DEV 0x62 +#define SB_DT019X_PCM_DEV 0x64 +#define SB_DT019X_SYNTH_DEV 0x66 +#define SB_DT019X_CD_DEV 0x68 +#define SB_DT019X_MIC_DEV 0x6a +#define SB_DT019X_SPKR_DEV 0x6a +#define SB_DT019X_LINE_DEV 0x6e +#define SB_DT019X_OUTPUT_SW2 0x4c +#define SB_DT019X_CAPTURE_SW 0x6c + +#define SB_DT019X_CAP_CD 0x02 +#define SB_DT019X_CAP_MIC 0x04 +#define SB_DT019X_CAP_LINE 0x06 +#define SB_DT019X_CAP_SYNTH 0x07 +#define SB_DT019X_CAP_MAIN 0x07 + +#define SB_ALS4000_MONO_IO_CTRL 0x4b +#define SB_ALS4000_MIC_IN_GAIN 0x4d +#define SB_ALS4000_FMDAC 0x4f +#define SB_ALS4000_3D_SND_FX 0x50 +#define SB_ALS4000_3D_TIME_DELAY 0x51 +#define SB_ALS4000_3D_AUTO_MUTE 0x52 +#define SB_ALS4000_QSOUND 0xdb + +/* IRQ setting bitmap */ +#define SB_IRQSETUP_IRQ9 0x01 +#define SB_IRQSETUP_IRQ5 0x02 +#define SB_IRQSETUP_IRQ7 0x04 +#define SB_IRQSETUP_IRQ10 0x08 + +/* IRQ types */ +#define SB_IRQTYPE_8BIT 0x01 +#define SB_IRQTYPE_16BIT 0x02 +#define SB_IRQTYPE_MPUIN 0x04 + +/* DMA setting bitmap */ +#define SB_DMASETUP_DMA0 0x01 +#define SB_DMASETUP_DMA1 0x02 +#define SB_DMASETUP_DMA3 0x08 +#define SB_DMASETUP_DMA5 0x20 +#define SB_DMASETUP_DMA6 0x40 +#define SB_DMASETUP_DMA7 0x80 + +/* + * + */ + +static inline void snd_sb_ack_8bit(sb_t *chip) +{ + inb(SBP(chip, DATA_AVAIL)); +} + +static inline void snd_sb_ack_16bit(sb_t *chip) +{ + inb(SBP(chip, DATA_AVAIL_16)); +} + +/* sb_common.c */ +int snd_sbdsp_command(sb_t *chip, unsigned char val); +int snd_sbdsp_get_byte(sb_t *chip); +int snd_sbdsp_reset(sb_t *chip); +int snd_sbdsp_create(snd_card_t *card, + unsigned long port, + int irq, + void (*irq_handler)(int, void *, struct pt_regs *), + int dma8, int dma16, + unsigned short hardware, + sb_t **r_chip); +/* sb_mixer.c */ +void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data); +unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg); +int snd_sbmixer_new(sb_t *chip); + +/* sb8_init.c */ +int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +/* sb8.c */ +void snd_sb8dsp_interrupt(sb_t *chip); +int snd_sb8_playback_open(snd_pcm_substream_t *substream); +int snd_sb8_capture_open(snd_pcm_substream_t *substream); +int snd_sb8_playback_close(snd_pcm_substream_t *substream); +int snd_sb8_capture_close(snd_pcm_substream_t *substream); +/* midi8.c */ +void snd_sb8dsp_midi_interrupt(sb_t *chip); +int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi); + +/* sb16_init.c */ +int snd_sb16dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction); +int snd_sb16dsp_configure(sb_t *chip); +/* sb16.c */ +void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs); +int snd_sb16_playback_open(snd_pcm_substream_t *substream); +int snd_sb16_capture_open(snd_pcm_substream_t *substream); +int snd_sb16_playback_close(snd_pcm_substream_t *substream); +int snd_sb16_capture_close(snd_pcm_substream_t *substream); + +/* exported mixer stuffs */ +enum { + SB_MIX_SINGLE, + SB_MIX_DOUBLE, + SB_MIX_INPUT_SW, + SB_MIX_CAPTURE_PRO, + SB_MIX_CAPTURE_DT019X +}; + +#define SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) \ + ((left_reg) | ((right_reg) << 8) | ((left_shift) << 16) | ((right_shift) << 19) | ((mask) << 24)) +#define SB_MIXVAL_SINGLE(reg, shift, mask) \ + ((reg) | ((shift) << 16) | ((mask) << 24)) +#define SB_MIXVAL_INPUT_SW(reg1, reg2, left_shift, right_shift) \ + ((reg1) | ((reg2) << 8) | ((left_shift) << 16) | ((right_shift) << 24)) + +int snd_sbmixer_add_ctl(sb_t *chip, const char *name, int index, int type, unsigned long value); + +/* for ease of use */ +struct sbmix_elem { + const char *name; + int type; + unsigned long private_value; +}; + +#define SB_SINGLE(xname, reg, shift, mask) \ +{ .name = xname, \ + .type = SB_MIX_SINGLE, \ + .private_value = SB_MIXVAL_SINGLE(reg, shift, mask) } + +#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \ +{ .name = xname, \ + .type = SB_MIX_DOUBLE, \ + .private_value = SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) } + +#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \ +{ .name = xname, \ + .type = SB_MIX_INPUT_SW, \ + .private_value = SB_MIXVAL_INPUT_SW(reg1, reg2, left_shift, right_shift) } + +static inline int snd_sbmixer_add_ctl_elem(sb_t *chip, const struct sbmix_elem *c) +{ + return snd_sbmixer_add_ctl(chip, c->name, 0, c->type, c->private_value); +} + +#endif /* __SOUND_SB_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/sb16_csp.h linux/include/sound/sb16_csp.h --- linux-2.4.21-rc1.orig/include/sound/sb16_csp.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/sb16_csp.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,167 @@ +#ifndef __SOUND_SB16_CSP_H +#define __SOUND_SB16_CSP_H + +/* + * Copyright (c) 1999 by Uros Bizjak + * Takashi Iwai + * + * SB16ASP/AWE32 CSP control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* CSP modes */ +#define SNDRV_SB_CSP_MODE_NONE 0x00 +#define SNDRV_SB_CSP_MODE_DSP_READ 0x01 /* Record from DSP */ +#define SNDRV_SB_CSP_MODE_DSP_WRITE 0x02 /* Play to DSP */ +#define SNDRV_SB_CSP_MODE_QSOUND 0x04 /* QSound */ + +/* CSP load flags */ +#define SNDRV_SB_CSP_LOAD_FROMUSER 0x01 +#define SNDRV_SB_CSP_LOAD_INITBLOCK 0x02 + +/* CSP sample width */ +#define SNDRV_SB_CSP_SAMPLE_8BIT 0x01 +#define SNDRV_SB_CSP_SAMPLE_16BIT 0x02 + +/* CSP channels */ +#define SNDRV_SB_CSP_MONO 0x01 +#define SNDRV_SB_CSP_STEREO 0x02 + +/* CSP rates */ +#define SNDRV_SB_CSP_RATE_8000 0x01 +#define SNDRV_SB_CSP_RATE_11025 0x02 +#define SNDRV_SB_CSP_RATE_22050 0x04 +#define SNDRV_SB_CSP_RATE_44100 0x08 +#define SNDRV_SB_CSP_RATE_ALL 0x0f + +/* CSP running state */ +#define SNDRV_SB_CSP_ST_IDLE 0x00 +#define SNDRV_SB_CSP_ST_LOADED 0x01 +#define SNDRV_SB_CSP_ST_RUNNING 0x02 +#define SNDRV_SB_CSP_ST_PAUSED 0x04 +#define SNDRV_SB_CSP_ST_AUTO 0x08 +#define SNDRV_SB_CSP_ST_QSOUND 0x10 + +/* maximum QSound value (180 degrees right) */ +#define SNDRV_SB_CSP_QSOUND_MAX_RIGHT 0x20 + +/* maximum microcode RIFF file size */ +#define SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE 0x3000 + +/* microcode header */ +typedef struct snd_sb_csp_mc_header { + char codec_name[16]; /* id name of codec */ + unsigned short func_req; /* requested function */ +} snd_sb_csp_mc_header_t; + +/* microcode to be loaded */ +typedef struct snd_sb_csp_microcode { + snd_sb_csp_mc_header_t info; + unsigned char data[SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE]; +} snd_sb_csp_microcode_t; + +/* start CSP with sample_width in mono/stereo */ +typedef struct snd_sb_csp_start { + int sample_width; /* sample width, look above */ + int channels; /* channels, look above */ +} snd_sb_csp_start_t; + +/* CSP information */ +typedef struct snd_sb_csp_info { + char codec_name[16]; /* id name of codec */ + unsigned short func_nr; /* function number */ + unsigned int acc_format; /* accepted PCM formats */ + unsigned short acc_channels; /* accepted channels */ + unsigned short acc_width; /* accepted sample width */ + unsigned short acc_rates; /* accepted sample rates */ + unsigned short csp_mode; /* CSP mode, see above */ + unsigned short run_channels; /* current channels */ + unsigned short run_width; /* current sample width */ + unsigned short version; /* version id: 0x10 - 0x1f */ + unsigned short state; /* state bits */ +} snd_sb_csp_info_t; + +/* HWDEP controls */ +/* get CSP information */ +#define SNDRV_SB_CSP_IOCTL_INFO _IOR('H', 0x10, snd_sb_csp_info_t) +/* load microcode to CSP */ +#define SNDRV_SB_CSP_IOCTL_LOAD_CODE _IOW('H', 0x11, snd_sb_csp_microcode_t) +/* unload microcode from CSP */ +#define SNDRV_SB_CSP_IOCTL_UNLOAD_CODE _IO('H', 0x12) +/* start CSP */ +#define SNDRV_SB_CSP_IOCTL_START _IOW('H', 0x13, snd_sb_csp_start_t) +/* stop CSP */ +#define SNDRV_SB_CSP_IOCTL_STOP _IO('H', 0x14) +/* pause CSP and DMA transfer */ +#define SNDRV_SB_CSP_IOCTL_PAUSE _IO('H', 0x15) +/* restart CSP and DMA transfer */ +#define SNDRV_SB_CSP_IOCTL_RESTART _IO('H', 0x16) + +#ifdef __KERNEL__ +#include "sb.h" +#include "hwdep.h" + +typedef struct snd_sb_csp snd_sb_csp_t; + +/* + * CSP operators + */ +typedef struct { + int (*csp_use) (snd_sb_csp_t * p); + int (*csp_unuse) (snd_sb_csp_t * p); + int (*csp_autoload) (snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); + int (*csp_start) (snd_sb_csp_t * p, int sample_width, int channels); + int (*csp_stop) (snd_sb_csp_t * p); + int (*csp_qsound_transfer) (snd_sb_csp_t * p); +} snd_sb_csp_ops_t; + +/* + * CSP private data + */ +struct snd_sb_csp { + sb_t *chip; /* SB16 DSP */ + int used; /* usage flag - exclusive */ + char codec_name[16]; /* name of codec */ + unsigned short func_nr; /* function number */ + unsigned int acc_format; /* accepted PCM formats */ + int acc_channels; /* accepted channels */ + int acc_width; /* accepted sample width */ + int acc_rates; /* accepted sample rates */ + int mode; /* MODE */ + int run_channels; /* current CSP channels */ + int run_width; /* current sample width */ + int version; /* CSP version (0x10 - 0x1f) */ + int running; /* running state */ + + snd_sb_csp_ops_t ops; /* operators */ + + spinlock_t q_lock; /* locking */ + int q_enabled; /* enabled flag */ + int qpos_left; /* left position */ + int qpos_right; /* right position */ + int qpos_changed; /* position changed flag */ + + snd_kcontrol_t *qsound_switch; + snd_kcontrol_t *qsound_space; + + struct semaphore access_mutex; /* locking */ +}; + +int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep); +#endif + +#endif /* __SOUND_SB16_CSP */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_device.h linux/include/sound/seq_device.h --- linux-2.4.21-rc1.orig/include/sound/seq_device.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_device.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,88 @@ +#ifndef __SOUND_SEQ_DEVICE_H +#define __SOUND_SEQ_DEVICE_H + +/* + * ALSA sequencer device management + * Copyright (c) 1999 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +typedef struct snd_seq_device snd_seq_device_t; +typedef struct snd_seq_dev_ops snd_seq_dev_ops_t; + +/* + * registered device information + */ + +#define ID_LEN 32 + +/* status flag */ +#define SNDRV_SEQ_DEVICE_FREE 0 +#define SNDRV_SEQ_DEVICE_REGISTERED 1 + +struct snd_seq_device { + /* device info */ + snd_card_t *card; /* sound card */ + int device; /* device number */ + char id[ID_LEN]; /* driver id */ + char name[80]; /* device name */ + int argsize; /* size of the argument */ + void *driver_data; /* private data for driver */ + int status; /* flag - read only */ + void *private_data; /* private data for the caller */ + void (*private_free)(snd_seq_device_t *device); + struct list_head list; /* link to next device */ +}; + + +/* driver operators + * init_device: + * Initialize the device with given parameters. + * Typically, + * 1. call snd_hwdep_new + * 2. allocate private data and initialize it + * 3. call snd_hwdep_register + * 4. store the instance to dev->driver_data pointer. + * + * free_device: + * Release the private data. + * Typically, call snd_device_free(dev->card, dev->driver_data) + */ +struct snd_seq_dev_ops { + int (*init_device)(snd_seq_device_t *dev); + int (*free_device)(snd_seq_device_t *dev); +}; + +/* + * prototypes + */ +void snd_seq_device_load_drivers(void); +int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, snd_seq_device_t **result); +int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize); +int snd_seq_device_unregister_driver(char *id); + +#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(snd_seq_device_t)) + + +/* + * id strings for generic devices + */ +#define SNDRV_SEQ_DEV_ID_MIDISYNTH "seq-midi" +#define SNDRV_SEQ_DEV_ID_OPL3 "opl3-synth" + + +#endif /* __SOUND_SEQ_DEVICE_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_instr.h linux/include/sound/seq_instr.h --- linux-2.4.21-rc1.orig/include/sound/seq_instr.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_instr.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,112 @@ +#ifndef __SOUND_SEQ_INSTR_H +#define __SOUND_SEQ_INSTR_H + +/* + * Main kernel header file for the ALSA sequencer + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "seq_kernel.h" + +/* Instrument cluster */ +typedef struct _snd_seq_kcluster { + snd_seq_instr_cluster_t cluster; + char name[32]; + int priority; + struct _snd_seq_kcluster *next; +} snd_seq_kcluster_t; + +/* return pointer to private data */ +#define KINSTR_DATA(kinstr) (void *)(((char *)kinstr) + sizeof(snd_seq_kinstr_t)) + +typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t; + +/* Instrument structure */ +typedef struct _snd_seq_kinstr { + snd_seq_instr_t instr; + char name[32]; + int type; /* instrument type */ + int use; /* use count */ + int busy; /* not useable */ + int add_len; /* additional length */ + snd_seq_kinstr_ops_t *ops; /* operations */ + struct _snd_seq_kinstr *next; +} snd_seq_kinstr_t; + +#define SNDRV_SEQ_INSTR_HASH_SIZE 32 + +/* Instrument flags */ +#define SNDRV_SEQ_INSTR_FLG_DIRECT (1<<0) /* accept only direct events */ + +/* List of all instruments */ +typedef struct { + snd_seq_kinstr_t *hash[SNDRV_SEQ_INSTR_HASH_SIZE]; + int count; /* count of all instruments */ + + snd_seq_kcluster_t *chash[SNDRV_SEQ_INSTR_HASH_SIZE]; + int ccount; /* count of all clusters */ + + int owner; /* current owner of the instrument list */ + unsigned int flags; + + spinlock_t lock; + spinlock_t ops_lock; + struct semaphore ops_mutex; + unsigned long ops_flags; +} snd_seq_kinstr_list_t; + +#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE 0 +#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE 1 + +struct snd_seq_kinstr_ops { + void *private_data; + long add_len; /* additional length */ + char *instr_type; + int (*info)(void *private_data, char *info_data, long len); + int (*put)(void *private_data, snd_seq_kinstr_t *kinstr, + char *instr_data, long len, int atomic, int cmd); + int (*get)(void *private_data, snd_seq_kinstr_t *kinstr, + char *instr_data, long len, int atomic, int cmd); + int (*get_size)(void *private_data, snd_seq_kinstr_t *kinstr, long *size); + int (*remove)(void *private_data, snd_seq_kinstr_t *kinstr, int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *kinstr, int what); + struct snd_seq_kinstr_ops *next; +}; + + +/* instrument operations */ +snd_seq_kinstr_list_t *snd_seq_instr_list_new(void); +void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list); +int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list, + snd_seq_instr_header_t *ifree, + int client, + int atomic); +snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list, + snd_seq_instr_t *instr, + int exact, + int follow_alias); +void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list, + snd_seq_kinstr_t *instr); +int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int client, + int atomic, + int hop); + +#endif /* __SOUND_SEQ_INSTR_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_kernel.h linux/include/sound/seq_kernel.h --- linux-2.4.21-rc1.orig/include/sound/seq_kernel.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_kernel.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,180 @@ +#ifndef __SOUND_SEQ_KERNEL_H +#define __SOUND_SEQ_KERNEL_H + +/* + * Main kernel header file for the ALSA sequencer + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include "asequencer.h" + +typedef sndrv_seq_tick_time_t snd_seq_tick_time_t; +typedef sndrv_seq_position_t snd_seq_position_t; +typedef sndrv_seq_frequency_t snd_seq_frequency_t; +typedef sndrv_seq_instr_cluster_t snd_seq_instr_cluster_t; +typedef enum sndrv_seq_client_type snd_seq_client_type_t; +typedef enum sndrv_seq_stop_mode snd_seq_stop_mode_t; +typedef struct sndrv_seq_port_info snd_seq_port_info_t; +typedef struct sndrv_seq_port_subscribe snd_seq_port_subscribe_t; +typedef struct sndrv_seq_event snd_seq_event_t; +typedef struct sndrv_seq_addr snd_seq_addr_t; +typedef struct sndrv_seq_ev_volume snd_seq_ev_volume_t; +typedef struct sndrv_seq_ev_loop snd_seq_ev_loop_t; +typedef struct sndrv_seq_remove_events snd_seq_remove_events_t; +typedef struct sndrv_seq_query_subs snd_seq_query_subs_t; +typedef struct sndrv_seq_real_time snd_seq_real_time_t; +typedef struct sndrv_seq_system_info snd_seq_system_info_t; +typedef struct sndrv_seq_client_info snd_seq_client_info_t; +typedef struct sndrv_seq_queue_info snd_seq_queue_info_t; +typedef struct sndrv_seq_queue_status snd_seq_queue_status_t; +typedef struct sndrv_seq_queue_tempo snd_seq_queue_tempo_t; +typedef struct sndrv_seq_queue_owner snd_seq_queue_owner_t; +typedef struct sndrv_seq_queue_timer snd_seq_queue_timer_t; +typedef struct sndrv_seq_queue_client snd_seq_queue_client_t; +typedef struct sndrv_seq_client_pool snd_seq_client_pool_t; +typedef struct sndrv_seq_instr snd_seq_instr_t; +typedef struct sndrv_seq_instr_data snd_seq_instr_data_t; +typedef struct sndrv_seq_instr_header snd_seq_instr_header_t; +typedef union sndrv_seq_timestamp snd_seq_timestamp_t; + +#define snd_seq_event_bounce_ext_data sndrv_seq_event_bounce_ext_data +#define snd_seq_ev_is_result_type sndrv_seq_ev_is_result_type +#define snd_seq_ev_is_channel_type sndrv_seq_ev_is_channel_type +#define snd_seq_ev_is_note_type sndrv_seq_ev_is_note_type +#define snd_seq_ev_is_control_type sndrv_seq_ev_is_control_type +#define snd_seq_ev_is_queue_type sndrv_seq_ev_is_queue_type +#define snd_seq_ev_is_message_type sndrv_seq_ev_is_message_type +#define snd_seq_ev_is_sample_type sndrv_seq_ev_is_sample_type +#define snd_seq_ev_is_user_type sndrv_seq_ev_is_user_type +#define snd_seq_ev_is_fixed_type sndrv_seq_ev_is_fixed_type +#define snd_seq_ev_is_instr_type sndrv_seq_ev_is_instr_type +#define snd_seq_ev_is_variable_type sndrv_seq_ev_is_variable_type +#define snd_seq_ev_is_reserved sndrv_seq_ev_is_reserved +#define snd_seq_ev_is_direct sndrv_seq_ev_is_direct +#define snd_seq_ev_is_prior sndrv_seq_ev_is_prior +#define snd_seq_ev_length_type sndrv_seq_ev_length_type +#define snd_seq_ev_is_fixed sndrv_seq_ev_is_fixed +#define snd_seq_ev_is_variable sndrv_seq_ev_is_variable +#define snd_seq_ev_is_varusr sndrv_seq_ev_is_varusr +#define snd_seq_ev_timestamp_type sndrv_seq_ev_timestamp_type +#define snd_seq_ev_is_tick sndrv_seq_ev_is_tick +#define snd_seq_ev_is_real sndrv_seq_ev_is_real +#define snd_seq_ev_timemode_type sndrv_seq_ev_timemode_type +#define snd_seq_ev_is_abstime sndrv_seq_ev_is_abstime +#define snd_seq_ev_is_reltime sndrv_seq_ev_is_reltime +#define snd_seq_queue_sync_port sndrv_seq_queue_sync_port +#define snd_seq_queue_owner sndrv_seq_queue_owner + +/* maximum number of events dequeued per schedule interval */ +#define SNDRV_SEQ_MAX_DEQUEUE 50 + +/* maximum number of queues */ +#define SNDRV_SEQ_MAX_QUEUES 8 + +/* max number of concurrent clients */ +#define SNDRV_SEQ_MAX_CLIENTS 192 + +/* max number of concurrent ports */ +#define SNDRV_SEQ_MAX_PORTS 254 + +/* max number of events in memory pool */ +#define SNDRV_SEQ_MAX_EVENTS 2000 + +/* default number of events in memory chunk */ +#define SNDRV_SEQ_DEFAULT_CHUNK_EVENTS 64 + +/* default number of events in memory pool */ +#define SNDRV_SEQ_DEFAULT_EVENTS 500 + +/* max number of events in memory pool for one client (outqueue) */ +#define SNDRV_SEQ_MAX_CLIENT_EVENTS 2000 + +/* default number of events in memory pool for one client (outqueue) */ +#define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS 200 + +/* max delivery path length */ +#define SNDRV_SEQ_MAX_HOPS 10 + +/* max size of event size */ +#define SNDRV_SEQ_MAX_EVENT_LEN 0x3fffffff + +/* typedefs */ +struct _snd_seq_user_client; +struct _snd_seq_kernel_client; +struct _snd_seq_client; +struct _snd_seq_queue; + +typedef struct _snd_seq_user_client user_client_t; +typedef struct _snd_seq_kernel_client kernel_client_t; +typedef struct _snd_seq_client client_t; +typedef struct _snd_seq_queue queue_t; + +/* call-backs for kernel client */ + +typedef struct { + void *private_data; + int allow_input: 1, + allow_output: 1; + /*...*/ +} snd_seq_client_callback_t; + +/* call-backs for kernel port */ +typedef int (snd_seq_kernel_port_open_t)(void *private_data, snd_seq_port_subscribe_t *info); +typedef int (snd_seq_kernel_port_close_t)(void *private_data, snd_seq_port_subscribe_t *info); +typedef int (snd_seq_kernel_port_input_t)(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop); +typedef void (snd_seq_kernel_port_private_free_t)(void *private_data); + +typedef struct { + struct module *owner; + void *private_data; + snd_seq_kernel_port_open_t *subscribe; + snd_seq_kernel_port_close_t *unsubscribe; + snd_seq_kernel_port_open_t *use; + snd_seq_kernel_port_close_t *unuse; + snd_seq_kernel_port_input_t *event_input; + snd_seq_kernel_port_private_free_t *private_free; + unsigned int callback_all; /* call subscribe callbacks at each connection/disconnection */ + /*...*/ +} snd_seq_port_callback_t; + +/* interface for kernel client */ +extern int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t *callback); +extern int snd_seq_delete_kernel_client(int client); +extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); +extern int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t *ev, int atomic, int hop); +extern int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg); + +#define SNDRV_SEQ_EXT_MASK 0xc0000000 +#define SNDRV_SEQ_EXT_USRPTR 0x80000000 +#define SNDRV_SEQ_EXT_CHAINED 0x40000000 + +typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count); +int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned); +int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data); + +/* port callback routines */ +void snd_port_init_callback(snd_seq_port_callback_t *p); +snd_seq_port_callback_t *snd_port_alloc_callback(void); + +/* port attach/detach */ +int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp, + int cap, int type, int midi_channels, int midi_voices, char *portname); +int snd_seq_event_port_detach(int client, int port); + +#endif /* __SOUND_SEQ_KERNEL_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_midi_emul.h linux/include/sound/seq_midi_emul.h --- linux-2.4.21-rc1.orig/include/sound/seq_midi_emul.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_midi_emul.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,197 @@ +#ifndef __SOUND_SEQ_MIDI_EMUL_H +#define __SOUND_SEQ_MIDI_EMUL_H + +/* + * Midi channel definition for optional channel management. + * + * Copyright (C) 1999 Steve Ratcliffe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_kernel.h" + +/* + * This structure is used to keep track of the current state on each + * channel. All drivers for hardware that does not understand midi + * directly will probably need to use this structure. + */ +typedef struct snd_midi_channel { + void *private; /* A back pointer to driver data */ + int number; /* The channel number */ + int client; /* The client associated with this channel */ + int port; /* The port associated with this channel */ + + unsigned char midi_mode; /* GM, GS, XG etc */ + unsigned int + drum_channel:1, /* Drum channel */ + param_type:1 /* RPN/NRPN */ + ; + + unsigned char midi_aftertouch; /* Aftertouch (key pressure) */ + unsigned char midi_pressure; /* Channel pressure */ + unsigned char midi_program; /* Instrument number */ + short midi_pitchbend; /* Pitch bend amount */ + + unsigned char control[128]; /* Current value of all controls */ + unsigned char note[128]; /* Current status for all notes */ + + short gm_rpn_pitch_bend_range; /* Pitch bend range */ + short gm_rpn_fine_tuning; /* Master fine tuning */ + short gm_rpn_coarse_tuning; /* Master coarse tuning */ + +} snd_midi_channel_t; + +/* + * A structure that represets a set of channels bound to a port. There + * would usually be 16 channels per port. But fewer could be used for + * particular cases. + * The channel set consists of information describing the client and + * port for this midi synth and an array of snd_midi_channel_t structures. + * A driver that had no need for snd_midi_channel_t could still use the + * channel set type if it wished with the channel array null. + */ +typedef struct snd_midi_channel_set { + void *private_data; /* Driver data */ + int client; /* Client for this port */ + int port; /* The port number */ + + int max_channels; /* Size of the channels array */ + snd_midi_channel_t *channels; + + unsigned char midi_mode; /* MIDI operating mode */ + unsigned char gs_master_volume; /* SYSEX master volume: 0-127 */ + unsigned char gs_chorus_mode; + unsigned char gs_reverb_mode; + +} snd_midi_channel_set_t; + +typedef struct snd_seq_midi_op { + void (*note_on)(void *private_data, int note, int vel, snd_midi_channel_t *chan); + void (*note_off)(void *private_data,int note, int vel, snd_midi_channel_t *chan); /* release note */ + void (*key_press)(void *private_data, int note, int vel, snd_midi_channel_t *chan); + void (*note_terminate)(void *private_data, int note, snd_midi_channel_t *chan); /* terminate note immediately */ + void (*control)(void *private_data, int type, snd_midi_channel_t *chan); + void (*nrpn)(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); + void (*sysex)(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +} snd_midi_op_t; + +/* + * These defines are used so that pitchbend, aftertouch etc, can be + * distinguished from controller values. + */ +/* 0-127 controller values */ +#define MIDI_CTL_PITCHBEND 0x80 +#define MIDI_CTL_AFTERTOUCH 0x81 +#define MIDI_CTL_CHAN_PRESSURE 0x82 + +/* + * These names exist to allow symbolic access to the controls array. + * The usage is eg: chan->gm_bank_select. Another implementation would + * be really have these members in the struct, and not the array. + */ +#define gm_bank_select control[0] +#define gm_modulation control[1] +#define gm_breath control[2] +#define gm_foot_pedal control[4] +#define gm_portamento_time control[5] +#define gm_data_entry control[6] +#define gm_volume control[7] +#define gm_balance control[8] +#define gm_pan control[10] +#define gm_expression control[11] +#define gm_effect_control1 control[12] +#define gm_effect_control2 control[13] +#define gm_slider1 control[16] +#define gm_slider2 control[17] +#define gm_slider3 control[18] +#define gm_slider4 control[19] + +#define gm_bank_select_lsb control[32] +#define gm_modulation_wheel_lsb control[33] +#define gm_breath_lsb control[34] +#define gm_foot_pedal_lsb control[36] +#define gm_portamento_time_lsb control[37] +#define gm_data_entry_lsb control[38] +#define gm_volume_lsb control[39] +#define gm_balance_lsb control[40] +#define gm_pan_lsb control[42] +#define gm_expression_lsb control[43] +#define gm_effect_control1_lsb control[44] +#define gm_effect_control2_lsb control[45] + +#define gm_sustain control[MIDI_CTL_SUSTAIN] +#define gm_hold gm_sustain +#define gm_portamento control[MIDI_CTL_PORTAMENTO] +#define gm_sustenuto control[MIDI_CTL_SUSTENUTO] + +/* + * These macros give the complete value of the controls that consist + * of coarse and fine pairs. Of course the fine controls are seldom used + * but there is no harm in being complete. + */ +#define SNDRV_GM_BANK_SELECT(cp) (((cp)->control[0]<<7)|((cp)->control[32])) +#define SNDRV_GM_MODULATION_WHEEL(cp) (((cp)->control[1]<<7)|((cp)->control[33])) +#define SNDRV_GM_BREATH(cp) (((cp)->control[2]<<7)|((cp)->control[34])) +#define SNDRV_GM_FOOT_PEDAL(cp) (((cp)->control[4]<<7)|((cp)->control[36])) +#define SNDRV_GM_PORTAMENTO_TIME(cp) (((cp)->control[5]<<7)|((cp)->control[37])) +#define SNDRV_GM_DATA_ENTRY(cp) (((cp)->control[6]<<7)|((cp)->control[38])) +#define SNDRV_GM_VOLUME(cp) (((cp)->control[7]<<7)|((cp)->control[39])) +#define SNDRV_GM_BALANCE(cp) (((cp)->control[8]<<7)|((cp)->control[40])) +#define SNDRV_GM_PAN(cp) (((cp)->control[10]<<7)|((cp)->control[42])) +#define SNDRV_GM_EXPRESSION(cp) (((cp)->control[11]<<7)|((cp)->control[43])) + + +/* MIDI mode */ +#define SNDRV_MIDI_MODE_NONE 0 /* Generic midi */ +#define SNDRV_MIDI_MODE_GM 1 +#define SNDRV_MIDI_MODE_GS 2 +#define SNDRV_MIDI_MODE_XG 3 +#define SNDRV_MIDI_MODE_MT32 4 + +/* MIDI note state */ +#define SNDRV_MIDI_NOTE_OFF 0x00 +#define SNDRV_MIDI_NOTE_ON 0x01 +#define SNDRV_MIDI_NOTE_RELEASED 0x02 +#define SNDRV_MIDI_NOTE_SUSTENUTO 0x04 + +#define SNDRV_MIDI_PARAM_TYPE_REGISTERED 0 +#define SNDRV_MIDI_PARAM_TYPE_NONREGISTERED 1 + +/* SYSEX parse flag */ +enum { + SNDRV_MIDI_SYSEX_NOT_PARSED = 0, + SNDRV_MIDI_SYSEX_GM_ON, + SNDRV_MIDI_SYSEX_GS_ON, + SNDRV_MIDI_SYSEX_GS_RESET, + SNDRV_MIDI_SYSEX_GS_CHORUS_MODE, + SNDRV_MIDI_SYSEX_GS_REVERB_MODE, + SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME, + SNDRV_MIDI_SYSEX_GS_PROGRAM, + SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL, + SNDRV_MIDI_SYSEX_XG_ON, +}; + +/* Prototypes for midi_process.c */ +void snd_midi_process_event(snd_midi_op_t *ops, snd_seq_event_t *ev, + snd_midi_channel_set_t *chanset); +void snd_midi_channel_set_clear(snd_midi_channel_set_t *chset); +void snd_midi_channel_init(snd_midi_channel_t *p, int n); +snd_midi_channel_t *snd_midi_channel_init_set(int n); +snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n); +void snd_midi_channel_free_set(snd_midi_channel_set_t *chset); + +#endif /* __SOUND_SEQ_MIDI_EMUL_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_midi_event.h linux/include/sound/seq_midi_event.h --- linux-2.4.21-rc1.orig/include/sound/seq_midi_event.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_midi_event.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,58 @@ +#ifndef __SOUND_SEQ_MIDI_EVENT_H +#define __SOUND_SEQ_MIDI_EVENT_H + +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99 Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "asequencer.h" + +#define MAX_MIDI_EVENT_BUF 256 + +typedef struct snd_midi_event_t snd_midi_event_t; + +/* midi status */ +struct snd_midi_event_t { + int qlen; /* queue length */ + int read; /* chars read */ + int type; /* current event type */ + unsigned char lastcmd; + unsigned char nostat; + int bufsize; + unsigned char *buf; /* input buffer */ + spinlock_t lock; +}; + +#define SND_MIDI_EVENT_NOSTATUS (1<<0) /* don't encode MIDI status */ + +int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev); +int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize); +void snd_midi_event_free(snd_midi_event_t *dev); +void snd_midi_event_init(snd_midi_event_t *dev); +void snd_midi_event_reset_encode(snd_midi_event_t *dev); +void snd_midi_event_reset_decode(snd_midi_event_t *dev); +void snd_midi_event_no_status(snd_midi_event_t *dev, int on); +/* encode from byte stream - return number of written bytes if success */ +long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev); +int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev); +/* decode from event to bytes - return number of written bytes if success */ +long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev); + +#endif /* __SOUND_SEQ_MIDI_EVENT_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_oss.h linux/include/sound/seq_oss.h --- linux-2.4.21-rc1.orig/include/sound/seq_oss.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_oss.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,102 @@ +#ifndef __SOUND_SEQ_OSS_H +#define __SOUND_SEQ_OSS_H + +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "asequencer.h" +#include "seq_kernel.h" + +/* + * type definitions + */ +typedef struct snd_seq_oss_arg_t snd_seq_oss_arg_t; +typedef struct snd_seq_oss_callback_t snd_seq_oss_callback_t; + +/* + * argument structure for synthesizer operations + */ +struct snd_seq_oss_arg_t { + /* given by OSS sequencer */ + int app_index; /* application unique index */ + int file_mode; /* file mode - see below */ + int seq_mode; /* sequencer mode - see below */ + + /* following must be initialized in open callback */ + snd_seq_addr_t addr; /* opened port address */ + void *private_data; /* private data for lowlevel drivers */ + + /* note-on event passing mode: initially given by OSS seq, + * but configurable by drivers - see below + */ + int event_passing; +}; + + +/* + * synthesizer operation callbacks + */ +struct snd_seq_oss_callback_t { + struct module *owner; + int (*open)(snd_seq_oss_arg_t *p, void *closure); + int (*close)(snd_seq_oss_arg_t *p); + int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg); + int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count); + int (*reset)(snd_seq_oss_arg_t *p); + int (*raw_event)(snd_seq_oss_arg_t *p, unsigned char *data); +}; + +/* flag: file_mode */ +#define SNDRV_SEQ_OSS_FILE_ACMODE 3 +#define SNDRV_SEQ_OSS_FILE_READ 1 +#define SNDRV_SEQ_OSS_FILE_WRITE 2 +#define SNDRV_SEQ_OSS_FILE_NONBLOCK 4 + +/* flag: seq_mode */ +#define SNDRV_SEQ_OSS_MODE_SYNTH 0 +#define SNDRV_SEQ_OSS_MODE_MUSIC 1 + +/* flag: event_passing */ +#define SNDRV_SEQ_OSS_PROCESS_EVENTS 0 /* key == 255 is processed as velocity change */ +#define SNDRV_SEQ_OSS_PASS_EVENTS 1 /* pass all events to callback */ +#define SNDRV_SEQ_OSS_PROCESS_KEYPRESS 2 /* key >= 128 will be processed as key-pressure */ + +/* default control rate: fixed */ +#define SNDRV_SEQ_OSS_CTRLRATE 100 + +/* default max queue length: configurable by module option */ +#define SNDRV_SEQ_OSS_MAX_QLEN 1024 + + +/* + * data pointer to snd_seq_register_device + */ +typedef struct snd_seq_oss_reg { + int type; + int subtype; + int nvoices; + snd_seq_oss_callback_t oper; + void *private_data; +} snd_seq_oss_reg_t; + +/* device id */ +#define SNDRV_SEQ_DEV_ID_OSS "seq-oss" + +#endif /* __SOUND_SEQ_OSS_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/seq_oss_legacy.h linux/include/sound/seq_oss_legacy.h --- linux-2.4.21-rc1.orig/include/sound/seq_oss_legacy.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_oss_legacy.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,31 @@ +#ifndef __SOUND_SEQ_OSS_LEGACY_H +#define __SOUND_SEQ_OSS_LEGACY_H + +/* + * OSS compatible macro definitions + * + * Copyright (C) 2000 Abramo Bagnara + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#ifndef SAMPLE_TYPE_AWE32 +#define SAMPLE_TYPE_AWE32 0x20 +#endif + +#endif /* __SOUND_SEQ_OSS_LEGACY_H */ + diff -urN linux-2.4.21-rc1.orig/include/sound/seq_virmidi.h linux/include/sound/seq_virmidi.h --- linux-2.4.21-rc1.orig/include/sound/seq_virmidi.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/seq_virmidi.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,84 @@ +#ifndef __SOUND_SEQ_VIRMIDI_H +#define __SOUND_SEQ_VIRMIDI_H + +/* + * Virtual Raw MIDI client on Sequencer + * Copyright (c) 2000 by Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "rawmidi.h" +#include "seq_midi_event.h" + +typedef struct _snd_virmidi_dev snd_virmidi_dev_t; + +/* + * device file instance: + * This instance is created at each time the midi device file is + * opened. Each instance has its own input buffer and MIDI parser + * (buffer), and is associated with the device instance. + */ +typedef struct _snd_virmidi { + struct list_head list; + int seq_mode; + int client; + int port; + int trigger: 1; + snd_midi_event_t *parser; + snd_seq_event_t event; + snd_virmidi_dev_t *rdev; + snd_rawmidi_substream_t *substream; +} snd_virmidi_t; + +#define SNDRV_VIRMIDI_SUBSCRIBE (1<<0) +#define SNDRV_VIRMIDI_USE (1<<1) + +/* + * device record: + * Each virtual midi device has one device instance. It contains + * common information and the linked-list of opened files, + */ +struct _snd_virmidi_dev { + snd_card_t *card; /* associated card */ + snd_rawmidi_t *rmidi; /* rawmidi device */ + int seq_mode; /* SNDRV_VIRMIDI_XXX */ + int device; /* sequencer device */ + int client; /* created/attached client */ + int port; /* created/attached port */ + unsigned int flags; /* SNDRV_VIRMIDI_* */ + rwlock_t filelist_lock; + struct list_head filelist; +}; + +/* sequencer mode: + * ATTACH = input/output events from midi device are routed to the + * attached sequencer port. sequencer port is not created + * by virmidi itself. + * the input to rawmidi must be processed by passing the + * incoming events via snd_virmidi_receive() + * DISPATCH = input/output events are routed to subscribers. + * sequencer port is created in virmidi. + */ +#define SNDRV_VIRMIDI_SEQ_NONE 0 +#define SNDRV_VIRMIDI_SEQ_ATTACH 1 +#define SNDRV_VIRMIDI_SEQ_DISPATCH 2 + +int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi); +int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev); + +#endif /* __SOUND_SEQ_VIRMIDI */ diff -urN linux-2.4.21-rc1.orig/include/sound/sfnt_info.h linux/include/sound/sfnt_info.h --- linux-2.4.21-rc1.orig/include/sound/sfnt_info.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/sfnt_info.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,184 @@ +#ifndef __SOUND_SFNT_INFO_H +#define __SOUND_SFNT_INFO_H + +/* + * Patch record compatible with AWE driver on OSS + * + * Copyright (C) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_oss_legacy.h" + +/* + * patch information record + */ + +/* patch interface header: 16 bytes */ +typedef struct soundfont_patch_info_t { + unsigned short key; /* use the key below */ +#define SNDRV_OSS_SOUNDFONT_PATCH _PATCHKEY(0x07) + + short device_no; /* synthesizer number */ + unsigned short sf_id; /* file id (should be zero) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ + + short type; /* patch operation type */ +#define SNDRV_SFNT_LOAD_INFO 0 /* awe_voice_rec */ +#define SNDRV_SFNT_LOAD_DATA 1 /* awe_sample_info */ +#define SNDRV_SFNT_OPEN_PATCH 2 /* awe_open_parm */ +#define SNDRV_SFNT_CLOSE_PATCH 3 /* none */ + /* 4 is obsolete */ +#define SNDRV_SFNT_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define SNDRV_SFNT_MAP_PRESET 6 /* awe_voice_map */ + /* 7 is not used */ +#define SNDRV_SFNT_PROBE_DATA 8 /* optarg=sample */ +#define SNDRV_SFNT_REMOVE_INFO 9 /* optarg=(bank<<8)|instr */ + + short reserved; /* word alignment data */ + + /* the actual patch data begins after this */ +} soundfont_patch_info_t; + + +/* + * open patch + */ + +#define SNDRV_SFNT_PATCH_NAME_LEN 32 + +typedef struct soundfont_open_parm_t { + unsigned short type; /* sample type */ +#define SNDRV_SFNT_PAT_TYPE_MISC 0 +#define SNDRV_SFNT_PAT_TYPE_GUS 6 +#define SNDRV_SFNT_PAT_TYPE_MAP 7 +#define SNDRV_SFNT_PAT_LOCKED 0x100 /* lock the samples */ +#define SNDRV_SFNT_PAT_SHARED 0x200 /* sample is shared */ + + short reserved; + char name[SNDRV_SFNT_PATCH_NAME_LEN]; +} soundfont_open_parm_t; + + +/* + * raw voice information record + */ + +/* wave table envelope & effect parameters to control EMU8000 */ +typedef struct soundfont_voice_parm_t { + unsigned short moddelay; /* modulation delay (0x8000) */ + unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */ + unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */ + unsigned short modrelease; /* modulation release time (0x807f) */ + short modkeyhold, modkeydecay; /* envelope change per key (not used) */ + unsigned short voldelay; /* volume delay (0x8000) */ + unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */ + unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */ + unsigned short volrelease; /* volume release time (0x807f) */ + short volkeyhold, volkeydecay; /* envelope change per key (not used) */ + unsigned short lfo1delay; /* LFO1 delay (0x8000) */ + unsigned short lfo2delay; /* LFO2 delay (0x8000) */ + unsigned short pefe; /* modulation pitch & cutoff (0x0000) */ + unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */ + unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */ + unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */ + unsigned char cutoff; /* initial cutoff (0xff) */ + unsigned char filterQ; /* initial filter Q [0-15] (0x0) */ + unsigned char chorus; /* chorus send (0x00) */ + unsigned char reverb; /* reverb send (0x00) */ + unsigned short reserved[4]; /* not used */ +} soundfont_voice_parm_t; + + +/* wave table parameters: 92 bytes */ +typedef struct soundfont_voice_info_t { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ + short rate_offset; /* sample rate pitch offset */ + unsigned short mode; /* sample mode */ +#define SNDRV_SFNT_MODE_ROMSOUND 0x8000 +#define SNDRV_SFNT_MODE_STEREO 1 +#define SNDRV_SFNT_MODE_LOOPING 2 +#define SNDRV_SFNT_MODE_NORELEASE 4 /* obsolete */ +#define SNDRV_SFNT_MODE_INIT_PARM 8 + + short root; /* midi root key */ + short tune; /* pitch tuning (in cents) */ + unsigned char low, high; /* key note range */ + unsigned char vellow, velhigh; /* velocity range */ + signed char fixkey, fixvel; /* fixed key, velocity */ + signed char pan, fixpan; /* panning, fixed panning */ + short exclusiveClass; /* exclusive class (0 = none) */ + unsigned char amplitude; /* sample volume (127 max) */ + unsigned char attenuation; /* attenuation (0.375dB) */ + short scaleTuning; /* pitch scale tuning(%), normally 100 */ + soundfont_voice_parm_t parm; /* voice envelope parameters */ + unsigned short sample_mode; /* sample mode_flag (set by driver) */ +} soundfont_voice_info_t; + + +/* instrument info header: 4 bytes */ +typedef struct soundfont_voice_rec_hdr_t { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define SNDRV_SFNT_WR_APPEND 0 /* append anyway */ +#define SNDRV_SFNT_WR_EXCLUSIVE 1 /* skip if already exists */ +#define SNDRV_SFNT_WR_REPLACE 2 /* replace if already exists */ +} soundfont_voice_rec_hdr_t; + + +/* + * sample wave information + */ + +/* wave table sample header: 32 bytes */ +typedef struct soundfont_sample_info_t { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ + short dummy; /* not used */ + unsigned short mode_flags; /* mode flags */ +#define SNDRV_SFNT_SAMPLE_8BITS 1 /* wave data is 8bits */ +#define SNDRV_SFNT_SAMPLE_UNSIGNED 2 /* wave data is unsigned */ +#define SNDRV_SFNT_SAMPLE_NO_BLANK 4 /* no blank loop is attached */ +#define SNDRV_SFNT_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */ +#define SNDRV_SFNT_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */ +#define SNDRV_SFNT_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ +#define SNDRV_SFNT_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ +#define SNDRV_SFNT_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ + unsigned int truesize; /* used memory size (set by driver) */ +} soundfont_sample_info_t; + + +/* + * voice preset mapping (aliasing) + */ + +typedef struct soundfont_voice_map_t { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} soundfont_voice_map_t; + + +#endif /* __SOUND_SFNT_INFO_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/snd_wavefront.h linux/include/sound/snd_wavefront.h --- linux-2.4.21-rc1.orig/include/sound/snd_wavefront.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/snd_wavefront.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,142 @@ +#ifndef __SOUND_SND_WAVEFRONT_H__ +#define __SOUND_SND_WAVEFRONT_H__ + +#include "cs4231.h" +#include "mpu401.h" +#include "hwdep.h" +#include "rawmidi.h" +#include "wavefront.h" /* generic OSS/ALSA/user-level wavefront header */ + +/* MIDI interface */ + +struct _snd_wavefront_midi; +struct _snd_wavefront_card; +struct _snd_wavefront; + +typedef struct _snd_wavefront_midi snd_wavefront_midi_t; +typedef struct _snd_wavefront_card snd_wavefront_card_t; +typedef struct _snd_wavefront snd_wavefront_t; + +typedef enum { internal_mpu = 0, external_mpu = 1 } snd_wavefront_mpu_id; + +struct _snd_wavefront_midi { + unsigned long base; /* I/O port address */ + char isvirtual; /* doing virtual MIDI stuff ? */ + char istimer; /* timer is used */ + snd_wavefront_mpu_id output_mpu; /* most-recently-used */ + snd_wavefront_mpu_id input_mpu; /* most-recently-used */ + unsigned int mode[2]; /* MPU401_MODE_XXX */ + snd_rawmidi_substream_t *substream_output[2]; + snd_rawmidi_substream_t *substream_input[2]; + struct timer_list timer; + spinlock_t open; + spinlock_t virtual; /* protects isvirtual */ +}; + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define UART_MODE_ON 0x3F + +extern snd_rawmidi_ops_t snd_wavefront_midi_output; +extern snd_rawmidi_ops_t snd_wavefront_midi_input; + +extern void snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *); +extern void snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *); +extern void snd_wavefront_midi_interrupt (snd_wavefront_card_t *); +extern int snd_wavefront_midi_start (snd_wavefront_card_t *); + +struct _snd_wavefront { + unsigned long irq; /* "you were one, one of the few ..." */ + unsigned long base; /* low i/o port address */ + struct resource *res_base; /* i/o port resource allocation */ + +#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 */ + volatile int irq_cnt; /* ditto */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char has_fx; /* has FX processor (Tropez+) */ + 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_are_midi; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + spinlock_t irq_lock; + wait_queue_head_t interrupt_sleeper; + snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */ +}; + +struct _snd_wavefront_card { + snd_wavefront_t wavefront; +#ifdef __ISAPNP__ + struct isapnp_dev *wss; + struct isapnp_dev *ctrl; + struct isapnp_dev *mpu; + struct isapnp_dev *synth; +#endif /* CONFIG_ISAPNP */ +}; + +extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card); +extern int snd_wavefront_interrupt_bits (int irq); +extern int snd_wavefront_detect_irq (snd_wavefront_t *dev) ; +extern int snd_wavefront_check_irq (snd_wavefront_t *dev, int irq); +extern int snd_wavefront_restart (snd_wavefront_t *dev); +extern int snd_wavefront_start (snd_wavefront_t *dev); +extern int snd_wavefront_detect (snd_wavefront_card_t *card); +extern int snd_wavefront_config_midi (snd_wavefront_t *dev) ; +extern int snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *, + unsigned char *); + +extern int snd_wavefront_synth_ioctl (snd_hwdep_t *, + struct file *, + unsigned int cmd, + unsigned long arg); +extern int snd_wavefront_synth_open (snd_hwdep_t *, struct file *); +extern int snd_wavefront_synth_release (snd_hwdep_t *, struct file *); + +/* FX processor - see also yss225.[ch] */ + +extern int snd_wavefront_fx_start (snd_wavefront_t *); +extern int snd_wavefront_fx_detect (snd_wavefront_t *); +extern int snd_wavefront_fx_ioctl (snd_hwdep_t *, + struct file *, + unsigned int cmd, + unsigned long arg); +extern int snd_wavefront_fx_open (snd_hwdep_t *, struct file *); +extern int snd_wavefront_fx_release (snd_hwdep_t *, struct file *); + +/* prefix in all snd_printk() delivered messages */ + +#define LOGNAME "WaveFront: " + +#endif /* __SOUND_SND_WAVEFRONT_H__ */ diff -urN linux-2.4.21-rc1.orig/include/sound/sndmagic.h linux/include/sound/sndmagic.h --- linux-2.4.21-rc1.orig/include/sound/sndmagic.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/sndmagic.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,208 @@ +#ifndef __SOUND_SNDMAGIC_H +#define __SOUND_SNDMAGIC_H + +/* + * Magic allocation, deallocation, check + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifdef CONFIG_SND_DEBUG_MEMORY + +void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags); +void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags); + +/** + * snd_magic_kmalloc - allocate a record with a magic-prefix + * @type: the type to allocate a record (like xxx_t) + * @extra: the extra size to allocate in bytes + * @flags: the allocation condition (GFP_XXX) + * + * Allocates a record of the given type with the extra space and + * returns its pointer. The allocated record has a secret magic-key + * to be checked via snd_magic_cast() for safe casts. + * + * The allocated pointer must be released via snd_magic_kfree(). + * + * The "struct xxx" style cannot be used as the type argument + * because the magic-key constant is generated from the type-name + * string. + */ +#define snd_magic_kmalloc(type, extra, flags) \ + (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) +/** + * snd_magic_kcalloc - allocate a record with a magic-prefix and initialize + * @type: the type to allocate a record (like xxx_t) + * @extra: the extra size to allocate in bytes + * @flags: the allocation condition (GFP_XXX) + * + * Works like snd_magic_kmalloc() but this clears the area with zero + * automatically. + */ +#define snd_magic_kcalloc(type, extra, flags) \ + (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) + +/** + * snd_magic_kfree - release the allocated area + * @ptr: the pointer allocated via snd_magic_kmalloc() or snd_magic_kcalloc() + * + * Releases the memory area allocated via snd_magic_kmalloc() or + * snd_magic_kcalloc() function. + */ +void snd_magic_kfree(void *ptr); + +static inline unsigned long _snd_magic_value(void *obj) +{ + return obj == NULL ? (unsigned long)-1 : *(((unsigned long *)obj) - 1); +} + +static inline int _snd_magic_bad(void *obj, unsigned long magic) +{ + return _snd_magic_value(obj) != magic; +} + +#define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd) + +/** + * snd_magic_cast - check and cast the magic-allocated pointer + * @type: the type of record to cast + * @ptr: the magic-allocated pointer + * @action...: the action to do if failed + * + * This macro provides a safe cast for the given type, which was + * allocated via snd_magic_kmalloc() or snd_magic_kcallc(). + * If the pointer is invalid, i.e. the cast-type doesn't match, + * the action arguments are called with a debug message. + */ +#define snd_magic_cast(type, ptr, action...) \ + (type *) ({\ + void *__ptr = ptr;\ + unsigned long __magic = _snd_magic_value(__ptr);\ + if (__magic != type##_magic) {\ + snd_printk("bad MAGIC (0x%lx)\n", __magic);\ + action;\ + }\ + __ptr;\ +}) + +#define snd_device_t_magic 0xa15a00ff +#define snd_pcm_t_magic 0xa15a0101 +#define snd_pcm_file_t_magic 0xa15a0102 +#define snd_pcm_substream_t_magic 0xa15a0103 +#define snd_pcm_proc_private_t_magic 0xa15a0104 +#define snd_pcm_oss_file_t_magic 0xa15a0105 +#define snd_mixer_oss_t_magic 0xa15a0106 +// #define snd_pcm_sgbuf_t_magic 0xa15a0107 + +#define snd_info_private_data_t_magic 0xa15a0201 +#define snd_info_entry_t_magic 0xa15a0202 +#define snd_ctl_file_t_magic 0xa15a0301 +#define snd_kcontrol_t_magic 0xa15a0302 +#define snd_rawmidi_t_magic 0xa15a0401 +#define snd_rawmidi_file_t_magic 0xa15a0402 +#define snd_virmidi_t_magic 0xa15a0403 +#define snd_virmidi_dev_t_magic 0xa15a0404 +#define snd_timer_t_magic 0xa15a0501 +#define snd_timer_user_t_magic 0xa15a0502 +#define snd_hwdep_t_magic 0xa15a0601 +#define snd_seq_device_t_magic 0xa15a0701 + +#define es18xx_t_magic 0xa15a1101 +#define trident_t_magic 0xa15a1201 +#define es1938_t_magic 0xa15a1301 +#define cs46xx_t_magic 0xa15a1401 +#define cs46xx_pcm_t_magic 0xa15a1402 +#define ensoniq_t_magic 0xa15a1501 +#define sonicvibes_t_magic 0xa15a1601 +#define mpu401_t_magic 0xa15a1701 +#define fm801_t_magic 0xa15a1801 +#define ac97_t_magic 0xa15a1901 +#define ak4531_t_magic 0xa15a1a01 +#define snd_uart16550_t_magic 0xa15a1b01 +#define emu10k1_t_magic 0xa15a1c01 +#define emu10k1_pcm_t_magic 0xa15a1c02 +#define emu10k1_midi_t_magic 0xa15a1c03 +#define snd_gus_card_t_magic 0xa15a1d01 +#define gus_pcm_private_t_magic 0xa15a1d02 +#define gus_proc_private_t_magic 0xa15a1d03 +#define tea6330t_t_magic 0xa15a1e01 +#define ad1848_t_magic 0xa15a1f01 +#define cs4231_t_magic 0xa15a2001 +#define es1688_t_magic 0xa15a2101 +#define opti93x_t_magic 0xa15a2201 +#define emu8000_t_magic 0xa15a2301 +#define emu8000_proc_private_t_magic 0xa15a2302 +#define snd_emux_t_magic 0xa15a2303 +#define snd_emux_port_t_magic 0xa15a2304 +#define sb_t_magic 0xa15a2401 +#define snd_sb_csp_t_magic 0xa15a2402 +#define snd_card_dummy_t_magic 0xa15a2501 +#define snd_card_dummy_pcm_t_magic 0xa15a2502 +#define opl3_t_magic 0xa15a2601 +#define snd_seq_dummy_port_t_magic 0xa15a2701 +#define ice1712_t_magic 0xa15a2801 +#define ad1816a_t_magic 0xa15a2901 +#define intel8x0_t_magic 0xa15a2a01 +#define es1968_t_magic 0xa15a2b01 +#define esschan_t_magic 0xa15a2b02 +#define via82xx_t_magic 0xa15a2c01 +#define pdplus_t_magic 0xa15a2d01 +#define cmipci_t_magic 0xa15a2e01 +#define ymfpci_t_magic 0xa15a2f01 +#define ymfpci_pcm_t_magic 0xa15a2f02 +#define cs4281_t_magic 0xa15a3001 +#define snd_i2c_bus_t_magic 0xa15a3101 +#define snd_i2c_device_t_magic 0xa15a3102 +#define cs8427_t_magic 0xa15a3111 +#define m3_t_magic 0xa15a3201 +#define m3_dma_t_magic 0xa15a3202 +#define nm256_t_magic 0xa15a3301 +#define nm256_dma_t_magic 0xa15a3302 +#define sam9407_t_magic 0xa15a3401 +#define pmac_t_magic 0xa15a3501 +#define ali_t_magic 0xa15a3601 +#define mtpav_t_magic 0xa15a3701 +#define mtpav_port_t_magic 0xa15a3702 +#define korg1212_t_magic 0xa15a3800 +#define opl3sa2_t_magic 0xa15a3900 +#define serialmidi_t_magic 0xa15a3a00 +#define sa11xx_uda1341_t_magic 0xa15a3b00 +#define uda1341_t_magic 0xa15a3c00 +#define l3_client_t_magic 0xa15a3d00 +#define snd_usb_audio_t_magic 0xa15a3e01 +#define usb_mixer_elem_info_t_magic 0xa15a3e02 +#define snd_usb_stream_t_magic 0xa15a3e03 +#define snd_usb_midi_t_magic 0xa15a3f01 +#define snd_usb_midi_out_endpoint_t_magic 0xa15a3f02 +#define snd_usb_midi_in_endpoint_t_magic 0xa15a3f03 +#define ak4117_t_magic 0xa15a4000 +#define psic_t_magic 0xa15a4100 + + +#else + +#define snd_magic_kcalloc(type, extra, flags) (type *) snd_kcalloc(sizeof(type) + extra, flags) +#define snd_magic_kmalloc(type, extra, flags) (type *) kmalloc(sizeof(type) + extra, flags) +#define snd_magic_cast(type, ptr, retval) (type *) ptr +#define snd_magic_cast1(type, ptr, retval) snd_magic_cast(type, ptr, retval) +#define snd_magic_kfree kfree + +#endif + +#endif /* __SOUND_SNDMAGIC_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/soundfont.h linux/include/sound/soundfont.h --- linux-2.4.21-rc1.orig/include/sound/soundfont.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/soundfont.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,131 @@ +#ifndef __SOUND_SOUNDFONT_H +#define __SOUND_SOUNDFONT_H + +/* + * Soundfont defines and definitions. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sfnt_info.h" +#include "util_mem.h" + +#define SF_MAX_INSTRUMENTS 128 /* maximum instrument number */ +#define SF_MAX_PRESETS 256 /* drums are mapped from 128 to 256 */ +#define SF_IS_DRUM_BANK(z) ((z) == 128) + +typedef struct snd_sf_zone { + struct snd_sf_zone *next; /* Link to next */ + unsigned char bank; /* Midi bank for this zone */ + unsigned char instr; /* Midi program for this zone */ + unsigned char mapped; /* True if mapped to something else */ + + soundfont_voice_info_t v; /* All the soundfont parameters */ + int counter; + struct snd_sf_sample *sample; /* Link to sample */ + + /* The following deals with preset numbers (programs) */ + struct snd_sf_zone *next_instr; /* Next zone of this instrument */ + struct snd_sf_zone *next_zone; /* Next zone in play list */ +} snd_sf_zone_t; + +typedef struct snd_sf_sample { + soundfont_sample_info_t v; + int counter; + snd_util_memblk_t *block; /* allocated data block */ + struct snd_sf_sample *next; +} snd_sf_sample_t; + +/* + * This represents all the information relating to a soundfont. + */ +typedef struct snd_soundfont { + struct snd_soundfont *next; /* Link to next */ + /*struct snd_soundfont *prev;*/ /* Link to previous */ + short id; /* file id */ + short type; /* font type */ + unsigned char name[SNDRV_SFNT_PATCH_NAME_LEN]; /* identifier */ + snd_sf_zone_t *zones; /* Font information */ + snd_sf_sample_t *samples; /* The sample headers */ +} snd_soundfont_t; + +/* + * Type of the sample access callback + */ +typedef int (*snd_sf_sample_new_t)(void *private_data, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *buf, long count); +typedef int (*snd_sf_sample_free_t)(void *private_data, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr); +typedef void (*snd_sf_sample_reset_t)(void *private); + +typedef struct snd_sf_callback { + void *private_data; + snd_sf_sample_new_t sample_new; + snd_sf_sample_free_t sample_free; + snd_sf_sample_reset_t sample_reset; +} snd_sf_callback_t; + +/* + * List of soundfonts. + */ +typedef struct snd_sf_list { + snd_soundfont_t *currsf; /* The currently open soundfont */ + int open_client; /* client pointer for lock */ + int mem_used; /* used memory size */ + snd_sf_zone_t *presets[SF_MAX_PRESETS]; + snd_soundfont_t *fonts; /* The list of soundfonts */ + int fonts_size; /* number of fonts allocated */ + int zone_counter; /* last allocated time for zone */ + int sample_counter; /* last allocated time for sample */ + int zone_locked; /* locked time for zone */ + int sample_locked; /* locked time for sample */ + snd_sf_callback_t callback; /* callback functions */ + char sf_locked; /* font lock flag */ + struct semaphore presets_mutex; + spinlock_t lock; + snd_util_memhdr_t *memhdr; +} snd_sf_list_t; + +/* Prototypes for soundfont.c */ +int snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client); +int snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data, + long count, int client); +int snd_soundfont_close_check(snd_sf_list_t *sflist, int client); + +snd_sf_list_t *snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr); +void snd_sf_free(snd_sf_list_t *sflist); + +int snd_soundfont_remove_samples(snd_sf_list_t *sflist); +int snd_soundfont_remove_unlocked(snd_sf_list_t *sflist); +int snd_soundfont_mem_used(snd_sf_list_t *sflist); + +int snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, + int preset, int bank, + int def_preset, int def_bank, + snd_sf_zone_t **table, int max_layers); + +/* Parameter conversions */ +int snd_sf_calc_parm_hold(int msec); +int snd_sf_calc_parm_attack(int msec); +int snd_sf_calc_parm_decay(int msec); +#define snd_sf_calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); +extern int snd_sf_vol_table[128]; +int snd_sf_linear_to_log(unsigned int amount, int offset, int ratio); + + +#endif /* __SOUND_SOUNDFONT_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/soundmem.h linux/include/sound/soundmem.h --- linux-2.4.21-rc1.orig/include/sound/soundmem.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/soundmem.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,8 @@ +/* + * Onboard memory management + */ + +struct SNDRV_STRU_BANK_INFO { + unsigned int address; + unsigned int size; +}; diff -urN linux-2.4.21-rc1.orig/include/sound/sscape_ioctl.h linux/include/sound/sscape_ioctl.h --- linux-2.4.21-rc1.orig/include/sound/sscape_ioctl.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/sscape_ioctl.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,19 @@ +#ifndef SSCAPE_IOCTL_H +#define SSCAPE_IOCTL_H + + +struct sscape_bootblock +{ + unsigned char code[256]; + unsigned version; +}; + +struct sscape_microcode +{ + unsigned char code[65536]; +}; + +#define SND_SSCAPE_LOAD_BOOTB _IOWR('P', 100, struct sscape_bootblock) +#define SND_SSCAPE_LOAD_MCODE _IOW('P', 101, struct sscape_microcode) + +#endif diff -urN linux-2.4.21-rc1.orig/include/sound/tea6330t.h linux/include/sound/tea6330t.h --- linux-2.4.21-rc1.orig/include/sound/tea6330t.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/tea6330t.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,42 @@ +#ifndef __SOUND_TEA6330T_H +#define __SOUND_TEA6330T_H + +/* + * Routines for control of TEA6330T circuit. + * Sound fader control circuit for car radios. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +#include "control.h" +#include "i2c.h" /* generic i2c support */ + +typedef struct { + snd_i2c_device_t *device; + snd_i2c_bus_t *bus; + int equalizer; + int fader; + unsigned char regs[8]; + unsigned char mleft, mright; + unsigned char bass, treble; + unsigned char max_bass, max_treble; +} tea6330t_t; + +extern int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer); +extern int snd_tea6330t_update_mixer(snd_card_t * card, snd_i2c_bus_t * bus, int equalizer, int fader); + +#endif /* __SOUND_TEA6330T_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/timer.h linux/include/sound/timer.h --- linux-2.4.21-rc1.orig/include/sound/timer.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/timer.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,157 @@ +#ifndef __SOUND_TIMER_H +#define __SOUND_TIMER_H + +/* + * Timer abstract layer + * Copyright (c) by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +typedef enum sndrv_timer_class snd_timer_class_t; +typedef enum sndrv_timer_slave_class snd_timer_slave_class_t; +typedef enum sndrv_timer_global snd_timer_global_t; +typedef struct sndrv_timer_id snd_timer_id_t; +typedef struct sndrv_timer_ginfo snd_timer_ginfo_t; +typedef struct sndrv_timer_gparams snd_timer_gparams_t; +typedef struct sndrv_timer_gstatus snd_timer_gstatus_t; +typedef struct sndrv_timer_select snd_timer_select_t; +typedef struct sndrv_timer_info snd_timer_info_t; +typedef struct sndrv_timer_params snd_timer_params_t; +typedef struct sndrv_timer_status snd_timer_status_t; +typedef struct sndrv_timer_read snd_timer_read_t; +typedef struct sndrv_timer_tread snd_timer_tread_t; + +#define _snd_timer_chip(timer) ((timer)->private_data) +#define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO) + +#define SNDRV_TIMER_DEVICES 16 + +#define SNDRV_TIMER_DEV_FLG_PCM 0x10000000 + +#define SNDRV_TIMER_HW_AUTO 0x00000001 /* auto trigger is supported */ +#define SNDRV_TIMER_HW_STOP 0x00000002 /* call stop before start */ +#define SNDRV_TIMER_HW_SLAVE 0x00000004 /* only slave timer (variable resolution) */ +#define SNDRV_TIMER_HW_FIRST 0x00000008 /* first tick can be incomplete */ +#define SNDRV_TIMER_HW_TASKLET 0x00000010 /* timer is called from tasklet */ + +#define SNDRV_TIMER_IFLG_SLAVE 0x00000001 +#define SNDRV_TIMER_IFLG_RUNNING 0x00000002 +#define SNDRV_TIMER_IFLG_START 0x00000004 +#define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ +#define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use tasklet) */ +#define SNDRV_TIMER_IFLG_CALLBACK 0x00000020 /* timer callback is active */ +#define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040 /* exclusive owner - no more instances */ + +#define SNDRV_TIMER_FLG_CHANGE 0x00000001 +#define SNDRV_TIMER_FLG_RESCHED 0x00000002 /* need reschedule */ + +typedef void (*snd_timer_callback_t) (snd_timer_instance_t * timeri, unsigned long ticks, unsigned long resolution); +typedef void (*snd_timer_ccallback_t) (snd_timer_instance_t * timeri, enum sndrv_timer_event event, + struct timespec * tstamp, unsigned long resolution); + +struct _snd_timer_hardware { + /* -- must be filled with low-level driver */ + unsigned int flags; /* various flags */ + unsigned long resolution; /* average timer resolution for one tick in nsec */ + unsigned long resolution_min; /* minimal resolution */ + unsigned long resolution_max; /* maximal resolution */ + unsigned long ticks; /* max timer ticks per interrupt */ + /* -- low-level functions -- */ + int (*open) (snd_timer_t * timer); + int (*close) (snd_timer_t * timer); + unsigned long (*c_resolution) (snd_timer_t * timer); + int (*start) (snd_timer_t * timer); + int (*stop) (snd_timer_t * timer); + int (*set_period) (snd_timer_t * timer, unsigned long period_num, unsigned long period_den); + int (*precise_resolution) (snd_timer_t * timer, unsigned long *num, unsigned long *den); +}; + +struct _snd_timer { + snd_timer_class_t tmr_class; + snd_card_t *card; + int tmr_device; + int tmr_subdevice; + char id[64]; + char name[80]; + unsigned int flags; + int running; /* running instances */ + unsigned long sticks; /* schedule ticks */ + void *private_data; + void (*private_free) (snd_timer_t *timer); + struct _snd_timer_hardware hw; + spinlock_t lock; + struct list_head device_list; + struct list_head open_list_head; + struct list_head active_list_head; + struct list_head ack_list_head; + struct list_head sack_list_head; /* slow ack list head */ + struct tasklet_struct task_queue; +}; + +struct _snd_timer_instance { + snd_timer_t * timer; + char *owner; + unsigned int flags; + void *private_data; + void (*private_free) (snd_timer_instance_t *ti); + snd_timer_callback_t callback; + snd_timer_ccallback_t ccallback; + void *callback_data; + unsigned long ticks; /* auto-load ticks when expired */ + unsigned long cticks; /* current ticks */ + unsigned long pticks; /* accumulated ticks for callback */ + unsigned long resolution; /* current resolution for tasklet */ + unsigned long lost; /* lost ticks */ + snd_timer_slave_class_t slave_class; + unsigned int slave_id; + struct list_head open_list; + struct list_head active_list; + struct list_head ack_list; + struct list_head slave_list_head; + struct list_head slave_active_head; + snd_timer_instance_t *master; +}; + +/* + * Registering + */ + +extern int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer); +extern void snd_timer_notify(snd_timer_t *timer, enum sndrv_timer_event event, struct timespec *tstamp); +extern int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer); +extern int snd_timer_global_free(snd_timer_t *timer); +extern int snd_timer_global_register(snd_timer_t *timer); +extern int snd_timer_global_unregister(snd_timer_t *timer); + +extern int snd_timer_open(snd_timer_instance_t ** ti, char *owner, snd_timer_id_t *tid, unsigned int slave_id); +extern int snd_timer_close(snd_timer_instance_t * timeri); +extern unsigned long snd_timer_resolution(snd_timer_instance_t * timeri); +extern int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks); +extern int snd_timer_stop(snd_timer_instance_t * timeri); +extern int snd_timer_continue(snd_timer_instance_t * timeri); +extern int snd_timer_pause(snd_timer_instance_t * timeri); + +extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left); + +extern unsigned int snd_timer_system_resolution(void); + +#endif /* __SOUND_TIMER_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/trident.h linux/include/sound/trident.h --- linux-2.4.21-rc1.orig/include/sound/trident.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/trident.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,502 @@ +#ifndef __SOUND_TRIDENT_H +#define __SOUND_TRIDENT_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "mpu401.h" +#include "ac97_codec.h" +#include "seq_midi_emul.h" +#include "seq_device.h" +#include "util_mem.h" +//#include "ainstr_iw.h" +//#include "ainstr_gf1.h" +#include "ainstr_simple.h" + +#ifndef PCI_VENDOR_ID_TRIDENT +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#endif +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#endif +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#endif + +#ifndef PCI_VENDOR_ID_SI +#define PCI_VENDOR_ID_SI 0x1039 +#endif +#ifndef PCI_DEVICE_ID_SI_7018 +#define PCI_DEVICE_ID_SI_7018 0x7018 +#endif + +#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) +#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) +#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) + +/* Trident chipsets have 1GB memory limit */ +#ifdef __alpha__ +#define TRIDENT_DMA_TYPE SNDRV_DMA_TYPE_PCI_16MB +#define TRIDENT_GFP_FLAGS GFP_DMA +#else +#define TRIDENT_DMA_TYPE SNDRV_DMA_TYPE_PCI +#if defined(__i386__) && !defined(CONFIG_1GB) +#define TRIDENT_GFP_FLAGS GFP_DMA +#else +#define TRIDENT_GFP_FLAGS 0 +#endif +#endif + +#define SNDRV_SEQ_DEV_ID_TRIDENT "trident-synth" + +#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 +#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 +#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 + +#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) + +/* TLB code constants */ +#define SNDRV_TRIDENT_PAGE_SIZE 4096 +#define SNDRV_TRIDENT_PAGE_SHIFT 12 +#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) + +#define ID_4DWAVE_DX 0x2000 +#define ID_4DWAVE_NX 0x2001 + +/* Bank definitions */ + +#define T4D_BANK_A 0 +#define T4D_BANK_B 1 +#define T4D_NUM_BANKS 2 + +/* Register definitions */ + +/* Global registers */ + +enum global_control_bits { + CHANNEL_IDX = 0x0000003f, + OVERRUN_IE = 0x00000400, /* interrupt enable: capture overrun */ + UNDERRUN_IE = 0x00000800, /* interrupt enable: playback underrun */ + ENDLP_IE = 0x00001000, /* interrupt enable: end of buffer */ + MIDLP_IE = 0x00002000, /* interrupt enable: middle buffer */ + ETOG_IE = 0x00004000, /* interrupt enable: envelope toggling */ + EDROP_IE = 0x00008000, /* interrupt enable: envelope drop */ + BANK_B_EN = 0x00010000, /* SiS: enable bank B (64 channels) */ + PCMIN_B_MIX = 0x00020000, /* SiS: PCM IN B mixing enable */ + I2S_OUT_ASSIGN = 0x00040000, /* SiS: I2S Out contains surround PCM */ + SPDIF_OUT_ASSIGN= 0x00080000, /* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */ + MAIN_OUT_ASSIGN = 0x00100000, /* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */ +}; + +enum miscint_bits { + PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, + REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, + MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, + ST_TARGET_REACHED = 0x00008000, + PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, + ACGPIO_IRQ = 0x01000000 +}; + +/* T2 legacy dma control registers. */ +#define LEGACY_DMAR0 0x00 // ADR0 +#define LEGACY_DMAR4 0x04 // CNT0 +#define LEGACY_DMAR6 0x06 // CNT0 - High bits +#define LEGACY_DMAR11 0x0b // MOD +#define LEGACY_DMAR15 0x0f // MMR + +#define T4D_START_A 0x80 +#define T4D_STOP_A 0x84 +#define T4D_DLY_A 0x88 +#define T4D_SIGN_CSO_A 0x8c +#define T4D_CSPF_A 0x90 +#define T4D_CSPF_B 0xbc +#define T4D_CEBC_A 0x94 +#define T4D_AINT_A 0x98 +#define T4D_AINTEN_A 0x9c +#define T4D_LFO_GC_CIR 0xa0 +#define T4D_MUSICVOL_WAVEVOL 0xa8 +#define T4D_SBDELTA_DELTA_R 0xac +#define T4D_MISCINT 0xb0 +#define T4D_START_B 0xb4 +#define T4D_STOP_B 0xb8 +#define T4D_SBBL_SBCL 0xc0 +#define T4D_SBCTRL_SBE2R_SBDD 0xc4 +#define T4D_STIMER 0xc8 +#define T4D_AINT_B 0xd8 +#define T4D_AINTEN_B 0xdc +#define T4D_RCI 0x70 + +/* MPU-401 UART */ +#define T4D_MPU401_BASE 0x20 +#define T4D_MPUR0 0x20 +#define T4D_MPUR1 0x21 +#define T4D_MPUR2 0x22 +#define T4D_MPUR3 0x23 + +/* S/PDIF Registers */ +#define NX_SPCTRL_SPCSO 0x24 +#define NX_SPLBA 0x28 +#define NX_SPESO 0x2c +#define NX_SPCSTATUS 0x64 + +/* Joystick */ +#define GAMEPORT_GCR 0x30 +#define GAMEPORT_MODE_ADC 0x80 +#define GAMEPORT_LEGACY 0x31 +#define GAMEPORT_AXES 0x34 + +/* NX Specific Registers */ +#define NX_TLBC 0x6c + +/* Channel Registers */ + +#define CH_START 0xe0 + +#define CH_DX_CSO_ALPHA_FMS 0xe0 +#define CH_DX_ESO_DELTA 0xe8 +#define CH_DX_FMC_RVOL_CVOL 0xec + +#define CH_NX_DELTA_CSO 0xe0 +#define CH_NX_DELTA_ESO 0xe8 +#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec + +#define CH_LBA 0xe4 +#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 +#define CH_EBUF1 0xf4 +#define CH_EBUF2 0xf8 + +/* AC-97 Registers */ + +#define DX_ACR0_AC97_W 0x40 +#define DX_ACR1_AC97_R 0x44 +#define DX_ACR2_AC97_COM_STAT 0x48 + +#define NX_ACR0_AC97_COM_STAT 0x40 +#define NX_ACR1_AC97_W 0x44 +#define NX_ACR2_AC97_R_PRIMARY 0x48 +#define NX_ACR3_AC97_R_SECONDARY 0x4c + +#define SI_AC97_WRITE 0x40 +#define SI_AC97_READ 0x44 +#define SI_SERIAL_INTF_CTRL 0x48 +#define SI_AC97_GPIO 0x4c +#define SI_ASR0 0x50 +#define SI_SPDIF_CS 0x70 +#define SI_GPIO 0x7c + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, + NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, + NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, + NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, + DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, + DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x00008000, + SI_AC97_AUDIO_BUSY = 0x00004000, + SI_AC97_MODEM_BUSY = 0x00002000, + SI_AC97_BUSY_READ = 0x00008000, + SI_AC97_SECONDARY = 0x00000080, +}; + +enum serial_intf_ctrl_bits { + WARM_RESET = 0x00000001, + COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, + PCM_SEC_AC97 = 0x00000008, + AC97_DBL_RATE = 0x00000010, + SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, + I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, + LINE1IN = 0x00000200, + MICIN = 0x00000400, + LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, + GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID = 0x00004000, + PCMOUT = 0x00010000, + SURROUT = 0x00020000, + CENTEROUT = 0x00040000, + LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, + LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, + SI_AC97_POWERDOWN = 0x04000000, +}; + +/* PCM defaults */ + +#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ +#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ + +typedef struct _snd_trident trident_t; +typedef struct _snd_trident_voice snd_trident_voice_t; +typedef struct _snd_trident_pcm_mixer snd_trident_pcm_mixer_t; + +typedef struct { + void (*sample_start)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_position_t position); + void (*sample_stop)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_stop_mode_t mode); + void (*sample_freq)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_frequency_t freq); + void (*sample_volume)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_ev_volume_t *volume); + void (*sample_loop)(trident_t *card, snd_trident_voice_t *voice, snd_seq_ev_loop_t *loop); + void (*sample_pos)(trident_t *card, snd_trident_voice_t *voice, snd_seq_position_t position); + void (*sample_private1)(trident_t *card, snd_trident_voice_t *voice, unsigned char *data); +} snd_trident_sample_ops_t; + +typedef struct { + snd_midi_channel_set_t * chset; + trident_t * trident; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + int midi_has_voices: 1; +} snd_trident_port_t; + +typedef struct snd_trident_memblk_arg { + short first_page, last_page; +} snd_trident_memblk_arg_t; + +typedef struct { + unsigned int * entries; /* 16k-aligned TLB table */ + dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ + unsigned long * shadow_entries; /* shadow entries with virtual addresses */ + void * buffer; /* pointer for table calloc */ + dma_addr_t buffer_dmaaddr; /* not accessible PCI BUS physical address */ + snd_util_memhdr_t * memhdr; /* page allocation list */ + void * silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; /* not accessible PCI BUS physical address */ +} snd_trident_tlb_t; + +struct _snd_trident_voice { + unsigned int number; + int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + + snd_seq_instr_t instr; + snd_trident_sample_ops_t *sample_ops; + + /* channel parameters */ + unsigned int CSO; /* 24 bits (16 on DX) */ + unsigned int ESO; /* 24 bits (16 on DX) */ + unsigned int LBA; /* 30 bits */ + unsigned short EC; /* 12 bits */ + unsigned short Alpha; /* 12 bits */ + unsigned short Delta; /* 16 bits */ + unsigned short Attribute; /* 16 bits - SiS 7018 */ + unsigned short Vol; /* 12 bits (6.6) */ + unsigned char Pan; /* 7 bits (1.4.2) */ + unsigned char GVSel; /* 1 bit */ + unsigned char RVol; /* 7 bits (5.2) */ + unsigned char CVol; /* 7 bits (5.2) */ + unsigned char FMC; /* 2 bits */ + unsigned char CTRL; /* 4 bits */ + unsigned char FMS; /* 4 bits */ + unsigned char LFO; /* 8 bits */ + + unsigned int negCSO; /* nonzero - use negative CSO */ + + snd_util_memblk_t *memblk; /* memory block if TLB enabled */ + + /* PCM data */ + + trident_t *trident; + snd_pcm_substream_t *substream; + snd_trident_voice_t *extra; /* extra PCM voice (acts as interrupt generator) */ + int running: 1, + capture: 1, + spdif: 1, + foldback: 1, + isync: 1, + isync2: 1, + isync3: 1; + int foldback_chan; /* foldback subdevice number */ + unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ + unsigned int spurious_threshold; /* spurious threshold */ + unsigned int isync_mark; + unsigned int isync_max; + unsigned int isync_ESO; + + /* --- */ + + void *private_data; + void (*private_free)(snd_trident_voice_t *voice); +}; + +struct _snd_4dwave { + int seq_client; + + snd_trident_port_t seq_ports[4]; + snd_simple_ops_t simple_ops; + snd_seq_kinstr_list_t *ilist; + + snd_trident_voice_t voices[64]; + + int ChanSynthCount; /* number of allocated synth channels */ + int max_size; /* maximum synth memory size in bytes */ + int current_size; /* current allocated synth mem in bytes */ +}; + +struct _snd_trident_pcm_mixer { + snd_trident_voice_t *voice; /* active voice */ + unsigned short vol; /* front volume */ + unsigned char pan; /* pan control */ + unsigned char rvol; /* rear volume */ + unsigned char cvol; /* center volume */ + unsigned char pad; + snd_kcontrol_t *ctl_vol; /* front volume */ + snd_kcontrol_t *ctl_pan; /* pan */ + snd_kcontrol_t *ctl_rvol; /* rear volume */ + snd_kcontrol_t *ctl_cvol; /* center volume */ +}; + +struct _snd_trident { + int irq; + + unsigned int device; /* device ID */ + + unsigned char bDMAStart; + + unsigned long port; + struct resource *res_port; + unsigned long midi_port; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + snd_trident_tlb_t tlb; /* TLB entries for NX cards */ + + unsigned char spdif_ctrl; + unsigned char spdif_pcm_ctrl; + unsigned int spdif_bits; + unsigned int spdif_pcm_bits; + snd_kcontrol_t *spdif_pcm_ctl; /* S/PDIF settings */ + unsigned int ac97_ctrl; + + unsigned int ChanMap[2]; /* allocation map for hardware channels */ + + int ChanPCM; /* max number of PCM channels */ + int ChanPCMcnt; /* actual number of PCM channels */ + + unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ + unsigned int in_suspend: 1; /* 1 during suspend/resume */ + + struct _snd_4dwave synth; /* synth specific variables */ + + spinlock_t event_lock; + spinlock_t voice_alloc; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; /* ADC/DAC PCM */ + snd_pcm_t *foldback; /* Foldback PCM */ + snd_pcm_t *spdif; /* SPDIF PCM */ + snd_rawmidi_t *rmidi; + snd_seq_device_t *seq_dev; + + ac97_t *ac97; + ac97_t *ac97_sec; + + unsigned int musicvol_wavevol; + snd_trident_pcm_mixer_t pcm_mixer[32]; + + spinlock_t reg_lock; + + struct snd_trident_gameport *gameport; +}; + +int snd_trident_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + trident_t ** rtrident); +int snd_trident_free(trident_t *trident); +void snd_trident_gameport(trident_t *trident); + +int snd_trident_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_attach_synthesizer(trident_t * trident); +int snd_trident_detach_synthesizer(trident_t * trident); +snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port); +void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice); +void snd_trident_start_voice(trident_t * trident, unsigned int voice); +void snd_trident_stop_voice(trident_t * trident, unsigned int voice); +void snd_trident_write_voice_regs(trident_t * trident, snd_trident_voice_t *voice); +void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max); + +/* TLB memory allocation */ +snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream); +int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_trident_synth_alloc(trident_t *trident, unsigned int size); +int snd_trident_synth_free(trident_t *trident, snd_util_memblk_t *blk); +int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size); +int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size); + +/* Power Management */ +#ifdef CONFIG_PM +void snd_trident_suspend(trident_t *trident); +void snd_trident_resume(trident_t *trident); +#endif + +#endif /* __SOUND_TRIDENT_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/uda1341.h linux/include/sound/uda1341.h --- linux-2.4.21-rc1.orig/include/sound/uda1341.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/uda1341.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,242 @@ +/* + * linux/include/linux/l3/uda1341.h + * + * Philips UDA1341 mixer device driver for ALSA + * + * Copyright (c) 2002 Tomas Kasparek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2002-03-13 Tomas Kasparek Initial release - based on uda1341.h from OSS + * 2002-03-30 Tomas Kasparek Proc filesystem support, complete mixer and DSP + * features support + */ + +/* uda1341.h,v 1.4 2003/02/25 12:48:16 perex Exp */ + +#define UDA1341_ALSA_NAME "snd-uda1341" + +/* + * Default rate set after inicialization + */ +#define AUDIO_RATE_DEFAULT 44100 + +/* + * UDA1341 L3 address and command types + */ +#define UDA1341_L3ADDR 5 +#define UDA1341_DATA0 (UDA1341_L3ADDR << 2 | 0) +#define UDA1341_DATA1 (UDA1341_L3ADDR << 2 | 1) +#define UDA1341_STATUS (UDA1341_L3ADDR << 2 | 2) + +enum uda1341_onoff { + OFF=0, + ON, +}; + +const char *onoff_names[] = { + "Off", + "On", +}; + +enum uda1341_format { + I2S=0, + LSB16, + LSB18, + LSB20, + MSB, + LSB16MSB, + LSB18MSB, + LSB20MSB, +}; + +const char *format_names[] = { + "I2S-bus", + "LSB 16bits", + "LSB 18bits", + "LSB 20bits", + "MSB", + "in LSB 16bits/out MSB", + "in LSB 18bits/out MSB", + "in LSB 20bits/out MSB", +}; + +enum uda1341_fs { + F512=0, + F384, + F256, + Funused, +}; + +const char *fs_names[] = { + "512*fs", + "384*fs", + "256*fs", + "Unused - bad value!", +}; + +enum uda1341_peak { + BEFORE=0, + AFTER, +}; + +const char *peak_names[] = { + "before", + "after", +}; + +enum uda1341_filter { + FLAT=0, + MIN, + MIN2, + MAX, +}; + +const char *filter_names[] = { + "flat", + "min", + "min", + "max", +}; + +const char*bass_values[][16] = { + {"0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", + "0 dB", "0 dB", "0 dB", "0 dB", "undefined", }, //flat + {"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "18 dB", + "18 dB", "18 dB", "18 dB", "18 dB", "undefined",}, // min + {"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "18 dB", + "18 dB", "18 dB", "18 dB", "18 dB", "undefined",}, // min + {"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "20 dB", + "22 dB", "24 dB", "24 dB", "24 dB", "undefined",}, // max +}; + +enum uda1341_mixer { + DOUBLE, + LINE, + MIC, + MIXER, +}; + +const char *mixer_names[] = { + "double differential", + "input channel 1 (line in)", + "input channel 2 (microphone)", + "digital mixer", +}; + +enum uda1341_deemp { + NONE, + D32, + D44, + D48, +}; + +const char *deemp_names[] = { + "none", + "32 kHz", + "44.1 kHz", + "48 kHz", +}; + +const char *mic_sens_value[] = { + "-3 dB", "0 dB", "3 dB", "9 dB", "15 dB", "21 dB", "27 dB", "not used", +}; + +const unsigned short AGC_atime[] = { + 11, 16, 11, 16, 21, 11, 16, 21, +}; + +const unsigned short AGC_dtime[] = { + 100, 100, 200, 200, 200, 400, 400, 400, +}; + +const char *AGC_level[] = { + "-9.0", "-11.5", "-15.0", "-17.5", +}; + +const char *ig_small_value[] = { + "-3.0", "-2.5", "-2.0", "-1.5", "-1.0", "-0.5", +}; + +/* + * this was computed as peak_value[i] = pow((63-i)*1.42,1.013) + * + * UDA1341 datasheet on page 21: Peak value (dB) = (Peak level - 63.5)*5*log2 + * There is an table with these values [level]=value: [3]=-90.31, [7]=-84.29 + * [61]=-2.78, [62] = -1.48, [63] = 0.0 + * I tried to compute it, but using but even using logarithm with base either 10 or 2 + * i was'n able to get values in the table from the formula. So I constructed another + * formula (see above) to interpolate the values as good as possible. If there is some + * mistake, please contact me on tomas.kasparek@seznam.cz. Thanks. + * UDA1341TS datasheet is available at: + * http://www-us9.semiconductors.com/acrobat/datasheets/UDA1341TS_3.pdf + */ +const char *peak_value[] = { + "-INF dB", "N.A.", "N.A", "90.31 dB", "N.A.", "N.A.", "N.A.", "-84.29 dB", + "-82.65 dB", "-81.13 dB", "-79.61 dB", "-78.09 dB", "-76.57 dB", "-75.05 dB", "-73.53 dB", + "-72.01 dB", "-70.49 dB", "-68.97 dB", "-67.45 dB", "-65.93 dB", "-64.41 dB", "-62.90 dB", + "-61.38 dB", "-59.86 dB", "-58.35 dB", "-56.83 dB", "-55.32 dB", "-53.80 dB", "-52.29 dB", + "-50.78 dB", "-49.26 dB", "-47.75 dB", "-46.24 dB", "-44.73 dB", "-43.22 dB", "-41.71 dB", + "-40.20 dB", "-38.69 dB", "-37.19 dB", "-35.68 dB", "-34.17 dB", "-32.67 dB", "-31.17 dB", + "-29.66 dB", "-28.16 dB", "-26.66 dB", "-25.16 dB", "-23.66 dB", "-22.16 dB", "-20.67 dB", + "-19.17 dB", "-17.68 dB", "-16.19 dB", "-14.70 dB", "-13.21 dB", "-11.72 dB", "-10.24 dB", + "-8.76 dB", "-7.28 dB", "-5.81 dB", "-4.34 dB", "-2.88 dB", "-1.43 dB", "0.00 dB", +}; + +enum uda1341_config { + CMD_READ_REG = 0, + CMD_RESET, + CMD_FS, + CMD_FORMAT, + CMD_OGAIN, + CMD_IGAIN, + CMD_DAC, + CMD_ADC, + CMD_VOLUME, + CMD_BASS, + CMD_TREBBLE, + CMD_PEAK, + CMD_DEEMP, + CMD_MUTE, + CMD_FILTER, + CMD_CH1, + CMD_CH2, + CMD_MIC, + CMD_MIXER, + CMD_AGC, + CMD_IG, + CMD_AGC_TIME, + CMD_AGC_LEVEL, + CMD_LAST, +}; + +enum write_through { + //used in update_bits (write_cfg) to avoid l3_write - just update local copy of regs. + REGS_ONLY=0, + //update local regs and write value to uda1341 - do l3_write + FLUSH, +}; + +int __init snd_chip_uda1341_mixer_new(snd_card_t *card, struct l3_client **clnt); +void __init snd_chip_uda1341_mixer_del(snd_card_t *card); + +#ifdef DEBUG_MODE +#define DEBUG(format, args...) do{printk(format, ##args);}while(0) +#else +#define DEBUG(format, args...) /* nothing */ +#endif + +#ifdef DEBUG_FUNCTION_NAMES +#define DEBUG_NAME(format, args...) do{printk(format, ##args);}while(0) +#else +#define DEBUG_NAME(format, args...) /* nothing */ +#endif + +/* + * Local variables: + * indent-tabs-mode: t + * End: + */ diff -urN linux-2.4.21-rc1.orig/include/sound/util_mem.h linux/include/sound/util_mem.h --- linux-2.4.21-rc1.orig/include/sound/util_mem.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/util_mem.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,64 @@ +#ifndef __SOUND_UTIL_MEM_H +#define __SOUND_UTIL_MEM_H +/* + * Copyright (C) 2000 Takashi Iwai + * + * Generic memory management routines for soundcard memory allocation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct snd_util_memblk snd_util_memblk_t; +typedef struct snd_util_memhdr snd_util_memhdr_t; +typedef unsigned int snd_util_unit_t; + +/* + * memory block + */ +struct snd_util_memblk { + snd_util_unit_t size; /* size of this block */ + snd_util_unit_t offset; /* zero-offset of this block */ + struct list_head list; /* link */ +}; + +#define snd_util_memblk_argptr(blk) (void*)((char*)(blk) + sizeof(snd_util_memblk_t)) + +/* + * memory management information + */ +struct snd_util_memhdr { + snd_util_unit_t size; /* size of whole data */ + struct list_head block; /* block linked-list header */ + int nblocks; /* # of allocated blocks */ + snd_util_unit_t used; /* used memory size */ + int block_extra_size; /* extra data size of chunk */ + struct semaphore block_mutex; /* lock */ +}; + +/* + * prototypes + */ +snd_util_memhdr_t *snd_util_memhdr_new(int memsize); +void snd_util_memhdr_free(snd_util_memhdr_t *hdr); +snd_util_memblk_t *snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size); +int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk); +int snd_util_mem_avail(snd_util_memhdr_t *hdr); + +/* functions without mutex */ +snd_util_memblk_t *__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size); +void __snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk); +snd_util_memblk_t *__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, struct list_head *prev); + +#endif /* __SOUND_UTIL_MEM_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/version.h linux/include/sound/version.h --- linux-2.4.21-rc1.orig/include/sound/version.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/version.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,3 @@ +/* include/version.h. Generated by configure. */ +#define CONFIG_SND_VERSION "0.9.2" +#define CONFIG_SND_DATE "" diff -urN linux-2.4.21-rc1.orig/include/sound/wavefront.h linux/include/sound/wavefront.h --- linux-2.4.21-rc1.orig/include/sound/wavefront.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/wavefront.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,695 @@ +#ifndef __SOUND_WAVEFRONT_H__ +#define __SOUND_WAVEFRONT_H__ + +/* + * Driver for Turtle Beach Wavefront cards (Maui,Tropez,Tropez+) + * + * Copyright (c) by Paul Barton-Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if (!defined(__GNUC__) && !defined(__GNUG__)) + + You will not be able to compile this file correctly without gcc, because + it is necessary to pack the "wavefront_alias" structure to a size + of 22 bytes, corresponding to 16-bit alignment (as would have been + the case on the original platform, MS-DOS). If this is not done, + then WavePatch-format files cannot be read/written correctly. + The method used to do this here ("__attribute__((packed)") is + completely compiler dependent. + + All other wavefront_* types end up aligned to 32 bit values and + still have the same (correct) size. + +#else + + /* However, note that as of G++ 2.7.3.2, g++ was unable to + correctly parse *type* __attribute__ tags. It will do the + right thing if we use the "packed" attribute on each struct + member, which has the same semantics anyway. + */ + +#endif /* __GNUC__ */ + +/***************************** WARNING ******************************** + PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO + BE USED WITH EITHER C *OR* C++. + **********************************************************************/ + +#ifndef NUM_MIDIKEYS +#define NUM_MIDIKEYS 128 +#endif /* NUM_MIDIKEYS */ + +#ifndef NUM_MIDICHANNELS +#define NUM_MIDICHANNELS 16 +#endif /* NUM_MIDICHANNELS */ + +/* These are very useful/important. the original wavefront interface + was developed on a 16 bit system, where sizeof(int) = 2 + bytes. Defining things like this makes the code much more portable, and + easier to understand without having to toggle back and forth + between a 16-bit view of the world and a 32-bit one. + */ + +#ifndef __KERNEL__ +/* keep them for compatibility */ +typedef short s16; +typedef unsigned short u16; +typedef int s32; +typedef unsigned int u32; +typedef char s8; +typedef unsigned char u8; +typedef s16 INT16; +typedef u16 UINT16; +typedef s32 INT32; +typedef u32 UINT32; +typedef s8 CHAR8; +typedef u8 UCHAR8; +#endif + +/* Pseudo-commands not part of the WaveFront command set. + These are used for various driver controls and direct + hardware control. + */ + +#define WFC_DEBUG_DRIVER 0 +#define WFC_FX_IOCTL 1 +#define WFC_PATCH_STATUS 2 +#define WFC_PROGRAM_STATUS 3 +#define WFC_SAMPLE_STATUS 4 +#define WFC_DISABLE_INTERRUPTS 5 +#define WFC_ENABLE_INTERRUPTS 6 +#define WFC_INTERRUPT_STATUS 7 +#define WFC_ROMSAMPLES_RDONLY 8 +#define WFC_IDENTIFY_SLOT_TYPE 9 + +/* Wavefront synth commands + */ + +#define WFC_DOWNLOAD_SAMPLE 0x80 +#define WFC_DOWNLOAD_BLOCK 0x81 +#define WFC_DOWNLOAD_MULTISAMPLE 0x82 +#define WFC_DOWNLOAD_SAMPLE_ALIAS 0x83 +#define WFC_DELETE_SAMPLE 0x84 +#define WFC_REPORT_FREE_MEMORY 0x85 +#define WFC_DOWNLOAD_PATCH 0x86 +#define WFC_DOWNLOAD_PROGRAM 0x87 +#define WFC_SET_SYNTHVOL 0x89 +#define WFC_SET_NVOICES 0x8B +#define WFC_DOWNLOAD_DRUM 0x90 +#define WFC_GET_SYNTHVOL 0x92 +#define WFC_GET_NVOICES 0x94 +#define WFC_DISABLE_CHANNEL 0x9A +#define WFC_ENABLE_CHANNEL 0x9B +#define WFC_MISYNTH_OFF 0x9D +#define WFC_MISYNTH_ON 0x9E +#define WFC_FIRMWARE_VERSION 0x9F +#define WFC_GET_NSAMPLES 0xA0 +#define WFC_DISABLE_DRUM_PROGRAM 0xA2 +#define WFC_UPLOAD_PATCH 0xA3 +#define WFC_UPLOAD_PROGRAM 0xA4 +#define WFC_SET_TUNING 0xA6 +#define WFC_GET_TUNING 0xA7 +#define WFC_VMIDI_ON 0xA8 +#define WFC_VMIDI_OFF 0xA9 +#define WFC_MIDI_STATUS 0xAA +#define WFC_GET_CHANNEL_STATUS 0xAB +#define WFC_DOWNLOAD_SAMPLE_HEADER 0xAC +#define WFC_UPLOAD_SAMPLE_HEADER 0xAD +#define WFC_UPLOAD_MULTISAMPLE 0xAE +#define WFC_UPLOAD_SAMPLE_ALIAS 0xAF +#define WFC_IDENTIFY_SAMPLE_TYPE 0xB0 +#define WFC_DOWNLOAD_EDRUM_PROGRAM 0xB1 +#define WFC_UPLOAD_EDRUM_PROGRAM 0xB2 +#define WFC_SET_EDRUM_CHANNEL 0xB3 +#define WFC_INSTOUT_LEVELS 0xB4 +#define WFC_PEAKOUT_LEVELS 0xB5 +#define WFC_REPORT_CHANNEL_PROGRAMS 0xB6 +#define WFC_HARDWARE_VERSION 0xCF +#define WFC_UPLOAD_SAMPLE_PARAMS 0xD7 +#define WFC_DOWNLOAD_OS 0xF1 +#define WFC_NOOP 0xFF + +#define WF_MAX_SAMPLE 512 +#define WF_MAX_PATCH 256 +#define WF_MAX_PROGRAM 128 + +#define WF_SECTION_MAX 44 /* longest OS section length */ + +/* # of bytes we send to the board when sending it various kinds of + substantive data, such as samples, patches and programs. +*/ + +#define WF_PROGRAM_BYTES 32 +#define WF_PATCH_BYTES 132 +#define WF_SAMPLE_BYTES 27 +#define WF_SAMPLE_HDR_BYTES 25 +#define WF_ALIAS_BYTES 25 +#define WF_DRUM_BYTES 9 +#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */ + +#define WF_ACK 0x80 +#define WF_DMA_ACK 0x81 + +/* OR-values for MIDI status bits */ + +#define WF_MIDI_VIRTUAL_ENABLED 0x1 +#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2 +#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4 + +/* slot indexes for struct address_info: makes code a little more mnemonic */ + +#define WF_SYNTH_SLOT 0 +#define WF_INTERNAL_MIDI_SLOT 1 +#define WF_EXTERNAL_MIDI_SLOT 2 + +/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401 + emulation. Note these NEVER show up in output from the device and + should NEVER be used in input unless Virtual MIDI mode has been + disabled. If they do show up as input, the results are unpredictable. +*/ + +#define WF_EXTERNAL_SWITCH 0xFD +#define WF_INTERNAL_SWITCH 0xF9 + +/* Debugging flags */ + +#define WF_DEBUG_CMD 0x1 +#define WF_DEBUG_DATA 0x2 +#define WF_DEBUG_LOAD_PATCH 0x4 +#define WF_DEBUG_IO 0x8 + +/* WavePatch file format stuff */ + +#define WF_WAVEPATCH_VERSION 120; /* Current version number (1.2) */ +#define WF_MAX_COMMENT 64 /* Comment length */ +#define WF_NUM_LAYERS 4 +#define WF_NAME_LENGTH 32 +#define WF_SOURCE_LENGTH 260 + +#define BankFileID "Bank" +#define DrumkitFileID "DrumKit" +#define ProgramFileID "Program" + +struct wf_envelope +{ + u8 attack_time:7; + u8 Unused1:1; + + u8 decay1_time:7; + u8 Unused2:1; + + u8 decay2_time:7; + u8 Unused3:1; + + u8 sustain_time:7; + u8 Unused4:1; + + u8 release_time:7; + u8 Unused5:1; + + u8 release2_time:7; + u8 Unused6:1; + + s8 attack_level; + s8 decay1_level; + s8 decay2_level; + s8 sustain_level; + s8 release_level; + + u8 attack_velocity:7; + u8 Unused7:1; + + u8 volume_velocity:7; + u8 Unused8:1; + + u8 keyboard_scaling:7; + u8 Unused9:1; +}; +typedef struct wf_envelope wavefront_envelope; + +struct wf_lfo +{ + u8 sample_number; + + u8 frequency:7; + u8 Unused1:1; + + u8 am_src:4; + u8 fm_src:4; + + s8 fm_amount; + s8 am_amount; + s8 start_level; + s8 end_level; + + u8 ramp_delay:7; + u8 wave_restart:1; /* for LFO2 only */ + + u8 ramp_time:7; + u8 Unused2:1; +}; +typedef struct wf_lfo wavefront_lfo; + +struct wf_patch +{ + s16 frequency_bias; /* ** THIS IS IN MOTOROLA FORMAT!! ** */ + + u8 amplitude_bias:7; + u8 Unused1:1; + + u8 portamento:7; + u8 Unused2:1; + + u8 sample_number; + + u8 pitch_bend:4; + u8 sample_msb:1; + u8 Unused3:3; + + u8 mono:1; + u8 retrigger:1; + u8 nohold:1; + u8 restart:1; + u8 filterconfig:2; /* SDK says "not used" */ + u8 reuse:1; + u8 reset_lfo:1; + + u8 fm_src2:4; + u8 fm_src1:4; + + s8 fm_amount1; + s8 fm_amount2; + + u8 am_src:4; + u8 Unused4:4; + + s8 am_amount; + + u8 fc1_mode:4; + u8 fc2_mode:4; + + s8 fc1_mod_amount; + s8 fc1_keyboard_scaling; + s8 fc1_bias; + s8 fc2_mod_amount; + s8 fc2_keyboard_scaling; + s8 fc2_bias; + + u8 randomizer:7; + u8 Unused5:1; + + struct wf_envelope envelope1; + struct wf_envelope envelope2; + struct wf_lfo lfo1; + struct wf_lfo lfo2; +}; +typedef struct wf_patch wavefront_patch; + +struct wf_layer +{ + u8 patch_number; + + u8 mix_level:7; + u8 mute:1; + + u8 split_point:7; + u8 play_below:1; + + u8 pan_mod_src:2; + u8 pan_or_mod:1; + u8 pan:4; + u8 split_type:1; +}; +typedef struct wf_layer wavefront_layer; + +struct wf_program +{ + struct wf_layer layer[WF_NUM_LAYERS]; +}; +typedef struct wf_program wavefront_program; + +struct wf_sample_offset +{ + s32 Fraction:4; + s32 Integer:20; + s32 Unused:8; +}; +typedef struct wf_sample_offset wavefront_sample_offset; + +/* Sample slot types */ + +#define WF_ST_SAMPLE 0 +#define WF_ST_MULTISAMPLE 1 +#define WF_ST_ALIAS 2 +#define WF_ST_EMPTY 3 + +/* pseudo's */ + +#define WF_ST_DRUM 4 +#define WF_ST_PROGRAM 5 +#define WF_ST_PATCH 6 +#define WF_ST_SAMPLEHDR 7 + +#define WF_ST_MASK 0xf + +/* Flags for slot status. These occupy the upper bits of the same byte + as a sample type. +*/ + +#define WF_SLOT_USED 0x80 /* XXX don't rely on this being accurate */ +#define WF_SLOT_FILLED 0x40 +#define WF_SLOT_ROM 0x20 + +#define WF_SLOT_MASK 0xf0 + +/* channel constants */ + +#define WF_CH_MONO 0 +#define WF_CH_LEFT 1 +#define WF_CH_RIGHT 2 + +/* Sample formats */ + +#define LINEAR_16BIT 0 +#define WHITE_NOISE 1 +#define LINEAR_8BIT 2 +#define MULAW_8BIT 3 + +#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2) + + +/* + + Because most/all of the sample data we pass in via pointers has + never been copied (just mmap-ed into user space straight from the + disk), it would be nice to allow handling of multi-channel sample + data without forcing user-level extraction of the relevant bytes. + + So, we need a way of specifying which channel to use (the WaveFront + only handles mono samples in a given slot), and the only way to do + this without using some struct other than wavefront_sample as the + interface is the awful hack of using the unused bits in a + wavefront_sample: + + Val Meaning + --- ------- + 0 no channel selection (use channel 1, sample is MONO) + 1 use first channel, and skip one + 2 use second channel, and skip one + 3 use third channel, and skip two + 4 use fourth channel, skip three + 5 use fifth channel, skip four + 6 use six channel, skip five + + + This can handle up to 4 channels, and anyone downloading >4 channels + of sample data just to select one of them needs to find some tools + like sox ... + + NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is + important. + +*/ + +#define WF_SET_CHANNEL(samp,chn) \ + (samp)->Unused1 = chn & 0x1; \ + (samp)->Unused2 = chn & 0x2; \ + (samp)->Unused3 = chn & 0x4 + +#define WF_GET_CHANNEL(samp) \ + (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1) + +typedef struct wf_sample { + struct wf_sample_offset sampleStartOffset; + struct wf_sample_offset loopStartOffset; + struct wf_sample_offset loopEndOffset; + struct wf_sample_offset sampleEndOffset; + s16 FrequencyBias; + u8 SampleResolution:2; /* sample_format */ + u8 Unused1:1; + u8 Loop:1; + u8 Bidirectional:1; + u8 Unused2:1; + u8 Reverse:1; + u8 Unused3:1; +} wavefront_sample; + +typedef struct wf_multisample { + s16 NumberOfSamples; /* log2 of the number of samples */ + s16 SampleNumber[NUM_MIDIKEYS]; +} wavefront_multisample; + +typedef struct wf_alias { + s16 OriginalSample __attribute__ ((packed)); + + struct wf_sample_offset sampleStartOffset __attribute__ ((packed)); + struct wf_sample_offset loopStartOffset __attribute__ ((packed)); + struct wf_sample_offset sampleEndOffset __attribute__ ((packed)); + struct wf_sample_offset loopEndOffset __attribute__ ((packed)); + + s16 FrequencyBias __attribute__ ((packed)); + + u8 SampleResolution:2 __attribute__ ((packed)); + u8 Unused1:1 __attribute__ ((packed)); + u8 Loop:1 __attribute__ ((packed)); + u8 Bidirectional:1 __attribute__ ((packed)); + u8 Unused2:1 __attribute__ ((packed)); + u8 Reverse:1 __attribute__ ((packed)); + u8 Unused3:1 __attribute__ ((packed)); + + /* This structure is meant to be padded only to 16 bits on their + original. Of course, whoever wrote their documentation didn't + realize that sizeof(struct) can be >= + sum(sizeof(struct-fields)) and so thought that giving a C level + description of the structs used in WavePatch files was + sufficient. I suppose it was, as long as you remember the + standard 16->32 bit issues. + */ + + u8 sixteen_bit_padding __attribute__ ((packed)); +} wavefront_alias; + +typedef struct wf_drum { + u8 PatchNumber; + u8 MixLevel:7; + u8 Unmute:1; + u8 Group:4; + u8 Unused1:4; + u8 PanModSource:2; + u8 PanModulated:1; + u8 PanAmount:4; + u8 Unused2:1; +} wavefront_drum; + +typedef struct wf_drumkit { + struct wf_drum drum[NUM_MIDIKEYS]; +} wavefront_drumkit; + +typedef struct wf_channel_programs { + u8 Program[NUM_MIDICHANNELS]; +} wavefront_channel_programs; + +/* How to get MIDI channel status from the data returned by + a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs) +*/ + +#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7)) + +typedef union wf_any { + wavefront_sample s; + wavefront_multisample ms; + wavefront_alias a; + wavefront_program pr; + wavefront_patch p; + wavefront_drum d; +} wavefront_any; + +/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h + might work for other wave-table based patch loading situations. + Alas, his fears were correct. The WaveFront doesn't even come with + just "patches", but several different kind of structures that + control the sound generation process. + */ + +typedef struct wf_patch_info { + + /* the first two fields are used by the OSS "patch loading" interface + only, and are unused by the current user-level library. + */ + + s16 key; /* Use WAVEFRONT_PATCH here */ + u16 devno; /* fill in when sending */ + u8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */ + +#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999 + + u16 number; /* patch/sample/prog number */ + + u32 size; /* size of any data included in + one of the fields in `hdrptr', or + as `dataptr'. + + NOTE: for actual samples, this is + the size of the *SELECTED CHANNEL* + even if more data is actually available. + + So, a stereo sample (2 channels) of + 6000 bytes total has `size' = 3000. + + See the macros and comments for + WF_{GET,SET}_CHANNEL above. + + */ + wavefront_any *hdrptr; /* user-space ptr to hdr bytes */ + u16 *dataptr; /* actual sample data */ + + wavefront_any hdr; /* kernel-space copy of hdr bytes */ +} wavefront_patch_info; + +/* The maximum number of bytes we will ever move to or from user space + in response to a WFC_* command. This obviously doesn't cover + actual sample data. +*/ + +#define WF_MAX_READ sizeof(wavefront_multisample) +#define WF_MAX_WRITE sizeof(wavefront_multisample) + +/* + This allows us to execute any WF command except the download/upload + ones, which are handled differently due to copyin/copyout issues as + well as data-nybbling to/from the card. + */ + +typedef struct wavefront_control { + int cmd; /* WFC_* */ + char status; /* return status to user-space */ + unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */ + unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */ +} wavefront_control; + +#define WFCTL_WFCMD 0x1 +#define WFCTL_LOAD_SPP 0x2 + +/* Modulator table */ + +#define WF_MOD_LFO1 0 +#define WF_MOD_LFO2 1 +#define WF_MOD_ENV1 2 +#define WF_MOD_ENV2 3 +#define WF_MOD_KEYBOARD 4 +#define WF_MOD_LOGKEY 5 +#define WF_MOD_VELOCITY 6 +#define WF_MOD_LOGVEL 7 +#define WF_MOD_RANDOM 8 +#define WF_MOD_PRESSURE 9 +#define WF_MOD_MOD_WHEEL 10 +#define WF_MOD_1 WF_MOD_MOD_WHEEL +#define WF_MOD_BREATH 11 +#define WF_MOD_2 WF_MOD_BREATH +#define WF_MOD_FOOT 12 +#define WF_MOD_4 WF_MOD_FOOT +#define WF_MOD_VOLUME 13 +#define WF_MOD_7 WF_MOD_VOLUME +#define WF_MOD_PAN 14 +#define WF_MOD_10 WF_MOD_PAN +#define WF_MOD_EXPR 15 +#define WF_MOD_11 WF_MOD_EXPR + +/* FX-related material */ + +typedef struct wf_fx_info { + int request; /* see list below */ + long data[4]; /* we don't need much */ +} wavefront_fx_info; + +/* support for each of these will be forthcoming once I or someone + else has figured out which of the addresses on page 6 and page 7 of + the YSS225 control each parameter. Incidentally, these come from + the Windows driver interface, but again, Turtle Beach didn't + document the API to use them. +*/ + +#define WFFX_SETOUTGAIN 0 +#define WFFX_SETSTEREOOUTGAIN 1 +#define WFFX_SETREVERBIN1GAIN 2 +#define WFFX_SETREVERBIN2GAIN 3 +#define WFFX_SETREVERBIN3GAIN 4 +#define WFFX_SETCHORUSINPORT 5 +#define WFFX_SETREVERBIN1PORT 6 +#define WFFX_SETREVERBIN2PORT 7 +#define WFFX_SETREVERBIN3PORT 8 +#define WFFX_SETEFFECTPORT 9 +#define WFFX_SETAUXPORT 10 +#define WFFX_SETREVERBTYPE 11 +#define WFFX_SETREVERBDELAY 12 +#define WFFX_SETCHORUSLFO 13 +#define WFFX_SETCHORUSPMD 14 +#define WFFX_SETCHORUSAMD 15 +#define WFFX_SETEFFECT 16 +#define WFFX_SETBASEALL 17 +#define WFFX_SETREVERBALL 18 +#define WFFX_SETCHORUSALL 20 +#define WFFX_SETREVERBDEF 22 +#define WFFX_SETCHORUSDEF 23 +#define WFFX_DELAYSETINGAIN 24 +#define WFFX_DELAYSETFBGAIN 25 +#define WFFX_DELAYSETFBLPF 26 +#define WFFX_DELAYSETGAIN 27 +#define WFFX_DELAYSETTIME 28 +#define WFFX_DELAYSETFBTIME 29 +#define WFFX_DELAYSETALL 30 +#define WFFX_DELAYSETDEF 32 +#define WFFX_SDELAYSETINGAIN 33 +#define WFFX_SDELAYSETFBGAIN 34 +#define WFFX_SDELAYSETFBLPF 35 +#define WFFX_SDELAYSETGAIN 36 +#define WFFX_SDELAYSETTIME 37 +#define WFFX_SDELAYSETFBTIME 38 +#define WFFX_SDELAYSETALL 39 +#define WFFX_SDELAYSETDEF 41 +#define WFFX_DEQSETINGAIN 42 +#define WFFX_DEQSETFILTER 43 +#define WFFX_DEQSETALL 44 +#define WFFX_DEQSETDEF 46 +#define WFFX_MUTE 47 +#define WFFX_FLANGESETBALANCE 48 +#define WFFX_FLANGESETDELAY 49 +#define WFFX_FLANGESETDWFFX_TH 50 +#define WFFX_FLANGESETFBGAIN 51 +#define WFFX_FLANGESETINGAIN 52 +#define WFFX_FLANGESETLFO 53 +#define WFFX_FLANGESETALL 54 +#define WFFX_FLANGESETDEF 56 +#define WFFX_PITCHSETSHIFT 57 +#define WFFX_PITCHSETBALANCE 58 +#define WFFX_PITCHSETALL 59 +#define WFFX_PITCHSETDEF 61 +#define WFFX_SRSSETINGAIN 62 +#define WFFX_SRSSETSPACE 63 +#define WFFX_SRSSETCENTER 64 +#define WFFX_SRSSETGAIN 65 +#define WFFX_SRSSETMODE 66 +#define WFFX_SRSSETDEF 68 + +/* Allow direct user-space control over FX memory/coefficient data. + In theory this could be used to download the FX microprogram, + but it would be a little slower, and involve some wierd code. + */ + +#define WFFX_MEMSET 69 + +#endif /* __SOUND_WAVEFRONT_H__ */ diff -urN linux-2.4.21-rc1.orig/include/sound/wavefront_fx.h linux/include/sound/wavefront_fx.h --- linux-2.4.21-rc1.orig/include/sound/wavefront_fx.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/wavefront_fx.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,9 @@ +#ifndef __SOUND_WAVEFRONT_FX_H +#define __SOUND_WAVEFRONT_FX_H + +extern int snd_wavefront_fx_detect (snd_wavefront_t *); +extern void snd_wavefront_fx_ioctl (snd_synth_t *sdev, + unsigned int cmd, + unsigned long arg); + +#endif __SOUND_WAVEFRONT_FX_H diff -urN linux-2.4.21-rc1.orig/include/sound/ymfpci.h linux/include/sound/ymfpci.h --- linux-2.4.21-rc1.orig/include/sound/ymfpci.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/ymfpci.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,372 @@ +#ifndef __SOUND_YMFPCI_H +#define __SOUND_YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "ac97_codec.h" + +#ifndef PCI_VENDOR_ID_YAMAHA +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_724 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_724F +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_740 +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_740C +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_744 +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_754 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 +#endif + +/* + * Direct registers + */ + +#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_ZVOUTVOL 0x0088 +#define YDSXGR_ZVOUTVOLL 0x0088 +#define YDSXGR_ZVOUTVOLR 0x008A +#define YDSXGR_SECADCOUTVOL 0x008C +#define YDSXGR_SECADCOUTVOLL 0x008C +#define YDSXGR_SECADCOUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_ZVLOOPVOL 0x009C +#define YDSXGR_ZVLOOPVOLL 0x009E +#define YDSXGR_ZVLOOPVOLR 0x009E +#define YDSXGR_SECADCLOOPVOL 0x00A0 +#define YDSXGR_SECADCLOOPVOLL 0x00A0 +#define YDSXGR_SECADCLOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL 0x00B8 +#define YDSXGR_SPDIFOUTVOLL 0x00B8 +#define YDSXGR_SPDIFOUTVOLR 0x00BA +#define YDSXGR_SPDIFLOOPVOL 0x00BC +#define YDSXGR_SPDIFLOOPVOLL 0x00BC +#define YDSXGR_SPDIFLOOPVOLR 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_DSXG_LEGACY 0x40 +#define PCIR_DSXG_ELEGACY 0x42 +#define PCIR_DSXG_CTRL 0x48 +#define PCIR_DSXG_PWRCTRL1 0x4a +#define PCIR_DSXG_PWRCTRL2 0x4e +#define PCIR_DSXG_FMBASE 0x60 +#define PCIR_DSXG_SBBASE 0x62 +#define PCIR_DSXG_MPU401BASE 0x64 +#define PCIR_DSXG_JOYBASE 0x66 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +/* + * + */ + +typedef struct _snd_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; + u32 num_of_frames; + u32 loop_count; + u32 start; + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; +} snd_ymfpci_playback_bank_t; + +typedef struct _snd_ymfpci_capture_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +} snd_ymfpci_capture_bank_t; + +typedef struct _snd_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +} snd_ymfpci_effect_bank_t; + +typedef struct _snd_ymfpci_voice ymfpci_voice_t; +typedef struct _snd_ymfpci_pcm ymfpci_pcm_t; +typedef struct _snd_ymfpci ymfpci_t; + +typedef enum { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +} ymfpci_voice_type_t; + +struct _snd_ymfpci_voice { + ymfpci_t *chip; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + snd_ymfpci_playback_bank_t *bank; + dma_addr_t bank_addr; + void (*interrupt)(ymfpci_t *chip, ymfpci_voice_t *voice); + ymfpci_pcm_t *ypcm; +}; + +typedef enum { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +} snd_ymfpci_pcm_type_t; + +struct _snd_ymfpci_pcm { + ymfpci_t *chip; + snd_ymfpci_pcm_type_t type; + snd_pcm_substream_t *substream; + ymfpci_voice_t *voices[2]; /* playback only */ + int running: 1; + int output_front: 1; + int output_rear: 1; + u32 period_size; /* cached from runtime->period_size */ + u32 buffer_size; /* cached from runtime->buffer_size */ + u32 period_pos; + u32 last_pos; + u32 capture_bank_number; + u32 shift; +}; + +struct _snd_ymfpci { + int irq; + + unsigned int device_id; /* PCI device ID */ + unsigned int rev; /* PCI revision */ + unsigned long reg_area_phys; + unsigned long reg_area_virt; + struct resource *res_reg_area; + + unsigned short old_legacy_ctrl; + unsigned int joystick_port; + + void *work_ptr; + dma_addr_t work_ptr_addr; + unsigned long work_ptr_size; + + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int work_size; + + void *bank_base_playback; + void *bank_base_capture; + void *bank_base_effect; + void *work_base; + dma_addr_t bank_base_playback_addr; + dma_addr_t bank_base_capture_addr; + dma_addr_t bank_base_effect_addr; + dma_addr_t work_base_addr; + void *ac3_tmp_base; + dma_addr_t ac3_tmp_base_addr; + + u32 *ctrl_playback; + snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + snd_ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; + snd_ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + + u32 active_bank; + ymfpci_voice_t voices[64]; + + ac97_t *ac97; + snd_rawmidi_t *rawmidi; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm2; + snd_pcm_t *pcm_spdif; + snd_pcm_t *pcm_4ch; + snd_pcm_substream_t *capture_substream[YDSXG_CAPTURE_VOICES]; + snd_pcm_substream_t *effect_substream[YDSXG_EFFECT_VOICES]; + snd_kcontrol_t *ctl_vol_recsrc; + snd_kcontrol_t *ctl_vol_adcrec; + snd_kcontrol_t *ctl_vol_spdifrec; + unsigned short spdif_bits, spdif_pcm_bits; + snd_kcontrol_t *spdif_pcm_ctl; + int mode_dup4ch; + int rear_opened; + int spdif_opened; + + spinlock_t reg_lock; + spinlock_t voice_lock; + wait_queue_head_t interrupt_sleep; + atomic_t interrupt_sleep_count; + snd_info_entry_t *proc_entry; + +#ifdef CONFIG_PM + u32 *saved_regs; + u32 saved_ydsxgr_mode; +#endif +}; + +int snd_ymfpci_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short old_legacy_ctrl, + ymfpci_t ** rcodec); + +int snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch); +int snd_ymfpci_joystick(ymfpci_t *chip); + +int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice); +int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice); + +#ifdef CONFIG_PM +void snd_ymfpci_suspend(ymfpci_t *chip); +void snd_ymfpci_resume(ymfpci_t *chip); +#endif + +#endif /* __SOUND_YMFPCI_H */ diff -urN linux-2.4.21-rc1.orig/include/sound/yss225.h linux/include/sound/yss225.h --- linux-2.4.21-rc1.orig/include/sound/yss225.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/include/sound/yss225.h 2003-04-29 04:59:51.000000000 -0600 @@ -0,0 +1,23 @@ +#ifndef __SOUND_YSS225_H +#define __SOUND_YSS225_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 /* __SOUND_YSS225_H */ diff -urN linux-2.4.21-rc1.orig/sound/Config.in linux/sound/Config.in --- linux-2.4.21-rc1.orig/sound/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/Config.in 2003-04-29 23:28:49.000000000 -0600 @@ -0,0 +1,50 @@ +# +# OSS (Open Sound System) and ALSA (Advanced Linux Sound Architecture) +# +# sound/Config.in +# + +mainmenu_option next_comment +comment 'Sound support' +bool 'Sound support' CONFIG_SOUND_SUPPORT +if [ "$CONFIG_SOUND_SUPPORT" != "n" ]; then + + + mainmenu_option next_comment + comment 'Open Sound System (OSS) - (obsolete)' + tristate 'Open Sound System (OSS) - (obsolete)' CONFIG_SOUND + if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in + fi + endmenu + + mainmenu_option next_comment + comment 'Advanced Linux Sound Architecture (ALSA)' + tristate 'Advanced Linux Sound Architecture (ALSA)' CONFIG_SND + + if [ "$CONFIG_SND" != "n" ]; then + source sound/core/Config.in + source sound/drivers/Config.in + if [ "$CONFIG_ISA" = "y" ]; then + source sound/isa/Config.in + fi + if [ "$CONFIG_PCI" = "y" ]; then + source sound/pci/Config.in + fi + if [ "$CONFIG_PPC" = "y" ]; then + source sound/ppc/Config.in + fi + if [ "$CONFIG_ARM" = "y" ]; then + source sound/arm/Config.in + fi + if [ "$CONFIG_USB" != "n" ]; then + source sound/usb/Config.in + fi + + fi + + endmenu + +fi + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/Makefile linux/sound/Makefile --- linux-2.4.21-rc1.orig/sound/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/Makefile 2003-04-30 00:13:17.000000000 -0600 @@ -0,0 +1,64 @@ +# Makefile for the Linux sound card driver +# + +O_TARGET := sound.o + +export-objs := sound_core.o + +obj-$(CONFIG_SND) += soundcore.o + +_subdirs := core i2c drivers isa pci ppc arm synth usb sparc + +subdir-$(CONFIG_SND) += $(_subdirs) + +ifeq ($(CONFIG_SND),y) + subdir-m += $(_subdirs) + obj-y += arm/arm.o + obj-y += core/core.o \ + core/seq/sq.o \ + core/seq/instr/instr.o \ + core/oss/oss.o \ + core/ioctl32/_ioctl32.o + obj-y += drivers/drivers.o \ + drivers/mpu401/_mpu401.o \ + drivers/opl3/_opl3.o + obj-y += i2c/_i2c.o + obj-y += isa/isa.o \ + isa/ad1816a/_ad1816a.o \ + isa/ad1848/_ad1848.o \ + isa/cs423x/_cs423x.o \ + isa/es1688/_es1688.o \ + isa/gus/_gus.o \ + isa/opti9xx/_opti9xx.o \ + isa/sb/_sb.o \ + isa/wavefront/_wavefront.o + obj-y += pci/pci.o \ + pci/ac97/_ac97.o \ + pci/ali5451/_ali5451.o \ + pci/cs46xx/_cs46xx.o \ + pci/emu10k1/_emu10k1.o \ + pci/ice1712/_ice1712.o \ + pci/korg1212/_korg1212.o \ + pci/nm256/_nm256.o \ + pci/rme9652/_rme9652.o \ + pci/trident/_trident.o \ + pci/ymfpci/_ymfpci.o + obj-y += ppc/ppc.o + obj-y += sparc/sparc.o + obj-y += synth/synth.o \ + synth/emux/_emux.o + obj-y += usb/_usbaudio.o + obj-y += last.o +endif + + +list-multi := soundcore.o + +soundcore-objs := sound_core.o sound_firmware.o + + +include $(TOPDIR)/Rules.make + + +soundcore.o: $(soundcore-objs) + $(LD) -r -o $@ $(soundcore-objs) diff -urN linux-2.4.21-rc1.orig/sound/arm/Config.in linux/sound/arm/Config.in --- linux-2.4.21-rc1.orig/sound/arm/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/arm/Config.in 2003-04-29 05:02:23.000000000 -0600 @@ -0,0 +1,10 @@ +# ALSA ARM drivers + +mainmenu_option next_comment +comment 'ALSA ARM devices' + +if [ "$CONFIG_ARCH_SA1100" = "y" ]; then + dep_tristate 'SA11xx UDA1341TS driver (H3600)' CONFIG_SND_SA11XX_UDA1341 $CONFIG_SND $CONFIG_L3 +fi + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/arm/Makefile linux/sound/arm/Makefile --- linux-2.4.21-rc1.orig/sound/arm/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/arm/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,17 @@ +# +# Makefile for ALSA +# + +O_TARGET := arm.o + +list-multi := snd-sa11xx-uda1341.o + +snd-sa11xx-uda1341-objs := sa11xx-uda1341.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o + +include $(TOPDIR)/Rules.make + +snd-sa11xx-uda1341.o: $(snd-sa11xx-uda1341-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sa11xx-uda1341-objs) diff -urN linux-2.4.21-rc1.orig/sound/arm/sa11xx-uda1341.c linux/sound/arm/sa11xx-uda1341.c --- linux-2.4.21-rc1.orig/sound/arm/sa11xx-uda1341.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/arm/sa11xx-uda1341.c 2003-02-25 05:48:15.000000000 -0700 @@ -0,0 +1,1048 @@ +/* + * Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard + * Copyright (C) 2002 Tomas Kasparek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License. + * + * History: + * + * 2002-03-13 Tomas Kasparek initial release - based on h3600-uda1341.c from OSS + * 2002-03-20 Tomas Kasparek playback over ALSA is working + * 2002-03-28 Tomas Kasparek playback over OSS emulation is working + * 2002-03-29 Tomas Kasparek basic capture is working (native ALSA) + * 2002-03-29 Tomas Kasparek capture is working (OSS emulation) + * 2002-04-04 Tomas Kasparek better rates handling (allow non-standard rates) + * 2003-02-14 Brian Avery fixed full duplex mode, other updates + * 2003-02-20 Tomas Kasparek merged updates by Brian (except HAL) + */ + +/* sa11xx-uda1341.c,v 1.8 2003/02/25 12:48:15 perex Exp */ + +/*************************************************************************************************** +* +* To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai +* available in the Alsa doc section on the website +* +* A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100. +* We are using SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated +* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it. +* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the +* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which +* is a mem loc that always decodes to 0's w/ no off chip access. +* +* Some alsa terminology: +* frame => num_channels * sample_size e.g stereo 16 bit is 2 * 16 = 32 bytes +* period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte +* buffer and 4 periods in the runtime structure this means we'll get an int every 256 +* bytes or 4 times per buffer. +* A number of the sizes are in frames rather than bytes, use frames_to_bytes and +* bytes_to_frames to convert. The easiest way to tell the units is to look at the +* type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t +* +* Notes about the pointer fxn: +* The pointer fxn needs to return the offset into the dma buffer in frames. +* Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts. +* +* Notes about pause/resume +* Implementing this would be complicated so it's skipped. The problem case is: +* A full duplex connection is going, then play is paused. At this point you need to start xmitting +* 0's to keep the record active which means you cant just freeze the dma and resume it later you'd +* need to save off the dma info, and restore it properly on a resume. Yeach! +* +* Notes about transfer methods: +* The async write calls fail. I probably need to implement something else to support them? +* +***************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#undef DEBUG_MODE +#undef DEBUG_FUNCTION_NAMES +#include + +/* {{{ Type definitions */ + +MODULE_AUTHOR("Tomas Kasparek "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{UDA1341,iPAQ H3600 UDA1341TS}}"); + +static char *id = NULL; /* ID for this card */ + +MODULE_PARM(id, "s"); +MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard."); + +#define chip_t sa11xx_uda1341_t + +typedef enum stream_id_t{ + PLAYBACK=0, + CAPTURE, + MAX_STREAMS, +}stream_id_t; + +typedef struct audio_stream { + char *id; /* identification string */ + int stream_id; /* numeric identification */ + dma_device_t dma_dev; /* device identifier for DMA */ + dma_regs_t *dma_regs; /* points to our DMA registers */ + + int active:1; /* we are using this stream for transfer now */ + + int sent_periods; /* # of sent periods from actual DMA buffer */ + int sent_total; /* # of sent periods total (just for info & debug) */ + + int sync; /* are we recoding - flag used to do DMA trans. for sync */ + spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */ + + snd_pcm_substream_t *stream; +}audio_stream_t; + +typedef struct snd_card_sa11xx_uda1341 { + struct pm_dev *pm_dev; + snd_card_t *card; + struct l3_client *uda1341; + + long samplerate; + audio_stream_t *s[MAX_STREAMS]; +}sa11xx_uda1341_t; + +static struct snd_card_sa11xx_uda1341 *sa11xx_uda1341 = NULL; + +static unsigned int rates[] = { + 8000, 10666, 10985, 14647, + 16000, 21970, 22050, 24000, + 29400, 32000, 44100, 48000, +}; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = RATES, + .list = rates, + .mask = 0, +}; + +/* }}} */ + +/* {{{ Clock and sample rate stuff */ + +/* + * Stop-gap solution until rest of hh.org HAL stuff is merged. + */ +#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12) +#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13) + +#ifdef CONFIG_SA1100_H3XXX +#define clr_sa11xx_uda1341_egpio(x) clr_h3600_egpio(x) +#define set_sa11xx_uda1341_egpio(x) set_h3600_egpio(x) +#else +#error This driver could serve H3x00 handhelds only! +#endif + +static void sa11xx_uda1341_set_audio_clock(long val) +{ + switch (val) { + case 24000: case 32000: case 48000: /* 00: 12.288 MHz */ + GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; + break; + + case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */ + GPSR = GPIO_H3600_CLK_SET0; + GPCR = GPIO_H3600_CLK_SET1; + break; + + case 8000: case 10666: case 16000: /* 10: 4.096 MHz */ + GPCR = GPIO_H3600_CLK_SET0; + GPSR = GPIO_H3600_CLK_SET1; + break; + + case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */ + GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; + break; + } +} + +static void sa11xx_uda1341_set_samplerate(sa11xx_uda1341_t *sa11xx_uda1341, long rate) +{ + int clk_div = 0; + int clk=0; + + DEBUG(KERN_DEBUG "set_samplerate rate: %ld\n", rate); + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * We have the following clock sources: + * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz + * Those can be divided either by 256, 384 or 512. + * This makes up 12 combinations for the following samplerates... + */ + if (rate >= 48000) + rate = 48000; + else if (rate >= 44100) + rate = 44100; + else if (rate >= 32000) + rate = 32000; + else if (rate >= 29400) + rate = 29400; + else if (rate >= 24000) + rate = 24000; + else if (rate >= 22050) + rate = 22050; + else if (rate >= 21970) + rate = 21970; + else if (rate >= 16000) + rate = 16000; + else if (rate >= 14647) + rate = 14647; + else if (rate >= 10985) + rate = 10985; + else if (rate >= 10666) + rate = 10666; + else + rate = 8000; + + /* Set the external clock generator */ + sa11xx_uda1341_set_audio_clock(rate); + + /* Select the clock divisor */ + switch (rate) { + case 8000: + case 10985: + case 22050: + case 24000: + clk = F512; + clk_div = SSCR0_SerClkDiv(16); + break; + case 16000: + case 21970: + case 44100: + case 48000: + clk = F256; + clk_div = SSCR0_SerClkDiv(8); + break; + case 10666: + case 14647: + case 29400: + case 32000: + clk = F384; + clk_div = SSCR0_SerClkDiv(12); + break; + } + + /* FMT setting should be moved away when other FMTs are added (FIXME) */ + l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16); + + l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk); + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; + DEBUG(KERN_DEBUG "set_samplerate done (new rate: %ld)\n", rate); + sa11xx_uda1341->samplerate = rate; +} + +/* }}} */ + +/* {{{ HW init and shutdown */ + +static void sa11xx_uda1341_audio_init(sa11xx_uda1341_t *sa11xx_uda1341) +{ + unsigned long flags; + + DEBUG_NAME(KERN_DEBUG "audio_init\n"); + + /* Setup DMA stuff */ + if (sa11xx_uda1341->s[PLAYBACK]) { + sa11xx_uda1341->s[PLAYBACK]->id = "UDA1341 out"; + sa11xx_uda1341->s[PLAYBACK]->stream_id = PLAYBACK; + sa11xx_uda1341->s[PLAYBACK]->dma_dev = DMA_Ser4SSPWr; + } + + if (sa11xx_uda1341->s[CAPTURE]) { + sa11xx_uda1341->s[CAPTURE]->id = "UDA1341 in"; + sa11xx_uda1341->s[CAPTURE]->stream_id = CAPTURE; + sa11xx_uda1341->s[CAPTURE]->dma_dev = DMA_Ser4SSPRd; + } + + /* Initialize the UDA1341 internal state */ + + /* Setup the uarts */ + local_irq_save(flags); + GAFR |= (GPIO_SSP_CLK); + GPDR &= ~(GPIO_SSP_CLK); + Ser4SSCR0 = 0; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + Ser4SSCR0 |= SSCR0_SSE; + local_irq_restore(flags); + + /* Enable the audio power */ + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); + set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); + set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); + + /* Initialize the UDA1341 internal state */ + l3_open(sa11xx_uda1341->uda1341); + + /* external clock configuration (after l3_open - regs must be + * initialized */ + sa11xx_uda1341_set_samplerate(sa11xx_uda1341, AUDIO_RATE_DEFAULT); + + /* Wait for the UDA1341 to wake up */ + set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); + mdelay(1); + + + /* make the left and right channels unswapped (flip the WS latch ) */ + Ser4SSDR = 0; + + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); +} + +static void sa11xx_uda1341_audio_shutdown(sa11xx_uda1341_t *sa11xx_uda1341) +{ + /* mute on */ + set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); + + /* disable the audio power and all signals leading to the audio chip */ + l3_close(sa11xx_uda1341->uda1341); + Ser4SSCR0 = 0; + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); + /* power off */ + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); + /* mute off */ + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); +} + +/* }}} */ + +/* {{{ DMA staff */ + +/* + * these are the address and sizes used to fill the xmit buffer + * so we can get a clock in record only mode + */ +#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS +#define FORCE_CLOCK_SIZE 4096 // was 2048 + +static void audio_dma_request(audio_stream_t *s, void (*callback)(void *)) +{ + int ret; + + DEBUG_NAME(KERN_DEBUG "audio_dma_request"); + + DEBUG("\t request id <%s>\n", s->id); + DEBUG("\t request dma_dev = 0x%x \n", s->dma_dev); + ret = sa1100_request_dma((s)->dma_dev, (s)->id, callback, s, &((s)->dma_regs)); + DEBUG("\t request ret = %d\n", ret); +} + +static void audio_dma_free(audio_stream_t *s) +{ + sa1100_free_dma((s)->dma_regs); + (s)->dma_regs = 0; +} + +static u_int audio_get_dma_pos(audio_stream_t *s) +{ + snd_pcm_substream_t * substream = s->stream; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int offset; + unsigned long flags; + + DEBUG_NAME(KERN_DEBUG "get_dma_pos"); + + // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel + spin_lock_irqsave(&s->dma_lock, flags); + offset = sa1100_get_dma_pos((s)->dma_regs) - runtime->dma_addr; + spin_unlock_irqrestore(&s->dma_lock, flags); + + DEBUG(" %d ->", offset); + offset = bytes_to_frames(runtime,offset); + DEBUG(" %d [fr]\n", offset); + + if (offset >= runtime->buffer_size){ + offset = runtime->buffer_size; + } + + DEBUG(KERN_DEBUG " hw_ptr_interrupt: %lX\n", + (unsigned long)runtime->hw_ptr_interrupt); + DEBUG(KERN_DEBUG " updated pos [fr]: %ld\n", + offset - (offset % runtime->min_align)); + + return offset; +} + +/* + * this stops the dma and clears the dma ptrs + */ +static void audio_stop_dma(audio_stream_t *s) +{ + long flags; + + DEBUG_NAME(KERN_DEBUG "stop_dma\n"); + + /* + * zero filling streams (sync=1) don;t have alsa streams attached + * but the 0 fill dma xfer still needs to be stopped + */ + if (!(s->stream || s->sync)) + return; + + spin_lock_irqsave(&(s->dma_lock), flags); + s->active = 0; + s->sent_periods = 0; + s->sent_total = 0; + s->sync = 0; + + /* this stops the dma channel and clears the buffer ptrs */ + sa1100_clear_dma((s)->dma_regs); + spin_unlock_irqrestore(&(s->dma_lock), flags); +} + +static void audio_reset(audio_stream_t *s) +{ + DEBUG_NAME(KERN_DEBUG "dma_reset\n"); + + if (s->stream) { + audio_stop_dma(s); + } + s->active = 0; +} + + +static void audio_process_dma(audio_stream_t *s) +{ + snd_pcm_substream_t * substream = s->stream; + snd_pcm_runtime_t *runtime; + int ret; + + DEBUG_NAME(KERN_DEBUG "process_dma\n"); + + /* we are requested to process synchronization DMA transfer */ + if(!s->active && s->sync){ + snd_assert(s->stream_id == PLAYBACK,return); + /* fill the xmit dma buffers and return */ + while (1) { + DEBUG(KERN_DEBUG "sent zero dma period (dma_size[B]: %d)\n", FORCE_CLOCK_SIZE); + ret = sa1100_start_dma((s)->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE); + if (ret) + return; + } + } + + /* must be set here - only valid for running streams, not for forced_clock dma fills */ + runtime = substream->runtime; + + DEBUG("audio_process_dma hw_ptr_base = 0x%x w_ptr_interrupt = 0x%x " + "period_size = %d periods = %d buffer_size = %d sync=0x%x dma_area = 0x%x\n", + runtime->hw_ptr_base, + runtime->hw_ptr_interrupt, + runtime->period_size, + runtime->periods, + runtime->buffer_size, + runtime->sync, + runtime->dma_area); + + DEBUG("audio_process_dma sent_total = %d sent_period = %d\n", + s->sent_total, + s->sent_periods); + + while(s->active) { + unsigned int dma_size; + unsigned int offset ; + + dma_size = frames_to_bytes(runtime,runtime->period_size) ; + offset = dma_size * s->sent_periods; + if (dma_size > MAX_DMA_SIZE){ + /* this should not happen! */ + printk(KERN_ERR "---> cut dma_size: %d -> ", dma_size); + dma_size = CUT_DMA_SIZE; + printk("%d <---\n", dma_size); + } + + /* + * the first time this while loop will run 3 times, i.e. it'll fill the 2 dma + * buffers then get a -EBUSY, every other time it'll refill the completed buffer + * and then get the -EBUSY so it'll just run twice + */ + ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size); + if (ret) + return; + + DEBUG(KERN_DEBUG "sent period %d (%d total)(dma_size[B]: %d" + "offset[B]: %06d)\n", + s->sent_periods, s->sent_total, dma_size, offset); + +#ifdef DEBUG_MODE + printk(KERN_DEBUG " dma_area:"); + for (i=0; i < 32; i++) { + printk(" %02x", *(char *)(runtime->dma_addr+offset+i)); + } + printk("\n"); +#endif + s->sent_total++; + s->sent_periods++; + s->sent_periods %= runtime->periods; + } +} + +static void audio_dma_callback(void *data) +{ + audio_stream_t *s = data; + char *buf; + int i; + + DEBUG_NAME(KERN_DEBUG "dma_callback\n"); + + DEBUG(KERN_DEBUG "----> period done <----\n"); + +#ifdef DEBUG_MODE + printk(KERN_DEBUG " dma_area:"); + buf = (char *)s->stream->runtime->dma_addr + ((s->sent_periods - 1 ) * + frames_to_bytes( s->stream->runtime, s->stream->runtime->period_size)); + for (i=0; i < 32; i++) { + printk(" %02x", *(char *)(buf + i)); + } + printk("\n"); +#endif + + /* + * If we are getting a callback for an active stream then we inform + * the PCM middle layer we've finished a period + */ + if (s->active) + snd_pcm_period_elapsed(s->stream); + + audio_process_dma(s); +} + +/* }}} */ + +/* {{{ PCM setting */ + +/* {{{ trigger & timer */ + +static int snd_card_sa11xx_uda1341_pcm_trigger(stream_id_t stream_id, + snd_pcm_substream_t * substream, int cmd) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int i; + + DEBUG_NAME(KERN_DEBUG "pcm_trigger id: %d cmd: %d\n", stream_id, cmd); + + DEBUG(KERN_DEBUG " sound: %d x %d [Hz]\n", runtime->channels, runtime->rate); + DEBUG(KERN_DEBUG " periods: %ld x %ld [fr]\n", (unsigned long)runtime->periods, + (unsigned long) runtime->period_size); + DEBUG(KERN_DEBUG " buffer_size: %ld [fr]\n", (unsigned long)runtime->buffer_size); + DEBUG(KERN_DEBUG " dma_addr %p\n", (char *)runtime->dma_addr); + +#ifdef DEBUG_MODE + printk(KERN_DEBUG " dma_area:"); + for (i=0; i < 32; i++) { + printk(" %02x", *(char *)(runtime->dma_addr+i)); + } + printk("\n"); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* now we need to make sure a record only stream has a clock */ + if (stream_id == CAPTURE && !chip->s[PLAYBACK]->active) { + /* we need to force fill the xmit DMA with zeros */ + DEBUG(KERN_DEBUG "starting zero fill DMA transfer\n"); + chip->s[PLAYBACK]->sync = 1; + audio_process_dma(chip->s[PLAYBACK]); + } + /* this case is when you were recording then you turn on a + * playback stream so we + * stop (also clears it) the dma first, clear the sync flag + * and then we let it get turned on + */ + else if (stream_id == PLAYBACK && chip->s[PLAYBACK]->sync) { + chip->s[PLAYBACK]->sync = 0; + audio_stop_dma(chip->s[PLAYBACK]); + } + + /* requested stream startup */ + chip->s[stream_id]->active = 1; + audio_process_dma(chip->s[stream_id]); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* requested stream shutdown */ + chip->s[stream_id]->active = 0; + audio_stop_dma(chip->s[stream_id]); + + /* + * now we need to make sure a record only stream has a clock + * so if we're stopping a playback with an active capture + * we need to turn the 0 fill dma on for the xmit side + */ + if (stream_id == PLAYBACK && chip->s[CAPTURE]->active) { + /* we need to force fill the xmit DMA with zeros */ + DEBUG(KERN_DEBUG "starting zero fill DMA transfer\n"); + chip->s[PLAYBACK]->sync = 1; + chip->s[PLAYBACK]->active = 0; + audio_process_dma(chip->s[PLAYBACK]); + } + /* + * we killed a capture only stream, so we should also kill + * the zero fill transmit + */ + else if (stream_id == CAPTURE && chip->s[PLAYBACK]->sync) { + audio_stop_dma(chip->s[PLAYBACK]); + } + + break; + default: + return -EINVAL; + break; + } + return 0; +} + +/* }}} */ + +static snd_pcm_hardware_t snd_sa11xx_uda1341_capture = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 16380, + .period_bytes_min = 64, + .period_bytes_max = 8190, /* <= MAX_DMA_SIZE from ams/arch-sa1100/dma.h */ + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_sa11xx_uda1341_playback = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 16380, + .period_bytes_min = 64, + .period_bytes_max = 8190, /* <= MAX_DMA_SIZE from ams/arch-sa1100/dma.h */ + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +/* {{{ snd_card_sa11xx_uda1341_playback functions */ + +static int snd_card_sa11xx_uda1341_playback_open(snd_pcm_substream_t * substream) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + DEBUG_NAME(KERN_DEBUG "playback_open\n"); + + chip->s[PLAYBACK]->stream = substream; + chip->s[PLAYBACK]->sent_periods = 0; + chip->s[PLAYBACK]->sent_total = 0; + + /* no reset here since we may be zero filling the DMA + * if we are, the dma stream will get reset in the pcm_trigger + * i.e. when it actually starts to play + */ + /* audio_reset(chip->s[PLAYBACK]); */ + + runtime->hw = snd_sa11xx_uda1341_playback; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_rates)) < 0) + return err; + + return 0; +} + +static int snd_card_sa11xx_uda1341_playback_close(snd_pcm_substream_t * substream) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + + DEBUG_NAME(KERN_DEBUG "playback_close\n"); + + chip->s[PLAYBACK]->stream = NULL; + + return 0; +} + +static int snd_card_sa11xx_uda1341_playback_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + DEBUG_NAME(KERN_DEBUG "playback_ioctl cmd: %d\n", cmd); + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_sa11xx_uda1341_playback_prepare(snd_pcm_substream_t * substream) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + DEBUG_NAME(KERN_DEBUG "playback_prepare\n"); + + /* set requested samplerate */ + sa11xx_uda1341_set_samplerate(chip, runtime->rate); + + return 0; +} + +static int snd_card_sa11xx_uda1341_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + DEBUG_NAME(KERN_DEBUG "playback_trigger\n"); + return snd_card_sa11xx_uda1341_pcm_trigger(PLAYBACK, substream, cmd); +} + +static snd_pcm_uframes_t snd_card_sa11xx_uda1341_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_uframes_t pos; + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + + DEBUG_NAME(KERN_DEBUG "playback_pointer\n"); + + pos = audio_get_dma_pos(chip->s[PLAYBACK]); + return pos; +} + +/* }}} */ + +/* {{{ snd_card_sa11xx_uda1341_record functions */ + +static int snd_card_sa11xx_uda1341_capture_open(snd_pcm_substream_t * substream) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + DEBUG_NAME(KERN_DEBUG "record_open\n"); + + chip->s[CAPTURE]->stream = substream; + chip->s[CAPTURE]->sent_periods = 0; + chip->s[CAPTURE]->sent_total = 0; + + audio_reset(chip->s[CAPTURE]); + + runtime->hw = snd_sa11xx_uda1341_capture; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_rates)) < 0) + return err; + + return 0; +} + +static int snd_card_sa11xx_uda1341_capture_close(snd_pcm_substream_t * substream) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + + DEBUG_NAME(KERN_DEBUG "record_close\n"); + + chip->s[CAPTURE]->stream = NULL; + + return 0; +} + +static int snd_card_sa11xx_uda1341_capture_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + DEBUG_NAME(KERN_DEBUG "record_ioctl cmd: %d\n", cmd); + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_sa11xx_uda1341_capture_prepare(snd_pcm_substream_t * substream) +{ + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + DEBUG_NAME(KERN_DEBUG "record_prepare\n"); + + /* set requested samplerate */ + sa11xx_uda1341_set_samplerate(chip, runtime->rate); + + return 0; +} + +static int snd_card_sa11xx_uda1341_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + DEBUG_NAME(KERN_DEBUG "record_trigger\n"); + return snd_card_sa11xx_uda1341_pcm_trigger(CAPTURE, substream, cmd); +} + +static snd_pcm_uframes_t snd_card_sa11xx_uda1341_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_uframes_t pos; + sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); + + DEBUG_NAME(KERN_DEBUG "record_pointer\n"); + pos = audio_get_dma_pos(chip->s[CAPTURE]); + return pos; +} + +/* }}} */ + +/* {{{ HW params & free */ + +static int snd_sa11xx_uda1341_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + + DEBUG_NAME(KERN_DEBUG "hw_params\n"); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sa11xx_uda1341_hw_free(snd_pcm_substream_t * substream) +{ + DEBUG_NAME(KERN_DEBUG "hw_free\n"); + return snd_pcm_lib_free_pages(substream); +} + +/* }}} */ + +static snd_pcm_ops_t snd_card_sa11xx_uda1341_playback_ops = { + .open = snd_card_sa11xx_uda1341_playback_open, + .close = snd_card_sa11xx_uda1341_playback_close, + .ioctl = snd_card_sa11xx_uda1341_playback_ioctl, + .hw_params = snd_sa11xx_uda1341_hw_params, + .hw_free = snd_sa11xx_uda1341_hw_free, + .prepare = snd_card_sa11xx_uda1341_playback_prepare, + .trigger = snd_card_sa11xx_uda1341_playback_trigger, + .pointer = snd_card_sa11xx_uda1341_playback_pointer, +}; + +static snd_pcm_ops_t snd_card_sa11xx_uda1341_capture_ops = { + .open = snd_card_sa11xx_uda1341_capture_open, + .close = snd_card_sa11xx_uda1341_capture_close, + .ioctl = snd_card_sa11xx_uda1341_capture_ioctl, + .hw_params = snd_sa11xx_uda1341_hw_params, + .hw_free = snd_sa11xx_uda1341_hw_free, + .prepare = snd_card_sa11xx_uda1341_capture_prepare, + .trigger = snd_card_sa11xx_uda1341_capture_trigger, + .pointer = snd_card_sa11xx_uda1341_capture_pointer, +}; + +static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device, int substreams) +{ + snd_pcm_t *pcm; + int err; + + DEBUG_NAME(KERN_DEBUG "sa11xx_uda1341_pcm\n"); + + if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, + substreams, substreams, &pcm)) < 0) + return err; + + /* + * this sets up our initial buffers and sets the dma_type to isa. + * isa works but I'm not sure why (or if) it's the right choice + * this may be too large, trying it for now + */ + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops); + pcm->private_data = sa11xx_uda1341; + pcm->info_flags = 0; + strcpy(pcm->name, "UDA1341 PCM"); + + sa11xx_uda1341->s[PLAYBACK] = snd_kcalloc(sizeof(audio_stream_t), GFP_KERNEL); + sa11xx_uda1341->s[CAPTURE] = snd_kcalloc(sizeof(audio_stream_t), GFP_KERNEL); + + sa11xx_uda1341_audio_init(sa11xx_uda1341); + + /* setup DMA controller */ + audio_dma_request(sa11xx_uda1341->s[PLAYBACK], audio_dma_callback); + audio_dma_request(sa11xx_uda1341->s[CAPTURE], audio_dma_callback); + + return 0; +} + +/* }}} */ + +/* {{{ module init & exit */ + +#ifdef CONFIG_PM + +static int sa11xx_uda1341_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + sa11xx_uda1341_t *sa11xx_uda1341 = pm_dev->data; + audio_stream_t *is, *os; + int stopstate; + + DEBUG_NAME(KERN_DEBUG "pm_callback\n"); + + /* pause resume is broken see note */ + printk("Pause/Resume support currently broken... \n"); + return -1; + + is = sa11xx_uda1341->s[PLAYBACK]; + os = sa11xx_uda1341->s[CAPTURE]; + + switch (req) { + case PM_SUSPEND: /* enter D1-D3 */ + if (is && is->dma_regs) { + stopstate = is->active; + audio_stop_dma(is); + DMA_CLEAR(is); + is->active = stopstate; + } + if (os && os->dma_regs) { + stopstate = os->active; + audio_stop_dma(os); + DMA_CLEAR(os); + os->active = stopstate; + } + if (is->stream || os->stream) + sa11xx_uda1341_audio_shutdown(sa11xx_uda1341); + break; + case PM_RESUME: /* enter D0 */ + if (is->stream || os->stream) + sa11xx_uda1341_audio_init(sa11xx_uda1341); + if (os && os->dma_regs) { + DMA_RESET(os); + audio_process_dma(os); + } + if (is && is->dma_regs) { + DMA_RESET(is); + audio_process_dma(is); + } + break; + } + return 0; +} + +#endif + +void snd_sa11xx_uda1341_free(snd_card_t *card) +{ + sa11xx_uda1341_t *chip = snd_magic_cast(sa11xx_uda1341_t, card->private_data, return); + + DEBUG_NAME(KERN_DEBUG "snd_sa11xx_uda1341_free\n"); + + audio_dma_free(chip->s[PLAYBACK]); + audio_dma_free(chip->s[CAPTURE]); + + kfree(chip->s[PLAYBACK]); + kfree(chip->s[CAPTURE]); + + chip->s[PLAYBACK] = NULL; + chip->s[CAPTURE] = NULL; + + snd_magic_kfree(chip); + card->private_data = NULL; +} + +static int __init sa11xx_uda1341_init(void) +{ + int err; + snd_card_t *card; + + DEBUG_NAME(KERN_DEBUG "sa11xx_uda1341_uda1341_init\n"); + + if (!machine_is_h3xxx()) + return -ENODEV; + + /* register the soundcard */ + card = snd_card_new(-1, id, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + sa11xx_uda1341 = snd_magic_kcalloc(sa11xx_uda1341_t, 0, GFP_KERNEL); + if (sa11xx_uda1341 == NULL) + return -ENOMEM; + + card->private_data = (void *)sa11xx_uda1341; + card->private_free = snd_sa11xx_uda1341_free; + + sa11xx_uda1341->card = card; + + // mixer + if ((err = snd_chip_uda1341_mixer_new(sa11xx_uda1341->card, &sa11xx_uda1341->uda1341))) + goto nodev; + + // PCM + if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0, 2)) < 0) + goto nodev; + + +#ifdef CONFIG_PM + sa11xx_uda1341->pm_dev = pm_register(PM_SYS_DEV, 0, sa11xx_uda1341_pm_callback); + if (sa11xx_uda1341->pm_dev) + sa11xx_uda1341->pm_dev->data = sa11xx_uda1341; +#endif + + strcpy(card->driver, "UDA1341"); + strcpy(card->shortname, "H3600 UDA1341TS"); + sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS"); + + if ((err = snd_card_register(card)) == 0) { + printk( KERN_INFO "iPAQ audio support initialized\n" ); + return 0; + } + + nodev: + snd_card_free(card); + return err; +} + +static void __exit sa11xx_uda1341_exit(void) +{ + snd_chip_uda1341_mixer_del(sa11xx_uda1341->card); + snd_card_free(sa11xx_uda1341->card); + sa11xx_uda1341 = NULL; +} + +module_init(sa11xx_uda1341_init); +module_exit(sa11xx_uda1341_exit); + +/* }}} */ + +/* + * Local variables: + * indent-tabs-mode: t + * End: + */ diff -urN linux-2.4.21-rc1.orig/sound/core/Config.in linux/sound/core/Config.in --- linux-2.4.21-rc1.orig/sound/core/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/Config.in 2003-04-29 22:58:48.000000000 -0600 @@ -0,0 +1,27 @@ +# ALSA soundcard-configuration + +if [ "$CONFIG_X86_64" = "y" -a "$CONFIG_IA32_EMULATION" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL $CONFIG_SND +fi +if [ "$CONFIG_PPC64" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL $CONFIG_SND +fi +if [ "$CONFIG_SPARC64" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL $CONFIG_SND +fi +dep_tristate ' Sequencer support' CONFIG_SND_SEQUENCER $CONFIG_SND +if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then + dep_tristate ' Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER +fi +bool ' OSS API emulation' CONFIG_SND_OSSEMUL +if [ "$CONFIG_SND_OSSEMUL" = "y" ]; then + define_bool CONFIG_SOUND y + dep_tristate ' OSS Mixer API' CONFIG_SND_MIXER_OSS $CONFIG_SND + dep_tristate ' OSS PCM (digital audio) API' CONFIG_SND_PCM_OSS $CONFIG_SND + dep_mbool ' OSS Sequencer API' CONFIG_SND_SEQUENCER_OSS $CONFIG_SND_SEQUENCER +fi +dep_tristate ' RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND $CONFIG_RTC +bool ' Verbose printk' CONFIG_SND_VERBOSE_PRINTK +bool ' Debug' CONFIG_SND_DEBUG +dep_bool ' Debug memory' CONFIG_SND_DEBUG_MEMORY $CONFIG_SND_DEBUG +dep_bool ' Debug detection' CONFIG_SND_DEBUG_DETECT $CONFIG_SND_DEBUG diff -urN linux-2.4.21-rc1.orig/sound/core/Makefile linux/sound/core/Makefile --- linux-2.4.21-rc1.orig/sound/core/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/Makefile 2003-04-30 00:14:37.000000000 -0600 @@ -0,0 +1,139 @@ +# +# Makefile for ALSA +# Copyright (c) 1999,2001 by Jaroslav Kysela +# + +O_TARGET := core.o + +export-objs := hwdep.o memalloc.o pcm.o pcm_lib.o rawmidi.o sound.o timer.o + + +list-multi := snd.o snd-pcm.o snd-page-alloc.o snd-rawmidi.o snd-timer.o snd-rtctimer.o snd-hwdep.o + +snd-objs := sound.o init.o memory.o info.o control.o misc.o \ + device.o wrappers.o +ifeq ($(CONFIG_ISA),y) +snd-objs += isadma.o +endif +ifeq ($(CONFIG_SND_OSSEMUL),y) +snd-objs += sound_oss.o info_oss.o +endif + +snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ + pcm_memory.o + +snd-page-alloc-objs := memalloc.o +ifeq ($(CONFIG_PCI),y) +snd-page-alloc-objs += sgbuf.o memory_wrapper.o +endif + +snd-rawmidi-objs := rawmidi.o +snd-timer-objs := timer.o +snd-rtctimer-objs := rtctimer.o +snd-hwdep-objs := hwdep.o + +obj-$(CONFIG_SND) += snd.o +ifeq ($(subst m,y,$(CONFIG_RTC)),y) + obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o + obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o +endif +obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o + +obj-$(CONFIG_SND_PCM_OSS) += snd-pcm.o snd-timer.o snd-page-alloc.o +obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o + +subdir-$(CONFIG_SND_MIXER_OSS) += oss +subdir-$(CONFIG_SND_PCM_OSS) += oss +subdir-$(CONFIG_SND_SEQUENCER) += seq +subdir-y += ioctl32 + + + +# Toplevel Module Dependency +obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_SERIAL_U16550) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_DT019X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o snd-rawmidi.o +obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-page-alloc.o snd-pcm.o +obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-hwdep.o + obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o +endif +obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd.o: $(snd-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-objs) + +snd-pcm.o: $(snd-pcm-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-objs) + +snd-page-alloc.o: $(snd-page-alloc-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-page-alloc-objs) + +snd-rawmidi.o: $(snd-rawmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rawmidi-objs) + +snd-timer.o: $(snd-timer-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-timer-objs) + +snd-rtctimer.o: $(snd-rtctimer-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rtctimer-objs) + +snd-hwdep.o: $(snd-hwdep-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-hwdep-objs) diff -urN linux-2.4.21-rc1.orig/sound/core/control.c linux/sound/core/control.c --- linux-2.4.21-rc1.orig/sound/core/control.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/control.c 2003-02-28 06:05:04.000000000 -0700 @@ -0,0 +1,952 @@ +/* + * Routines for driver control interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _snd_kctl_ioctl { + struct list_head list; /* list of all ioctls */ + snd_kctl_ioctl_func_t fioctl; +} snd_kctl_ioctl_t; + +#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list) + +static DECLARE_RWSEM(snd_ioctl_rwsem); +static LIST_HEAD(snd_control_ioctls); + +static int snd_ctl_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + unsigned long flags; + snd_card_t *card; + snd_ctl_file_t *ctl; + int err; + + card = snd_cards[cardnum]; + if (!card) { + err = -ENODEV; + goto __error1; + } + err = snd_card_file_add(card, file); + if (err < 0) { + err = -ENODEV; + goto __error1; + } + if (!try_module_get(card->module)) { + err = -EFAULT; + goto __error2; + } + ctl = snd_magic_kcalloc(snd_ctl_file_t, 0, GFP_KERNEL); + if (ctl == NULL) { + err = -ENOMEM; + goto __error; + } + INIT_LIST_HEAD(&ctl->events); + init_waitqueue_head(&ctl->change_sleep); + spin_lock_init(&ctl->read_lock); + ctl->card = card; + ctl->pid = current->pid; + file->private_data = ctl; + write_lock_irqsave(&card->ctl_files_rwlock, flags); + list_add_tail(&ctl->list, &card->ctl_files); + write_unlock_irqrestore(&card->ctl_files_rwlock, flags); + return 0; + + __error: + module_put(card->module); + __error2: + snd_card_file_remove(card, file); + __error1: + return err; +} + +static void snd_ctl_empty_read_queue(snd_ctl_file_t * ctl) +{ + snd_kctl_event_t *cread; + + spin_lock(&ctl->read_lock); + while (!list_empty(&ctl->events)) { + cread = snd_kctl_event(ctl->events.next); + list_del(&cread->list); + kfree(cread); + } + spin_unlock(&ctl->read_lock); +} + +static int snd_ctl_release(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct list_head *list; + snd_card_t *card; + snd_ctl_file_t *ctl; + snd_kcontrol_t *control; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + fasync_helper(-1, file, 0, &ctl->fasync); + file->private_data = NULL; + card = ctl->card; + write_lock_irqsave(&card->ctl_files_rwlock, flags); + list_del(&ctl->list); + write_unlock_irqrestore(&card->ctl_files_rwlock, flags); + down_write(&card->controls_rwsem); + list_for_each(list, &card->controls) { + control = snd_kcontrol(list); + if (control->owner == ctl) + control->owner = NULL; + } + up_write(&card->controls_rwsem); + snd_ctl_empty_read_queue(ctl); + snd_magic_kfree(ctl); + module_put(card->module); + snd_card_file_remove(card, file); + return 0; +} + +void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id) +{ + unsigned long flags; + struct list_head *flist; + snd_ctl_file_t *ctl; + snd_kctl_event_t *ev; + + snd_runtime_check(card != NULL && id != NULL, return); + read_lock(&card->ctl_files_rwlock); +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + card->mixer_oss_change_count++; +#endif + list_for_each(flist, &card->ctl_files) { + struct list_head *elist; + ctl = snd_ctl_file(flist); + if (!ctl->subscribed) + continue; + spin_lock_irqsave(&ctl->read_lock, flags); + list_for_each(elist, &ctl->events) { + ev = snd_kctl_event(elist); + if (ev->id.numid == id->numid) { + ev->mask |= mask; + goto _found; + } + } + ev = snd_kcalloc(sizeof(*ev), GFP_ATOMIC); + if (ev) { + ev->id = *id; + ev->mask = mask; + list_add_tail(&ev->list, &ctl->events); + } else { + snd_printk(KERN_ERR "No memory available to allocate event\n"); + } + _found: + wake_up(&ctl->change_sleep); + kill_fasync(&ctl->fasync, SIGIO, POLL_IN); + spin_unlock_irqrestore(&ctl->read_lock, flags); + } + read_unlock(&card->ctl_files_rwlock); +} + +/** + * snd_ctl_new - create a control instance from the template + * @control: the control template + * + * Allocates a new snd_kcontrol_t instance and copies the given template + * to the new instance. + * + * Returns the pointer of the new instance, or NULL on failure. + */ +snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) +{ + snd_kcontrol_t *kctl; + + snd_runtime_check(control != NULL, return NULL); + kctl = (snd_kcontrol_t *)snd_magic_kmalloc(snd_kcontrol_t, 0, GFP_KERNEL); + if (kctl == NULL) + return NULL; + *kctl = *control; + return kctl; +} + +/** + * snd_ctl_new1 - create a control instance from the template + * @ncontrol: the initialization record + * @private_data: the private data to set + * + * Allocates a new snd_kcontrol_t instance and initialize from the given + * template. When the access field of ncontrol is 0, it's assumed as + * READWRITE access. + * + * Returns the pointer of the newly generated instance, or NULL on failure. + */ +snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) +{ + snd_kcontrol_t kctl; + + snd_runtime_check(ncontrol != NULL, return NULL); + snd_assert(ncontrol->info != NULL, return NULL); + memset(&kctl, 0, sizeof(kctl)); + kctl.id.iface = ncontrol->iface; + kctl.id.device = ncontrol->device; + kctl.id.subdevice = ncontrol->subdevice; + if (ncontrol->name) + strncpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)-1); + kctl.id.index = ncontrol->index; + kctl.access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : + (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); + kctl.info = ncontrol->info; + kctl.get = ncontrol->get; + kctl.put = ncontrol->put; + kctl.private_value = ncontrol->private_value; + kctl.private_data = private_data; + return snd_ctl_new(&kctl); +} + +/** + * snd_ctl_free_one - release the control instance + * @kcontrol: the control instance + * + * Releases the control instance created via snd_ctl_new() + * or snd_ctl_new1(). + * Don't call this after the control was added to the card. + */ +void snd_ctl_free_one(snd_kcontrol_t * kcontrol) +{ + if (kcontrol) { + if (kcontrol->private_free) + kcontrol->private_free(kcontrol); + snd_magic_kfree(kcontrol); + } +} + +/** + * snd_ctl_add - add the control instance to the card + * @card: the card instance + * @kcontrol: the control instance to add + * + * Adds the control instance created via snd_ctl_new() or + * snd_ctl_new1() to the given card. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) +{ + snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); + snd_assert(kcontrol->info != NULL, return -EINVAL); + snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_READ) || kcontrol->get != NULL, return -EINVAL); + snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kcontrol->put != NULL, return -EINVAL); + down_write(&card->controls_rwsem); + list_add_tail(&kcontrol->list, &card->controls); + card->controls_count++; + kcontrol->id.numid = ++card->last_numid; + up_write(&card->controls_rwsem); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &kcontrol->id); + return 0; +} + +/** + * snd_ctl_remove - remove the control from the card and release it + * @card: the card instance + * @kcontrol: the control instance to remove + * + * Removes the control from the card and then releases the instance. + * You don't need to call snd_ctl_free_one(). + * + * Returns 0 if successful, or a negative error code on failure. + */ +int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) +{ + snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); + down_write(&card->controls_rwsem); + list_del(&kcontrol->list); + card->controls_count--; + up_write(&card->controls_rwsem); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &kcontrol->id); + snd_ctl_free_one(kcontrol); + return 0; +} + +/** + * snd_ctl_remove_id - remove the control of the given id and release it + * @card: the card instance + * @id: the control id to remove + * + * Finds the control instance with the given id, removes it from the + * card list and releases it. + * + * Returns 0 if successful, or a negative error code on failure. + */ +int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + snd_kcontrol_t *kctl; + + kctl = snd_ctl_find_id(card, id); + if (kctl == NULL) + return -ENOENT; + return snd_ctl_remove(card, kctl); +} + +static snd_kcontrol_t *_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id); /* w/o lock */ + +/** + * snd_ctl_rename_id - replace the id of a control on the card + * @card: the card instance + * @src_id: the old id + * @dst_id: the new id + * + * Finds the control with the old id from the card, and replaces the + * id with the new one. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id) +{ + snd_kcontrol_t *kctl; + + down_write(&card->controls_rwsem); + kctl = _ctl_find_id(card, src_id); + if (kctl == NULL) { + up_write(&card->controls_rwsem); + return -ENOENT; + } + kctl->id = *dst_id; + kctl->id.numid = ++card->last_numid; + up_write(&card->controls_rwsem); + return 0; +} + +static snd_kcontrol_t *_ctl_find_numid(snd_card_t * card, unsigned int numid) +{ + struct list_head *list; + snd_kcontrol_t *kctl; + + snd_runtime_check(card != NULL && numid != 0, return NULL); + list_for_each(list, &card->controls) { + kctl = snd_kcontrol(list); + if (kctl->id.numid == numid) + return kctl; + } + return NULL; +} + +static snd_kcontrol_t *_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + struct list_head *list; + snd_kcontrol_t *kctl; + + snd_runtime_check(card != NULL && id != NULL, return NULL); + if (id->numid != 0) + return _ctl_find_numid(card, id->numid); + list_for_each(list, &card->controls) { + kctl = snd_kcontrol(list); + if (kctl->id.iface != id->iface) + continue; + if (kctl->id.device != id->device) + continue; + if (kctl->id.subdevice != id->subdevice) + continue; + if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name))) + continue; + if (kctl->id.index != id->index) + continue; + return kctl; + } + return NULL; +} + +/** + * snd_ctl_find_id - find the control instance with the given id + * @card: the card instance + * @id: the id to search + * + * Finds the control instance with the given id from the card. + * + * Returns the pointer of the instance if found, or NULL if not. + */ +snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + snd_kcontrol_t *kctl; + down_read(&card->controls_rwsem); + kctl = _ctl_find_id(card, id); + up_read(&card->controls_rwsem); + return kctl; +} + +/** + * snd_ctl_find_numid - find the control instance with the given number-id + * @card: the card instance + * @numid: the number-id to search + * + * Finds the control instance with the given number-id from the card. + * + * Returns the pointer of the instance if found, or NULL if not. + */ +snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid) +{ + snd_kcontrol_t *kctl; + down_read(&card->controls_rwsem); + kctl = _ctl_find_numid(card, numid); + up_read(&card->controls_rwsem); + return kctl; +} + +static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl, + unsigned int cmd, unsigned long arg) +{ + snd_ctl_card_info_t info; + + memset(&info, 0, sizeof(info)); + down_read(&snd_ioctl_rwsem); + info.card = card->number; + strncpy(info.id, card->id, sizeof(info.id) - 1); + strncpy(info.driver, card->driver, sizeof(info.driver) - 1); + strncpy(info.name, card->shortname, sizeof(info.name) - 1); + strncpy(info.longname, card->longname, sizeof(info.longname) - 1); + strncpy(info.mixername, card->mixername, sizeof(info.mixername) - 1); + strncpy(info.components, card->components, sizeof(info.components) - 1); + up_read(&snd_ioctl_rwsem); + if (copy_to_user((void *) arg, &info, sizeof(snd_ctl_card_info_t))) + return -EFAULT; + return 0; +} + +static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t *_list) +{ + struct list_head *plist; + snd_ctl_elem_list_t list; + snd_kcontrol_t *kctl; + snd_ctl_elem_id_t *dst, *id; + int offset, space; + + if (copy_from_user(&list, _list, sizeof(list))) + return -EFAULT; + offset = list.offset; + space = list.space; + /* try limit maximum space */ + if (space > 16384) + return -ENOMEM; + if (space > 0) { + /* allocate temporary buffer for atomic operation */ + dst = vmalloc(space * sizeof(snd_ctl_elem_id_t)); + if (dst == NULL) + return -ENOMEM; + down_read(&card->controls_rwsem); + list.count = card->controls_count; + plist = card->controls.next; + while (offset-- > 0 && plist != &card->controls) + plist = plist->next; + list.used = 0; + id = dst; + while (space > 0 && plist != &card->controls) { + kctl = snd_kcontrol(plist); + memcpy(id, &kctl->id, sizeof(snd_ctl_elem_id_t)); + id++; + plist = plist->next; + space--; + list.used++; + } + up_read(&card->controls_rwsem); + if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(snd_ctl_elem_id_t))) + return -EFAULT; + vfree(dst); + } else { + down_read(&card->controls_rwsem); + list.count = card->controls_count; + up_read(&card->controls_rwsem); + } + if (copy_to_user(_list, &list, sizeof(list))) + return -EFAULT; + return 0; +} + +static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *_info) +{ + snd_card_t *card = ctl->card; + snd_ctl_elem_info_t info; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + down_read(&card->controls_rwsem); + kctl = _ctl_find_id(card, &info.id); + if (kctl == NULL) { + up_read(&card->controls_rwsem); + return -ENOENT; + } +#ifdef CONFIG_SND_DEBUG + info.access = 0; +#endif + result = kctl->info(kctl, &info); + if (result >= 0) { + snd_assert(info.access == 0, ); + info.id = kctl->id; + info.access = kctl->access; + if (kctl->owner) { + info.access |= SNDRV_CTL_ELEM_ACCESS_LOCK; + if (kctl->owner == ctl) + info.access |= SNDRV_CTL_ELEM_ACCESS_OWNER; + info.owner = kctl->owner_pid; + } else { + info.owner = -1; + } + } + up_read(&card->controls_rwsem); + if (result >= 0) + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *_control) +{ + snd_ctl_elem_value_t *control; + snd_kcontrol_t *kctl; + int result, indirect; + + control = kmalloc(sizeof(*control), GFP_KERNEL); + if (control == NULL) + return -ENOMEM; + if (copy_from_user(control, _control, sizeof(*control))) + return -EFAULT; + down_read(&card->controls_rwsem); + kctl = _ctl_find_id(card, &control->id); + if (kctl == NULL) { + result = -ENOENT; + } else { + indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; + if (control->indirect != indirect) { + result = -EACCES; + } else { + if ((kctl->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) { + result = kctl->get(kctl, control); + if (result >= 0) + control->id = kctl->id; + } else + result = -EPERM; + } + } + up_read(&card->controls_rwsem); + if (result >= 0) + if (copy_to_user(_control, control, sizeof(*control))) + return -EFAULT; + kfree(control); + return result; +} + +static int snd_ctl_elem_write(snd_ctl_file_t *file, snd_ctl_elem_value_t *_control) +{ + snd_card_t *card = file->card; + snd_ctl_elem_value_t *control; + snd_kcontrol_t *kctl; + int result, indirect; + + control = kmalloc(sizeof(*control), GFP_KERNEL); + if (control == NULL) + return -ENOMEM; + if (copy_from_user(control, _control, sizeof(*control))) + return -EFAULT; + down_read(&card->controls_rwsem); + kctl = _ctl_find_id(card, &control->id); + if (kctl == NULL) { + result = -ENOENT; + } else { + indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; + if (control->indirect != indirect) { + result = -EACCES; + } else { + if (!(kctl->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || + kctl->put == NULL || + (kctl->owner != NULL && kctl->owner != file)) { + result = -EPERM; + } else { + result = kctl->put(kctl, control); + if (result >= 0) + control->id = kctl->id; + } + if (result > 0) { + up_read(&card->controls_rwsem); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + result = 0; + goto __unlocked; + } + } + } + up_read(&card->controls_rwsem); + __unlocked: + if (result >= 0) + if (copy_to_user(_control, control, sizeof(*control))) + return -EFAULT; + kfree(control); + return result; +} + +static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id) +{ + snd_card_t *card = file->card; + snd_ctl_elem_id_t id; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + down_write(&card->controls_rwsem); + kctl = _ctl_find_id(card, &id); + if (kctl == NULL) { + result = -ENOENT; + } else { + if (kctl->owner != NULL) + result = -EBUSY; + else { + kctl->owner = file; + kctl->owner_pid = current->pid; + result = 0; + } + } + up_write(&card->controls_rwsem); + return result; +} + +static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id) +{ + snd_card_t *card = file->card; + snd_ctl_elem_id_t id; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + down_write(&card->controls_rwsem); + kctl = _ctl_find_id(card, &id); + if (kctl == NULL) { + result = -ENOENT; + } else { + if (kctl->owner == NULL) + result = -EINVAL; + else if (kctl->owner != file) + result = -EPERM; + else { + kctl->owner = NULL; + kctl->owner_pid = 0; + result = 0; + } + } + up_write(&card->controls_rwsem); + return result; +} + +static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int *ptr) +{ + int subscribe; + if (get_user(subscribe, ptr)) + return -EFAULT; + if (subscribe < 0) { + subscribe = file->subscribed; + if (put_user(subscribe, ptr)) + return -EFAULT; + return 0; + } + if (subscribe) { + file->subscribed = 1; + return 0; + } else if (file->subscribed) { + snd_ctl_empty_read_queue(file); + file->subscribed = 0; + } + return 0; +} + +static int snd_ctl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_ctl_file_t *ctl; + snd_card_t *card; + struct list_head *list; + snd_kctl_ioctl_t *p; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + card = ctl->card; + snd_assert(card != NULL, return -ENXIO); + switch (cmd) { + case SNDRV_CTL_IOCTL_PVERSION: + return put_user(SNDRV_CTL_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_CTL_IOCTL_CARD_INFO: + return snd_ctl_card_info(card, ctl, cmd, arg); + case SNDRV_CTL_IOCTL_ELEM_LIST: + return snd_ctl_elem_list(ctl->card, (snd_ctl_elem_list_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_INFO: + return snd_ctl_elem_info(ctl, (snd_ctl_elem_info_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_READ: + return snd_ctl_elem_read(ctl->card, (snd_ctl_elem_value_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_WRITE: + return snd_ctl_elem_write(ctl, (snd_ctl_elem_value_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_LOCK: + return snd_ctl_elem_lock(ctl, (snd_ctl_elem_id_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_UNLOCK: + return snd_ctl_elem_unlock(ctl, (snd_ctl_elem_id_t *) arg); + case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: + return snd_ctl_subscribe_events(ctl, (int *) arg); + case SNDRV_CTL_IOCTL_POWER: + if (get_user(err, (int *)arg)) + return -EFAULT; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; +#ifdef CONFIG_PM + if (card->set_power_state) { + snd_power_lock(card); + err = card->set_power_state(card, err); + snd_power_unlock(card); + } else +#endif + err = -ENOPROTOOPT; + return err; + case SNDRV_CTL_IOCTL_POWER_STATE: +#ifdef CONFIG_PM + return put_user(card->power_state, (int *)arg) ? -EFAULT : 0; +#else + return put_user(SNDRV_CTL_POWER_D0, (int *)arg) ? -EFAULT : 0; +#endif + } + down_read(&snd_ioctl_rwsem); + list_for_each(list, &snd_control_ioctls) { + p = list_entry(list, snd_kctl_ioctl_t, list); + err = p->fioctl(card, ctl, cmd, arg); + if (err != -ENOIOCTLCMD) { + up_read(&snd_ioctl_rwsem); + return err; + } + } + up_read(&snd_ioctl_rwsem); + snd_printd("unknown ioctl = 0x%x\n", cmd); + return -ENOTTY; +} + +static ssize_t snd_ctl_read(struct file *file, char *buffer, size_t count, loff_t * offset) +{ + snd_ctl_file_t *ctl; + int err = 0; + ssize_t result = 0; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); + if (!ctl->subscribed) + return -EBADFD; + if (count < sizeof(snd_ctl_event_t)) + return -EINVAL; + spin_lock_irq(&ctl->read_lock); + while (count >= sizeof(snd_ctl_event_t)) { + snd_ctl_event_t ev; + snd_kctl_event_t *kev; + while (list_empty(&ctl->events)) { + wait_queue_t wait; + if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { + err = -EAGAIN; + goto __end; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&ctl->change_sleep, &wait); + spin_unlock_irq(&ctl->read_lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctl->change_sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + spin_lock_irq(&ctl->read_lock); + } + kev = snd_kctl_event(ctl->events.next); + ev.type = SNDRV_CTL_EVENT_ELEM; + ev.data.elem.mask = kev->mask; + ev.data.elem.id = kev->id; + list_del(&kev->list); + spin_unlock_irq(&ctl->read_lock); + kfree(kev); + if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) { + err = -EFAULT; + goto __end; + } + spin_lock_irq(&ctl->read_lock); + buffer += sizeof(snd_ctl_event_t); + count -= sizeof(snd_ctl_event_t); + result += sizeof(snd_ctl_event_t); + } + __end: + spin_unlock_irq(&ctl->read_lock); + return result > 0 ? result : err; +} + +static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + snd_ctl_file_t *ctl; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return 0); + if (!ctl->subscribed) + return 0; + poll_wait(file, &ctl->change_sleep, wait); + + mask = 0; + if (!list_empty(&ctl->events)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +/* + * register the device-specific control-ioctls. + * called from each device manager like pcm.c, hwdep.c, etc. + */ +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) +{ + snd_kctl_ioctl_t *pn; + + pn = (snd_kctl_ioctl_t *) + snd_kcalloc(sizeof(snd_kctl_ioctl_t), GFP_KERNEL); + if (pn == NULL) + return -ENOMEM; + pn->fioctl = fcn; + down_write(&snd_ioctl_rwsem); + list_add_tail(&pn->list, &snd_control_ioctls); + up_write(&snd_ioctl_rwsem); + return 0; +} + +/* + * de-register the device-specific control-ioctls. + */ +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) +{ + struct list_head *list; + snd_kctl_ioctl_t *p; + + snd_runtime_check(fcn != NULL, return -EINVAL); + down_write(&snd_ioctl_rwsem); + list_for_each(list, &snd_control_ioctls) { + p = list_entry(list, snd_kctl_ioctl_t, list); + if (p->fioctl == fcn) { + list_del(&p->list); + up_write(&snd_ioctl_rwsem); + kfree(p); + return 0; + } + } + up_write(&snd_ioctl_rwsem); + snd_BUG(); + return -EINVAL; +} + +static int snd_ctl_fasync(int fd, struct file * file, int on) +{ + snd_ctl_file_t *ctl; + int err; + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + err = fasync_helper(fd, file, on, &ctl->fasync); + if (err < 0) + return err; + return 0; +} + +/* + * INIT PART + */ + +static struct file_operations snd_ctl_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = snd_ctl_read, + .open = snd_ctl_open, + .release = snd_ctl_release, + .poll = snd_ctl_poll, + .ioctl = snd_ctl_ioctl, + .fasync = snd_ctl_fasync, +}; + +static snd_minor_t snd_ctl_reg = +{ + .comment = "ctl", + .f_ops = &snd_ctl_f_ops, +}; + +/* + * registration of the control device: + * called from init.c + */ +int snd_ctl_register(snd_card_t *card) +{ + int err, cardnum; + char name[16]; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + sprintf(name, "controlC%i", cardnum); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, + card, 0, &snd_ctl_reg, name)) < 0) + return err; + return 0; +} + +/* + * disconnection of the control device: + * called from init.c + */ +int snd_ctl_disconnect(snd_card_t *card) +{ + struct list_head *flist; + snd_ctl_file_t *ctl; + + down_read(&card->controls_rwsem); + list_for_each(flist, &card->ctl_files) { + ctl = snd_ctl_file(flist); + wake_up(&ctl->change_sleep); + kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); + } + up_read(&card->controls_rwsem); + return 0; +} + +/* + * de-registration of the control device: + * called from init.c + */ +int snd_ctl_unregister(snd_card_t *card) +{ + int err, cardnum; + snd_kcontrol_t *control; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0) + return err; + while (!list_empty(&card->controls)) { + control = snd_kcontrol(card->controls.next); + snd_ctl_remove(card, control); + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/device.c linux/sound/core/device.c --- linux-2.4.21-rc1.orig/sound/core/device.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/device.c 2003-01-31 08:19:24.000000000 -0700 @@ -0,0 +1,240 @@ +/* + * Device management routines + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +/** + * snd_device_new - create an ALSA device component + * @card: the card instance + * @type: the device type, SNDRV_DEV_TYPE_XXX + * @device_data: the data pointer of this device + * @ops: the operator table + * + * Creates a new device component for the given data pointer. + * The device will be assigned to the card and managed together + * by the card. + * + * The data pointer plays a role as the identifier, too, so the + * pointer address must be unique and unchanged. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_device_new(snd_card_t *card, snd_device_type_t type, + void *device_data, snd_device_ops_t *ops) +{ + snd_device_t *dev; + + snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO); + dev = (snd_device_t *) snd_magic_kcalloc(snd_device_t, 0, GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + dev->card = card; + dev->type = type; + dev->state = SNDRV_DEV_BUILD; + dev->device_data = device_data; + dev->ops = ops; + list_add(&dev->list, &card->devices); /* add to the head of list */ + return 0; +} + +/** + * snd_device_free - release the device from the card + * @card: the card instance + * @device_data: the data pointer to release + * + * Removes the device from the list on the card and invokes the + * callback, dev_unregister or dev_free, corresponding to the state. + * Then release the device. + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ +int snd_device_free(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + + snd_assert(card != NULL, return -ENXIO); + snd_assert(device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + /* unlink */ + list_del(&dev->list); + if ((dev->state == SNDRV_DEV_REGISTERED || dev->state == SNDRV_DEV_DISCONNECTED) && + dev->ops->dev_unregister) { + if (dev->ops->dev_unregister(dev)) + snd_printk(KERN_ERR "device unregister failure\n"); + } else { + if (dev->ops->dev_free) { + if (dev->ops->dev_free(dev)) + snd_printk(KERN_ERR "device free failure\n"); + } + } + snd_magic_kfree(dev); + return 0; + } + snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0)); + return -ENXIO; +} + +/** + * snd_device_free - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ +int snd_device_disconnect(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + + snd_assert(card != NULL, return -ENXIO); + snd_assert(device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_disconnect) { + if (dev->ops->dev_disconnect(dev)) + snd_printk(KERN_ERR "device disconnect failure\n"); + dev->state = SNDRV_DEV_DISCONNECTED; + } + return 0; + } + snd_printd("device disconnect %p (from %p), not found\n", device_data, __builtin_return_address(0)); + return -ENXIO; +} + +/** + * snd_device_register - register the device + * @card: the card instance + * @device_data: the data pointer to register + * + * Registers the device which was already created via + * snd_device_new(). Usually this is called from snd_card_register(), + * but it can be called later if any new devices are created after + * invokation of snd_card_register(). + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ +int snd_device_register(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + int err; + + snd_assert(card != NULL && device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { + if ((err = dev->ops->dev_register(dev)) < 0) + return err; + dev->state = SNDRV_DEV_REGISTERED; + return 0; + } + return -EBUSY; + } + snd_BUG(); + return -ENXIO; +} + +/* + * register all the devices on the card. + * called from init.c + */ +int snd_device_register_all(snd_card_t *card) +{ + struct list_head *list; + snd_device_t *dev; + int err; + + snd_assert(card != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { + if ((err = dev->ops->dev_register(dev)) < 0) + return err; + dev->state = SNDRV_DEV_REGISTERED; + } + } + return 0; +} + +/* + * disconnect all the devices on the card. + * called from init.c + */ +int snd_device_disconnect_all(snd_card_t *card) +{ + snd_device_t *dev; + struct list_head *list; + int err = 0; + + snd_assert(card != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (snd_device_disconnect(card, dev->device_data) < 0) + err = -ENXIO; + } + return err; +} + +/* + * release all the devices on the card. + * called from init.c + */ +int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd) +{ + snd_device_t *dev; + struct list_head *list; + int err; + unsigned int range_low, range_high; + + snd_assert(card != NULL, return -ENXIO); + range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; + range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; + __again: + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->type >= range_low && dev->type <= range_high) { + if ((err = snd_device_free(card, dev->device_data)) < 0) + return err; + goto __again; + } + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/hwdep.c linux/sound/core/hwdep.c --- linux-2.4.21-rc1.orig/sound/core/hwdep.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/hwdep.c 2003-02-19 08:37:04.000000000 -0700 @@ -0,0 +1,501 @@ +/* + * Hardware dependent layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Hardware dependent layer"); +MODULE_LICENSE("GPL"); + +snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS]; + +static DECLARE_MUTEX(register_mutex); + +static int snd_hwdep_free(snd_hwdep_t *hwdep); +static int snd_hwdep_dev_free(snd_device_t *device); +static int snd_hwdep_dev_register(snd_device_t *device); +static int snd_hwdep_dev_unregister(snd_device_t *device); + +/* + + */ + +static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.llseek) + return hw->ops.llseek(hw, file, offset, orig); + return -ENXIO; +} + +static ssize_t snd_hwdep_read(struct file * file, char *buf, size_t count, loff_t *offset) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.read) + return hw->ops.read(hw, buf, count, offset); + return -ENXIO; +} + +static ssize_t snd_hwdep_write(struct file * file, const char *buf, size_t count, loff_t *offset) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.write) + return hw->ops.write(hw, buf, count, offset); + return -ENXIO; +} + +static int snd_hwdep_open(struct inode *inode, struct file * file) +{ + int major = major(inode->i_rdev); + int cardnum; + int device; + snd_hwdep_t *hw; + int err; + wait_queue_t wait; + + switch (major) { + case CONFIG_SND_MAJOR: + cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_HWDEP; + break; +#ifdef CONFIG_SND_OSSEMUL + case SOUND_MAJOR: + cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + device = 0; + break; +#endif + default: + return -ENXIO; + } + cardnum %= SNDRV_CARDS; + device %= SNDRV_MINOR_HWDEPS; + hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device]; + + snd_assert(hw != NULL, return -ENODEV); + if (!hw->ops.open) + return -ENXIO; +#ifdef CONFIG_SND_OSSEMUL + if (major == SOUND_MAJOR && hw->oss_type < 0) + return -ENXIO; +#endif + init_waitqueue_entry(&wait, current); + add_wait_queue(&hw->open_wait, &wait); + down(&hw->open_mutex); + while (1) { + if (hw->exclusive && hw->used > 0) { + err = -EBUSY; + break; + } + err = hw->ops.open(hw, file); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&hw->open_wait, &wait); + if (err >= 0) { + file->private_data = hw; + hw->used++; + } + up(&hw->open_mutex); + return err; +} + +static int snd_hwdep_release(struct inode *inode, struct file * file) +{ + int err = -ENXIO; + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + down(&hw->open_mutex); + if (hw->ops.release) { + err = hw->ops.release(hw, file); + wake_up(&hw->open_wait); + } + if (hw->used > 0) + hw->used--; + up(&hw->open_mutex); + return -ENXIO; +} + +static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return 0); + if (hw->ops.poll) + return hw->ops.poll(hw, file, wait); + return 0; +} + +static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t *_info) +{ + snd_hwdep_info_t info; + + memset(&info, 0, sizeof(info)); + info.card = hw->card->number; + strncpy(info.id, hw->id, sizeof(info.id) - 1); + strncpy(info.name, hw->name, sizeof(info.name) - 1); + info.iface = hw->iface; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *_info) +{ + snd_hwdep_dsp_status_t info; + int err; + + if (! hw->ops.dsp_status) + return -ENXIO; + memset(&info, 0, sizeof(info)); + info.dsp_loaded = hw->dsp_loaded; + if ((err = hw->ops.dsp_status(hw, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *_info) +{ + snd_hwdep_dsp_image_t info; + int err; + + if (! hw->ops.dsp_load || ! hw->ops.dsp_status) + return -ENXIO; + memset(&info, 0, sizeof(info)); + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + /* check whether the dsp was already loaded */ + if (hw->dsp_loaded & (1 << info.index)) + return -EBUSY; + if (verify_area(VERIFY_READ, info.image, info.length)) + return -EFAULT; + err = hw->ops.dsp_load(hw, &info); + if (err < 0) + return err; + hw->dsp_loaded |= (1 << info.index); + return 0; +} + +static int snd_hwdep_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + switch (cmd) { + case SNDRV_HWDEP_IOCTL_PVERSION: + return put_user(SNDRV_HWDEP_VERSION, (int *)arg); + case SNDRV_HWDEP_IOCTL_INFO: + return snd_hwdep_info(hw, (snd_hwdep_info_t *)arg); + case SNDRV_HWDEP_IOCTL_DSP_STATUS: + return snd_hwdep_dsp_status(hw, (snd_hwdep_dsp_status_t *)arg); + case SNDRV_HWDEP_IOCTL_DSP_LOAD: + return snd_hwdep_dsp_load(hw, (snd_hwdep_dsp_image_t *)arg); + } + if (hw->ops.ioctl) + return hw->ops.ioctl(hw, file, cmd, arg); + return -ENOTTY; +} + +static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.mmap) + return hw->ops.mmap(hw, file, vma); + return -ENXIO; +} + +static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_MINOR_HWDEPS; + switch (cmd) { + case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_MINOR_HWDEPS) { + if (snd_hwdep_devices[tmp + device]) + break; + device++; + } + if (device >= SNDRV_MINOR_HWDEPS) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_HWDEP_INFO: + { + snd_hwdep_info_t *info = (snd_hwdep_info_t *)arg; + int device; + snd_hwdep_t *hwdep; + + if (get_user(device, &info->device)) + return -EFAULT; + if (device < 0 || device >= SNDRV_MINOR_HWDEPS) + return -ENXIO; + hwdep = snd_hwdep_devices[tmp + device]; + if (hwdep == NULL) + return -ENXIO; + return snd_hwdep_info(hwdep, info); + } + } + return -ENOIOCTLCMD; +} + +/* + + */ + +static struct file_operations snd_hwdep_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .llseek = snd_hwdep_llseek, + .read = snd_hwdep_read, + .write = snd_hwdep_write, + .open = snd_hwdep_open, + .release = snd_hwdep_release, + .poll = snd_hwdep_poll, + .ioctl = snd_hwdep_ioctl, + .mmap = snd_hwdep_mmap, +}; + +static snd_minor_t snd_hwdep_reg = +{ + .comment = "hardware dependent", + .f_ops = &snd_hwdep_f_ops, +}; + +/** + * snd_hwdep_new - create a new hwdep instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero-based) + * @rhwdep: the pointer to store the new hwdep instance + * + * Creates a new hwdep instance with the given index on the card. + * The callbacks (hwdep->ops) must be set on the returned instance + * after this call manually by the caller. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hwdep; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_hwdep_dev_free, + .dev_register = snd_hwdep_dev_register, + .dev_unregister = snd_hwdep_dev_unregister + }; + + snd_assert(rhwdep != NULL, return -EINVAL); + *rhwdep = NULL; + snd_assert(card != NULL, return -ENXIO); + hwdep = snd_magic_kcalloc(snd_hwdep_t, 0, GFP_KERNEL); + if (hwdep == NULL) + return -ENOMEM; + hwdep->card = card; + hwdep->device = device; + if (id) { + strncpy(hwdep->id, id, sizeof(hwdep->id) - 1); + } +#ifdef CONFIG_SND_OSSEMUL + hwdep->oss_type = -1; +#endif + if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) { + snd_hwdep_free(hwdep); + return err; + } + init_waitqueue_head(&hwdep->open_wait); + init_MUTEX(&hwdep->open_mutex); + *rhwdep = hwdep; + return 0; +} + +static int snd_hwdep_free(snd_hwdep_t *hwdep) +{ + snd_assert(hwdep != NULL, return -ENXIO); + if (hwdep->private_free) + hwdep->private_free(hwdep); + snd_magic_kfree(hwdep); + return 0; +} + +static int snd_hwdep_dev_free(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + return snd_hwdep_free(hwdep); +} + +static int snd_hwdep_dev_register(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + int idx, err; + char name[32]; + + down(®ister_mutex); + idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; + if (snd_hwdep_devices[idx]) { + up(®ister_mutex); + return -EBUSY; + } + snd_hwdep_devices[idx] = hwdep; + sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, + hwdep->card, hwdep->device, + &snd_hwdep_reg, name)) < 0) { + snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n", + hwdep->card->number, hwdep->device); + snd_hwdep_devices[idx] = NULL; + up(®ister_mutex); + return err; + } +#ifdef CONFIG_SND_OSSEMUL + hwdep->ossreg = 0; + if (hwdep->oss_type >= 0) { + if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { + snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n"); + } else { + if (snd_register_oss_device(hwdep->oss_type, + hwdep->card, hwdep->device, + &snd_hwdep_reg, hwdep->oss_dev) < 0) { + snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n", + hwdep->card->number, hwdep->device); + } else + hwdep->ossreg = 1; + } + } +#endif + up(®ister_mutex); + return 0; +} + +static int snd_hwdep_dev_unregister(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + int idx; + + snd_assert(hwdep != NULL, return -ENXIO); + down(®ister_mutex); + idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; + if (snd_hwdep_devices[idx] != hwdep) { + up(®ister_mutex); + return -EINVAL; + } +#ifdef CONFIG_SND_OSSEMUL + if (hwdep->ossreg) + snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); +#endif + snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); + snd_hwdep_devices[idx] = NULL; + up(®ister_mutex); + return snd_hwdep_free(hwdep); +} + +/* + * Info interface + */ + +static void snd_hwdep_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + int idx; + snd_hwdep_t *hwdep; + + down(®ister_mutex); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) { + hwdep = snd_hwdep_devices[idx]; + if (hwdep == NULL) + continue; + snd_iprintf(buffer, "%02i-%02i: %s\n", + idx / SNDRV_MINOR_HWDEPS, + idx % SNDRV_MINOR_HWDEPS, + hwdep->name); + } + up(®ister_mutex); +} + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_hwdep_proc_entry = NULL; + +static int __init alsa_hwdep_init(void) +{ + snd_info_entry_t *entry; + + memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices)); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 512; + entry->c.text.read = snd_hwdep_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_hwdep_proc_entry = entry; + snd_ctl_register_ioctl(snd_hwdep_control_ioctl); + return 0; +} + +static void __exit alsa_hwdep_exit(void) +{ + snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); + if (snd_hwdep_proc_entry) { + snd_info_unregister(snd_hwdep_proc_entry); + snd_hwdep_proc_entry = NULL; + } +} + +module_init(alsa_hwdep_init) +module_exit(alsa_hwdep_exit) + +EXPORT_SYMBOL(snd_hwdep_new); diff -urN linux-2.4.21-rc1.orig/sound/core/info.c linux/sound/core/info.c --- linux-2.4.21-rc1.orig/sound/core/info.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/info.c 2003-02-28 09:37:25.000000000 -0700 @@ -0,0 +1,1214 @@ +/* + * Information interface for ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * + */ + +int snd_info_check_reserved_words(const char *str) +{ + static char *reserved[] = + { + "dev", + "version", + "meminfo", + "memdebug", + "detect", + "devices", + "oss", + "cards", + "timers", + "synth", + "pcm", + "seq", + NULL + }; + char **xstr = reserved; + + while (*xstr) { + if (!strcmp(*xstr, str)) + return 0; + xstr++; + } + if (!strncmp(str, "card", 4)) + return 0; + return 1; +} + +#ifdef CONFIG_PROC_FS + +extern int major; +extern struct file_operations snd_fops; + +static DECLARE_MUTEX(info_mutex); + +typedef struct _snd_info_private_data { + snd_info_buffer_t *rbuffer; + snd_info_buffer_t *wbuffer; + snd_info_entry_t *entry; + void *file_private_data; +} snd_info_private_data_t; + +static int snd_info_version_init(void); +static int snd_info_version_done(void); + + +/** + * snd_iprintf - printf on the procfs buffer + * @buffer: the procfs buffer + * @fmt: the printf format + * + * Outputs the string on the procfs buffer just like printf(). + * + * Returns the size of output string. + */ +int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) +{ + va_list args; + int res; + char sbuffer[512]; + + if (buffer->stop || buffer->error) + return 0; + va_start(args, fmt); + res = vsnprintf(sbuffer, sizeof(sbuffer), fmt, args); + va_end(args); + if (buffer->size + res >= buffer->len) { + buffer->stop = 1; + return 0; + } + strcpy(buffer->curr, sbuffer); + buffer->curr += res; + buffer->size += res; + return res; +} + +/* + + */ + +struct proc_dir_entry *snd_proc_root = NULL; +struct proc_dir_entry *snd_proc_dev = NULL; +snd_info_entry_t *snd_seq_root = NULL; +#ifdef CONFIG_SND_OSSEMUL +snd_info_entry_t *snd_oss_root = NULL; +#endif + +#ifdef LINUX_2_2 +static void snd_info_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + +static inline void snd_info_entry_prepare(struct proc_dir_entry *de) +{ + de->fill_inode = snd_info_fill_inode; +} + +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) +{ + if (parent && de) + proc_unregister(parent, de->low_ino); +} +#else +static inline void snd_info_entry_prepare(struct proc_dir_entry *de) +{ + de->owner = THIS_MODULE; +} + +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) +{ + if (de) + remove_proc_entry(de->name, parent); +} +#endif + +static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + loff_t ret; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + entry = data->entry; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) + lock_kernel(); +#endif + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + ret = file->f_pos; + goto out; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + ret = file->f_pos; + goto out; + case 2: /* SEEK_END */ + default: + ret = -EINVAL; + goto out; + } + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->llseek) { + ret = entry->c.ops->llseek(entry, + data->file_private_data, + file, offset, orig); + goto out; + } + break; + } + ret = -ENXIO; +out: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) + unlock_kernel(); +#endif + return ret; +} + +static ssize_t snd_info_entry_read(struct file *file, char *buffer, + size_t count, loff_t * offset) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + snd_info_buffer_t *buf; + long size = 0, size1; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + snd_assert(data != NULL, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + buf = data->rbuffer; + if (buf == NULL) + return -EIO; + if (file->f_pos >= (long)buf->size) + return 0; + size = buf->size < count ? buf->size : count; + size1 = buf->size - file->f_pos; + if (size1 < size) + size = size1; + if (copy_to_user(buffer, buf->buffer + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->read) + return entry->c.ops->read(entry, + data->file_private_data, + file, buffer, count); + if (size > 0) + file->f_pos += size; + break; + } + return size; +} + +static ssize_t snd_info_entry_write(struct file *file, const char *buffer, + size_t count, loff_t * offset) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + snd_info_buffer_t *buf; + long size = 0, size1; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + snd_assert(data != NULL, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + buf = data->wbuffer; + if (buf == NULL) + return -EIO; + if (file->f_pos < 0) + return -EINVAL; + if (file->f_pos >= (long)buf->len) + return -ENOMEM; + size = buf->len < count ? buf->len : count; + size1 = buf->len - file->f_pos; + if (size1 < size) + size = size1; + if (copy_from_user(buf->buffer + file->f_pos, buffer, size)) + return -EFAULT; + if ((long)buf->size < file->f_pos + size) + buf->size = file->f_pos + size; + file->f_pos += size; + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->write) + return entry->c.ops->write(entry, + data->file_private_data, + file, buffer, count); + if (size > 0) + file->f_pos += size; + break; + } + return size; +} + +static int snd_info_entry_open(struct inode *inode, struct file *file) +{ + snd_info_entry_t *entry; + snd_info_private_data_t *data; + snd_info_buffer_t *buffer; + struct proc_dir_entry *p; + int mode, err; + + down(&info_mutex); + p = PDE(inode); + entry = p == NULL ? NULL : (snd_info_entry_t *)p->data; + if (entry == NULL) { + up(&info_mutex); + return -ENODEV; + } + if (!try_module_get(entry->module)) { + err = -EFAULT; + goto __error1; + } + mode = file->f_flags & O_ACCMODE; + if (mode == O_RDONLY || mode == O_RDWR) { + if ((entry->content == SNDRV_INFO_CONTENT_TEXT && + !entry->c.text.read_size) || + (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->read == NULL) || + entry->content == SNDRV_INFO_CONTENT_DEVICE) { + err = -ENODEV; + goto __error; + } + } + if (mode == O_WRONLY || mode == O_RDWR) { + if ((entry->content == SNDRV_INFO_CONTENT_TEXT && + !entry->c.text.write_size) || + (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->write == NULL) || + entry->content == SNDRV_INFO_CONTENT_DEVICE) { + err = -ENODEV; + goto __error; + } + } + data = snd_magic_kcalloc(snd_info_private_data_t, 0, GFP_KERNEL); + if (data == NULL) { + err = -ENOMEM; + goto __error; + } + data->entry = entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + if (mode == O_RDONLY || mode == O_RDWR) { + buffer = (snd_info_buffer_t *) + snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + if (buffer == NULL) { + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->len = (entry->c.text.read_size + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + buffer->buffer = vmalloc(buffer->len); + if (buffer->buffer == NULL) { + kfree(buffer); + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->curr = buffer->buffer; + data->rbuffer = buffer; + } + if (mode == O_WRONLY || mode == O_RDWR) { + buffer = (snd_info_buffer_t *) + snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + if (buffer == NULL) { + if (mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->len = (entry->c.text.write_size + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + buffer->buffer = vmalloc(buffer->len); + if (buffer->buffer == NULL) { + if (mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + kfree(buffer); + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->curr = buffer->buffer; + data->wbuffer = buffer; + } + break; + case SNDRV_INFO_CONTENT_DATA: /* data */ + if (entry->c.ops->open) { + if ((err = entry->c.ops->open(entry, mode, + &data->file_private_data)) < 0) { + snd_magic_kfree(data); + goto __error; + } + } + break; + } + file->private_data = data; + up(&info_mutex); + if (entry->content == SNDRV_INFO_CONTENT_TEXT && + (mode == O_RDONLY || mode == O_RDWR)) { + if (entry->c.text.read) { + down(&entry->access); + entry->c.text.read(entry, data->rbuffer); + up(&entry->access); + } + } + return 0; + + __error: + module_put(entry->module); + __error1: + up(&info_mutex); + return err; +} + +static int snd_info_entry_release(struct inode *inode, struct file *file) +{ + snd_info_entry_t *entry; + snd_info_private_data_t *data; + int mode; + + mode = file->f_flags & O_ACCMODE; + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + if (mode == O_RDONLY || mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + if (mode == O_WRONLY || mode == O_RDWR) { + if (entry->c.text.write) { + entry->c.text.write(entry, data->wbuffer); + if (data->wbuffer->error) { + snd_printk(KERN_WARNING "data write error to %s (%i)\n", + entry->name, + data->wbuffer->error); + } + } + vfree(data->wbuffer->buffer); + kfree(data->wbuffer); + } + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->release) + entry->c.ops->release(entry, mode, + data->file_private_data); + break; + } + module_put(entry->module); + snd_magic_kfree(data); + return 0; +} + +static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + unsigned int mask; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + mask = 0; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->poll) + return entry->c.ops->poll(entry, + data->file_private_data, + file, wait); + if (entry->c.ops->read) + mask |= POLLIN | POLLRDNORM; + if (entry->c.ops->write) + mask |= POLLOUT | POLLWRNORM; + break; + } + return mask; +} + +static int snd_info_entry_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->ioctl) + return entry->c.ops->ioctl(entry, + data->file_private_data, + file, cmd, arg); + break; + } + return -ENOTTY; +} + +static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file->f_dentry->d_inode; + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->mmap) + return entry->c.ops->mmap(entry, + data->file_private_data, + inode, file, vma); + break; + } + return -ENXIO; +} + +static struct file_operations snd_info_entry_operations = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .llseek = snd_info_entry_llseek, + .read = snd_info_entry_read, + .write = snd_info_entry_write, + .poll = snd_info_entry_poll, + .ioctl = snd_info_entry_ioctl, + .mmap = snd_info_entry_mmap, + .open = snd_info_entry_open, + .release = snd_info_entry_release, +}; + +#ifdef LINUX_2_2 +static struct inode_operations snd_info_entry_inode_operations = +{ + &snd_info_entry_operations, /* default sound info directory file-ops */ +}; + +static struct inode_operations snd_info_device_inode_operations = +{ + &snd_fops, /* default sound info directory file-ops */ +}; +#endif /* LINUX_2_2 */ + +static int snd_info_card_readlink(struct dentry *dentry, + char *buffer, int buflen) +{ + char *s = PDE(dentry->d_inode)->data; +#ifndef LINUX_2_2 + return vfs_readlink(dentry, buffer, buflen, s); +#else + int len; + + if (s == NULL) + return -EIO; + len = strlen(s); + if (len > buflen) + len = buflen; + if (copy_to_user(buffer, s, len)) + return -EFAULT; + return len; +#endif +} + +#ifndef LINUX_2_2 +static int snd_info_card_followlink(struct dentry *dentry, + struct nameidata *nd) +{ + char *s = PDE(dentry->d_inode)->data; + return vfs_follow_link(nd, s); +} +#else +static struct dentry *snd_info_card_followlink(struct dentry *dentry, + struct dentry *base, + unsigned int follow) +{ + char *s = PDE(dentry->d_inode)->data; + return lookup_dentry(s, base, follow); +} +#endif + +#ifdef LINUX_2_2 +static struct file_operations snd_info_card_link_operations = +{ + NULL +}; +#endif + +struct inode_operations snd_info_card_link_inode_operations = +{ +#ifdef LINUX_2_2 + .default_file_ops = &snd_info_card_link_operations, +#endif + .readlink = snd_info_card_readlink, + .follow_link = snd_info_card_followlink, +}; + +/** + * snd_create_proc_entry - create a procfs entry + * @name: the name of the proc file + * @mode: the file permission bits, S_Ixxx + * @parent: the parent proc-directory entry + * + * Creates a new proc file entry with the given name and permission + * on the given directory. + * + * Returns the pointer of new instance or NULL on failure. + */ +struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *p; + p = create_proc_entry(name, mode, parent); + if (p) + snd_info_entry_prepare(p); + return p; +} + +int __init snd_info_init(void) +{ + struct proc_dir_entry *p; + + p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); + if (p == NULL) + return -ENOMEM; + snd_proc_root = p; + p = snd_create_proc_entry("dev", S_IFDIR | S_IRUGO | S_IXUGO, snd_proc_root); + if (p == NULL) + return -ENOMEM; + snd_proc_dev = p; +#ifdef CONFIG_SND_OSSEMUL + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_oss_root = entry; + } +#endif +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_seq_root = entry; + } +#endif + snd_info_version_init(); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_info_init(); +#endif + snd_minor_info_init(); +#ifdef CONFIG_SND_OSSEMUL + snd_minor_info_oss_init(); +#endif + snd_card_info_init(); + return 0; +} + +int __exit snd_info_done(void) +{ + snd_card_info_done(); +#ifdef CONFIG_SND_OSSEMUL + snd_minor_info_oss_done(); +#endif + snd_minor_info_done(); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_info_done(); +#endif + snd_info_version_done(); + if (snd_proc_root) { +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_root) + snd_info_unregister(snd_seq_root); +#endif +#ifdef CONFIG_SND_OSSEMUL + if (snd_oss_root) + snd_info_unregister(snd_oss_root); +#endif + snd_remove_proc_entry(snd_proc_root, snd_proc_dev); + snd_remove_proc_entry(&proc_root, snd_proc_root); + } + return 0; +} + +/* + + */ + + +/* + * create a card proc file + * called from init.c + */ +int snd_info_card_create(snd_card_t * card) +{ + char str[8]; + snd_info_entry_t *entry; + + snd_assert(card != NULL, return -ENXIO); + + sprintf(str, "card%i", card->number); + if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + card->proc_root = entry; + return 0; +} + +/* + * register the card proc file + * called from init.c + */ +int snd_info_card_register(snd_card_t * card) +{ + char *s; + struct proc_dir_entry *p; + + snd_assert(card != NULL, return -ENXIO); + + if (!strcmp(card->id, card->proc_root->name)) + return 0; + + s = snd_kmalloc_strdup(card->proc_root->name, GFP_KERNEL); + if (s == NULL) + return -ENOMEM; + p = snd_create_proc_entry(card->id, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, snd_proc_root); + if (p == NULL) + return -ENOMEM; + p->data = s; +#ifndef LINUX_2_2 + p->owner = card->module; + p->proc_iops = &snd_info_card_link_inode_operations; +#else + p->ops = &snd_info_card_link_inode_operations; +#endif + card->proc_root_link = p; + return 0; +} + +/* + * de-register the card proc file + * called from init.c + */ +int snd_info_card_free(snd_card_t * card) +{ + void *data; + + snd_assert(card != NULL, return -ENXIO); + if (card->proc_root_link) { + data = card->proc_root_link->data; + card->proc_root_link->data = NULL; + kfree(data); + snd_remove_proc_entry(snd_proc_root, card->proc_root_link); + card->proc_root_link = NULL; + } + if (card->proc_root) { + snd_info_unregister(card->proc_root); + card->proc_root = NULL; + } + return 0; +} + + +/** + * snd_info_get_line - read one line from the procfs buffer + * @buffer: the procfs buffer + * @line: the buffer to store + * @len: the max. buffer size - 1 + * + * Reads one line from the buffer and stores the string. + * + * Returns zero if successful, or 1 if error or EOF. + */ +int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) +{ + int c = -1; + + if (len <= 0 || buffer->stop || buffer->error) + return 1; + while (--len > 0) { + c = *buffer->curr++; + if (c == '\n') { + if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { + buffer->stop = 1; + } + break; + } + *line++ = c; + if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { + buffer->stop = 1; + break; + } + } + while (c != '\n' && !buffer->stop) { + c = *buffer->curr++; + if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { + buffer->stop = 1; + } + } + *line = '\0'; + return 0; +} + +/** + * snd_info_get_line - parse a string token + * @dest: the buffer to store the string token + * @src: the original string + * @len: the max. length of token - 1 + * + * Parses the original string and copy a token to the given + * string buffer. + * + * Returns the updated pointer of the original string so that + * it can be used for the next call. + */ +char *snd_info_get_str(char *dest, char *src, int len) +{ + int c; + + while (*src == ' ' || *src == '\t') + src++; + if (*src == '"' || *src == '\'') { + c = *src++; + while (--len > 0 && *src && *src != c) { + *dest++ = *src++; + } + if (*src == c) + src++; + } else { + while (--len > 0 && *src && *src != ' ' && *src != '\t') { + *dest++ = *src++; + } + } + *dest = 0; + while (*src == ' ' || *src == '\t') + src++; + return src; +} + +/** + * snd_info_create_entry - create an info entry + * @name: the proc file name + * + * Creates an info entry with the given file name and initializes as + * the default state. + * + * Usually called from other functions such as + * snd_info_create_card_entry(). + * + * Returns the pointer of the new instance, or NULL on failure. + */ +static snd_info_entry_t *snd_info_create_entry(const char *name) +{ + snd_info_entry_t *entry; + entry = snd_magic_kcalloc(snd_info_entry_t, 0, GFP_KERNEL); + if (entry == NULL) + return NULL; + entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); + if (entry->name == NULL) { + snd_magic_kfree(entry); + return NULL; + } + entry->mode = S_IFREG | S_IRUGO; + entry->content = SNDRV_INFO_CONTENT_TEXT; + init_MUTEX(&entry->access); + return entry; +} + +/** + * snd_info_create_module_entry - create an info entry for the given module + * @module: the module pointer + * @name: the file name + * @parent: the parent directory + * + * Creates a new info entry and assigns it to the given module. + * + * Returns the pointer of the new instance, or NULL on failure. + */ +snd_info_entry_t *snd_info_create_module_entry(struct module a˜;qŠïÁåTvî`pT^9ÖÅ-)bÛ«ô$ýÓwÑr *name, + snd_info_entry_t *parent) +{ + snd_info_entry_t *entry = snd_info_create_entry(name); + if (entry) { + entry->module = module; + entry->parent = parent; + } + return entry; +} + +/** + * snd_info_create_card_entry - create an info entry for the given card + * @card: the card instance + * @name: the file name + * @parent: the parent directory + * + * Creates a new info entry and assigns it to the given card. + * + * Returns the pointer of the new instance, or NULL on failure. + */ +snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, + const char *name, + snd_info_entry_t * parent) +{ + snd_info_entry_t *entry = snd_info_create_entry(name); + if (entry) { + entry->module = card->module; + entry->card = card; + entry->parent = parent; + } + return entry; +} + +static int snd_info_dev_free_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_free_entry(entry); + return 0; +} + +static int snd_info_dev_register_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + return snd_info_register(entry); +} + +static int snd_info_dev_unregister_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + return snd_info_unregister(entry); +} + +/** + * snd_card_proc_new - create an info entry for the given card + * @card: the card instance + * @name: the file name + * @entryp: the pointer to store the new info entry + * + * Creates a new info entry and assigns it to the given card. + * Unlike snd_info_create_card_entry(), this function registers the + * info entry as an ALSA device component, so that it can be + * unregistered/released without explicit call. + * Also, you don't have to register this entry via snd_info_register(), + * since this will be registered by snd_card_register() automatically. + * + * The parent is assumed as card->proc_root. + * + * For releasing this entry, use snd_device_free() instead of + * snd_info_free_entry(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_card_proc_new(snd_card_t *card, const char *name, + snd_info_entry_t **entryp) +{ + static snd_device_ops_t ops = { + .dev_free = snd_info_dev_free_entry, + .dev_register = snd_info_dev_register_entry, + // .dev_disconnect = snd_info_dev_disconnect_entry, + .dev_unregister = snd_info_dev_unregister_entry + }; + snd_info_entry_t *entry; + int err; + + entry = snd_info_create_card_entry(card, name, card->proc_root); + if (! entry) + return -ENOMEM; + if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { + snd_info_free_entry(entry); + return err; + } + if (entryp) + *entryp = entry; + return 0; +} + +/** + * snd_info_free_entry - release the info entry + * @entry: the info entry + * + * Releases the info entry. Don't call this after registered. + */ +void snd_info_free_entry(snd_info_entry_t * entry) +{ + if (entry == NULL) + return; + if (entry->name) + kfree((char *)entry->name); + if (entry->private_free) + entry->private_free(entry); + snd_magic_kfree(entry); +} + +#ifdef LINUX_2_2 +static void snd_info_device_fill_inode(struct inode *inode, int fill) +{ + struct proc_dir_entry *de; + snd_info_entry_t *entry; + + if (!fill) { + MOD_DEC_USE_COUNT; + return; + } + MOD_INC_USE_COUNT; + de = PDE(inode); + if (de == NULL) + return; + entry = (snd_info_entry_t *) de->data; + if (entry == NULL) + return; + inode->i_gid = device_gid; + inode->i_uid = device_uid; + inode->i_rdev = MKDEV(entry->c.device.major, entry->c.device.minor); +} + +static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry) +{ + de->fill_inode = snd_info_device_fill_inode; +} +#else +static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry) +{ + de->rdev = mk_kdev(entry->c.device.major, entry->c.device.minor); + de->owner = THIS_MODULE; +} +#endif /* LINUX_2_2 */ + +/* + * create a procfs device file + */ +snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode) +{ + unsigned short _major = number >> 16; + unsigned short minor = (unsigned short) number; + snd_info_entry_t *entry; + struct proc_dir_entry *p = NULL; + + if (!_major) + _major = major; + if (!mode) + mode = S_IFCHR | S_IRUGO | S_IWUGO; + mode &= (device_mode & (S_IRUGO | S_IWUGO)) | S_IFCHR | S_IFBLK; + entry = snd_info_create_module_entry(THIS_MODULE, name, NULL); + if (entry == NULL) + return NULL; + entry->content = SNDRV_INFO_CONTENT_DEVICE; + entry->mode = mode; + entry->c.device.major = _major; + entry->c.device.minor = minor; + down(&info_mutex); + p = create_proc_entry(entry->name, entry->mode, snd_proc_dev); + if (p) { + snd_info_device_entry_prepare(p, entry); +#ifdef LINUX_2_2 + p->ops = &snd_info_device_inode_operations; +#endif + } else { + up(&info_mutex); + snd_info_free_entry(entry); + return NULL; + } + p->gid = device_gid; + p->uid = device_uid; + p->data = (void *) entry; + entry->p = p; + up(&info_mutex); +#ifdef CONFIG_DEVFS_FS + if (strncmp(name, "controlC", 8)) { /* created in sound.c */ + char dname[32]; + sprintf(dname, "snd/%s", name); + devfs_register(NULL, dname, DEVFS_FL_DEFAULT, + _major, minor, mode, + &snd_fops, NULL); + } +#endif + return entry; +} + +/* + * release a procfs device file + */ +void snd_info_free_device(snd_info_entry_t * entry) +{ + snd_runtime_check(entry, return); + down(&info_mutex); + snd_remove_proc_entry(snd_proc_dev, entry->p); + up(&info_mutex); + if (entry->p && strncmp(entry->name, "controlC", 8)) + devfs_remove("snd/%s", entry->name); + snd_info_free_entry(entry); +} + +/** + * snd_info_register - register the info entry + * @entry: the info entry + * + * Registers the proc info entry. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_info_register(snd_info_entry_t * entry) +{ + struct proc_dir_entry *root, *p = NULL; + + snd_assert(entry != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + down(&info_mutex); + p = snd_create_proc_entry(entry->name, entry->mode, root); + if (!p) { + up(&info_mutex); + return -ENOMEM; + } +#ifndef LINUX_2_2 + p->owner = entry->module; +#endif + if (!S_ISDIR(entry->mode)) { +#ifndef LINUX_2_2 + p->proc_fops = &snd_info_entry_operations; +#else + p->ops = &snd_info_entry_inode_operations; +#endif + } + p->size = entry->size; + p->data = entry; + entry->p = p; + up(&info_mutex); + return 0; +} + +/** + * snd_info_unregister - de-register the info entry + * @entry: the info entry + * + * De-registers the info entry and releases the instance. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_info_unregister(snd_info_entry_t * entry) +{ + struct proc_dir_entry *root; + + snd_assert(entry != NULL && entry->p != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + snd_assert(root, return -ENXIO); + down(&info_mutex); + snd_remove_proc_entry(root, entry->p); + up(&info_mutex); + snd_info_free_entry(entry); + return 0; +} + +/* + + */ + +static snd_info_entry_t *snd_info_version_entry = NULL; + +static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + static char *kernel_version = UTS_RELEASE; + + snd_iprintf(buffer, + "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n" + "Compiled on " __DATE__ " for kernel %s" +#ifdef CONFIG_SMP + " (SMP)" +#endif +#ifdef MODVERSIONS + " with versioned symbols" +#endif + ".\n", kernel_version); +} + +static int __init snd_info_version_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); + if (entry == NULL) + return -ENOMEM; + entry->c.text.read_size = 256; + entry->c.text.read = snd_info_version_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_info_version_entry = entry; + return 0; +} + +static int __exit snd_info_version_done(void) +{ + if (snd_info_version_entry) + snd_info_unregister(snd_info_version_entry); + return 0; +} + +#endif /* CONFIG_PROC_FS */ diff -urN linux-2.4.21-rc1.orig/sound/core/info_oss.c linux/sound/core/info_oss.c --- linux-2.4.21-rc1.orig/sound/core/info_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/info_oss.c 2002-10-13 07:16:38.000000000 -0600 @@ -0,0 +1,150 @@ +/* + * Information interface for ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) + +/* + * OSS compatible part + */ + +static DECLARE_MUTEX(strings); +static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT]; +static snd_info_entry_t *snd_sndstat_proc_entry; + +int snd_oss_info_register(int dev, int num, char *string) +{ + char *x; + + snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO); + snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO); + down(&strings); + if (string == NULL) { + if ((x = snd_sndstat_strings[num][dev]) != NULL) { + kfree(x); + x = NULL; + } + } else { + x = snd_kmalloc_strdup(string, GFP_KERNEL); + if (x == NULL) { + up(&strings); + return -ENOMEM; + } + } + snd_sndstat_strings[num][dev] = x; + up(&strings); + return 0; +} + +extern void snd_card_info_read_oss(snd_info_buffer_t * buffer); + +static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev) +{ + int idx, ok = -1; + char *str; + + snd_iprintf(buf, "\n%s:", id); + down(&strings); + for (idx = 0; idx < SNDRV_CARDS; idx++) { + str = snd_sndstat_strings[idx][dev]; + if (str) { + if (ok < 0) { + snd_iprintf(buf, "\n"); + ok++; + } + snd_iprintf(buf, "%i: %s\n", idx, str); + } + } + up(&strings); + if (ok < 0) + snd_iprintf(buf, " NOT ENABLED IN CONFIG\n"); + return ok; +} + +static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n"); + snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n", + system_utsname.sysname, + system_utsname.nodename, + system_utsname.release, + system_utsname.version, + system_utsname.machine); + snd_iprintf(buffer, "Config options: 0\n"); + snd_iprintf(buffer, "\nInstalled drivers: \n"); + snd_iprintf(buffer, "Type 10: ALSA emulation\n"); + snd_iprintf(buffer, "\nCard config: \n"); + snd_card_info_read_oss(buffer); + snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO); + snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH); + snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI); + snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS); + snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS); +} + +int snd_info_minor_register(void) +{ + snd_info_entry_t *entry; + + memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 2048; + entry->c.text.read = snd_sndstat_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_sndstat_proc_entry = entry; + return 0; +} + +int snd_info_minor_unregister(void) +{ + if (snd_sndstat_proc_entry) { + snd_info_unregister(snd_sndstat_proc_entry); + snd_sndstat_proc_entry = NULL; + } + return 0; +} + +#else + +int snd_info_minor_register(void) +{ + return 0; +} + +int snd_info_minor_unregister(void) +{ + return 0; +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -urN linux-2.4.21-rc1.orig/sound/core/init.c linux/sound/core/init.c --- linux-2.4.21-rc1.orig/sound/core/init.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/init.c 2003-04-29 06:05:23.000000000 -0600 @@ -0,0 +1,681 @@ +/* + * Initialization routines + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_shutdown_f_ops { + struct file_operations f_ops; + struct snd_shutdown_f_ops *next; +}; + +int snd_cards_count = 0; +unsigned int snd_cards_lock = 0; /* locked for registering/using */ +snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; +rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED; + +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); +#endif + +static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "%s\n", entry->card->id); +} + +/** + * snd_card_new - create and initialize a soundcard structure + * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] + * @xid: card identification (ASCII string) + * @module: top level module for locking + * @extra_size: allocate this extra size after the main soundcard structure + * + * Creates and initializes a soundcard structure. + * + * Returns kmallocated snd_card_t structure. Creates the ALSA control interface + * (which is blocked until snd_card_register function is called). + */ +snd_card_t *snd_card_new(int idx, const char *xid, + struct module *module, int extra_size) +{ + snd_card_t *card; + int err; + + if (extra_size < 0) + extra_size = 0; + card = (snd_card_t *) snd_kcalloc(sizeof(snd_card_t) + extra_size, GFP_KERNEL); + if (card == NULL) + return NULL; + if (xid) { + if (!snd_info_check_reserved_words(xid)) + goto __error; + strncpy(card->id, xid, sizeof(card->id) - 1); + } + write_lock(&snd_card_rwlock); + if (idx < 0) { + int idx2; + for (idx2 = 0; idx2 < snd_ecards_limit; idx2++) + if (!(snd_cards_lock & (1 << idx2))) { + idx = idx2; + break; + } + if (idx < 0 && snd_ecards_limit < SNDRV_CARDS) + /* for dynamically additional devices like hotplug: + * increment the limit if still free slot exists. + */ + idx = snd_ecards_limit++; + } else if (idx < snd_ecards_limit) { + if (snd_cards_lock & (1 << idx)) + idx = -1; /* invalid */ + } else if (idx < SNDRV_CARDS) + snd_ecards_limit = idx + 1; /* increase the limit */ + else + idx = -1; + if (idx < 0) { + write_unlock(&snd_card_rwlock); + if (idx >= snd_ecards_limit) + snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); + goto __error; + } + snd_cards_lock |= 1 << idx; /* lock it */ + write_unlock(&snd_card_rwlock); + card->number = idx; + card->module = module; + INIT_LIST_HEAD(&card->devices); + init_rwsem(&card->controls_rwsem); + rwlock_init(&card->ctl_files_rwlock); + INIT_LIST_HEAD(&card->controls); + INIT_LIST_HEAD(&card->ctl_files); + spin_lock_init(&card->files_lock); + init_waitqueue_head(&card->shutdown_sleep); +#ifdef CONFIG_PM + init_MUTEX(&card->power_lock); + init_waitqueue_head(&card->power_sleep); +#endif + /* the control interface cannot be accessed from the user space until */ + /* snd_cards_bitmask and snd_cards are set with snd_card_register */ + if ((err = snd_ctl_register(card)) < 0) { + snd_printd("unable to register control minors\n"); + goto __error; + } + if ((err = snd_info_card_create(card)) < 0) { + snd_printd("unable to create card info\n"); + goto __error_ctl; + } + if (extra_size > 0) + card->private_data = (char *)card + sizeof(snd_card_t); + return card; + + __error_ctl: + snd_ctl_unregister(card); + __error: + kfree(card); + return NULL; +} + +static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) +{ + return POLLERR | POLLNVAL; +} + +/** + * snd_card_disconnect - disconnect all APIs from the file-operations (user space) + * @card: soundcard structure + * + * Disconnects all APIs from the file-operations (user space). + * + * Returns zero, otherwise a negative error code. + * + * Note: The current implementation replaces all active file->f_op with special + * dummy file operations (they do nothing except release). + */ +int snd_card_disconnect(snd_card_t * card) +{ + struct snd_monitor_file *mfile; + struct file *file; + struct snd_shutdown_f_ops *s_f_ops; + struct file_operations *f_ops, *old_f_ops; + int err; + + spin_lock(&card->files_lock); + if (card->shutdown) { + spin_unlock(&card->files_lock); + return 0; + } + card->shutdown = 1; + spin_unlock(&card->files_lock); + + /* phase 1: disable fops (user space) operations for ALSA API */ + write_lock(&snd_card_rwlock); + snd_cards[card->number] = NULL; + write_unlock(&snd_card_rwlock); + + /* phase 2: replace file->f_op with special dummy operations */ + + spin_lock(&card->files_lock); + mfile = card->files; + while (mfile) { + file = mfile->file; + + /* it's critical part, use endless loop */ + /* we have no room to fail */ + s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC); + if (s_f_ops == NULL) + panic("Atomic allocation failed for snd_shutdown_f_ops!"); + + f_ops = &s_f_ops->f_ops; + + memset(f_ops, 0, sizeof(*f_ops)); +#ifndef LINUX_2_2 + f_ops->owner = file->f_op->owner; +#endif + f_ops->release = file->f_op->release; + f_ops->poll = snd_disconnect_poll; + + s_f_ops->next = card->s_f_ops; + card->s_f_ops = s_f_ops; + + f_ops = fops_get(f_ops); + + old_f_ops = file->f_op; + file->f_op = f_ops; /* must be atomic */ + fops_put(old_f_ops); + + mfile = mfile->next; + } + spin_unlock(&card->files_lock); + + /* phase 3: notify all connected devices about disconnection */ + /* at this point, they cannot respond to any calls except release() */ + + snd_ctl_disconnect(card); + +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); +#endif + + /* notify all devices that we are disconnected */ + err = snd_device_disconnect_all(card); + if (err < 0) + snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); + + return 0; +} + +/** + * snd_card_free - frees given soundcard structure + * @card: soundcard structure + * + * This function releases the soundcard structure and the all assigned + * devices automatically. That is, you don't have to release the devices + * by yourself. + * + * Returns zero. Frees all associated devices and frees the control + * interface associated to given soundcard. + */ +int snd_card_free(snd_card_t * card) +{ + struct snd_shutdown_f_ops *s_f_ops; + + if (card == NULL) + return -EINVAL; + write_lock(&snd_card_rwlock); + snd_cards[card->number] = NULL; + snd_cards_count--; + write_unlock(&snd_card_rwlock); + + /* wait, until all devices are ready for the free operation */ + wait_event(card->shutdown_sleep, card->files == NULL); + +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); +#endif + if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { + snd_printk(KERN_ERR "unable to free all devices (pre)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { + snd_printk(KERN_ERR "unable to free all devices (normal)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_ctl_unregister(card) < 0) { + snd_printk(KERN_ERR "unable to unregister control minors\n"); + /* Not fatal error */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { + snd_printk(KERN_ERR "unable to free all devices (post)\n"); + /* Fatal, but this situation should never occur */ + } + if (card->private_free) + card->private_free(card); + snd_info_free_entry(card->proc_id); + if (snd_info_card_free(card) < 0) { + snd_printk(KERN_WARNING "unable to free card info\n"); + /* Not fatal error */ + } + while (card->s_f_ops) { + s_f_ops = card->s_f_ops; + card->s_f_ops = s_f_ops->next; + kfree(s_f_ops); + } + write_lock(&snd_card_rwlock); + snd_cards_lock &= ~(1 << card->number); + write_unlock(&snd_card_rwlock); + kfree(card); + return 0; +} + +static void snd_card_free_thread(void * __card) +{ + snd_card_t *card = __card; + struct module * module = card->module; + + if (!try_module_get(module)) { + snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number); + module = NULL; + } + + snd_card_free(card); + + module_put(module); +} + +/** + * snd_card_free_in_thread - call snd_card_free() in thread + * @card: soundcard structure + * + * This function schedules the call of snd_card_free() function in a + * work queue. When all devices are released (non-busy), the work + * is woken up and calls snd_card_free(). + * + * When a card can be disconnected at any time by hotplug service, + * this function should be used in disconnect (or detach) callback + * instead of calling snd_card_free() directly. + * + * Returns - zero otherwise a negative error code if the start of thread failed. + */ +int snd_card_free_in_thread(snd_card_t * card) +{ + DECLARE_WORK(works, snd_card_free_thread, card); + + if (card->files == NULL) { + snd_card_free(card); + return 0; + } + if (schedule_work(&works)) + return 0; + + snd_printk(KERN_ERR "kernel_thread failed in snd_card_free_in_thread for card %i\n", card->number); + /* try to free the structure immediately */ + snd_card_free(card); + return -EFAULT; +} + +static void choose_default_id(snd_card_t * card) +{ + int i, len, idx_flag = 0, loops = 8; + char *id, *spos; + + id = spos = card->shortname; + while (*id != '\0') { + if (*id == ' ') + spos = id + 1; + id++; + } + id = card->id; + while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { + if (isalnum(*spos)) + *id++ = *spos; + spos++; + } + *id = '\0'; + + id = card->id; + + while (1) { + if (loops-- == 0) { + snd_printk(KERN_ERR "unable to choose default card id (%s)", id); + strcpy(card->id, card->proc_root->name); + return; + } + if (!snd_info_check_reserved_words(id)) + goto __change; + for (i = 0; i < snd_ecards_limit; i++) { + if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) + goto __change; + } + break; + + __change: + len = strlen(id); + if (idx_flag) + id[len-1]++; + else if ((size_t)len <= sizeof(card->id) - 3) { + strcat(id, "_1"); + idx_flag++; + } else { + spos = id + len - 2; + if ((size_t)len <= sizeof(card->id) - 2) + spos++; + *spos++ = '_'; + *spos++ = '1'; + *spos++ = '\0'; + idx_flag++; + } + } +} + +/** + * snd_card_register - register the soundcard + * @card: soundcard structure + * + * This function registers all the devices assigned to the soundcard. + * Until calling this, the ALSA control interface is blocked from the + * external accesses. Thus, you should call this function at the end + * of the initialization of the card. + * + * Returns zero otherwise a negative error code if the registrain failed. + */ +int snd_card_register(snd_card_t * card) +{ + int err; + snd_info_entry_t *entry; + + snd_runtime_check(card != NULL, return -EINVAL); + if ((err = snd_device_register_all(card)) < 0) + return err; + write_lock(&snd_card_rwlock); + if (snd_cards[card->number]) { + /* already registered */ + write_unlock(&snd_card_rwlock); + return 0; + } + if (!card->id[0]) + choose_default_id(card); + snd_cards[card->number] = card; + snd_cards_count++; + write_unlock(&snd_card_rwlock); + if ((err = snd_info_card_register(card)) < 0) { + snd_printd("unable to create card info\n"); + goto __skip_info; + } + if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { + snd_printd("unable to create card entry\n"); + goto __skip_info; + } + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_id_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + card->proc_id = entry; + __skip_info: +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); +#endif + return 0; +} + +static snd_info_entry_t *snd_card_info_entry = NULL; + +static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx, count; + snd_card_t *card; + + for (idx = count = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) { + count++; + snd_iprintf(buffer, "%i [%-15s]: %s - %s\n", + idx, + card->id, + card->driver, + card->shortname); + snd_iprintf(buffer, " %s\n", + card->longname); + } + read_unlock(&snd_card_rwlock); + } + if (!count) + snd_iprintf(buffer, "--- no soundcards ---\n"); +} + +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) + +void snd_card_info_read_oss(snd_info_buffer_t * buffer) +{ + int idx, count; + snd_card_t *card; + + for (idx = count = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) { + count++; + snd_iprintf(buffer, "%s\n", card->longname); + } + read_unlock(&snd_card_rwlock); + } + if (!count) { + snd_iprintf(buffer, "--- no soundcards ---\n"); + } +} + +#endif + +#ifdef MODULE +static snd_info_entry_t *snd_card_module_info_entry; +static void snd_card_module_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + snd_card_t *card; + + for (idx = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) + snd_iprintf(buffer, "%i %s\n", idx, card->module->name); + read_unlock(&snd_card_rwlock); + } +} +#endif + +int __init snd_card_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); + snd_runtime_check(entry != NULL, return -ENOMEM); + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_card_info_entry = entry; + +#ifdef MODULE + entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_module_info_read; + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); + else + snd_card_module_info_entry = entry; + } +#endif + + return 0; +} + +int __exit snd_card_info_done(void) +{ + if (snd_card_info_entry) + snd_info_unregister(snd_card_info_entry); +#ifdef MODULE + if (snd_card_module_info_entry) + snd_info_unregister(snd_card_module_info_entry); +#endif + return 0; +} + +/** + * snd_component_add - add a component string + * @card: soundcard structure + * @component: the component id string + * + * This function adds the component id string to the supported list. + * The component can be referred from the alsa-lib. + * + * Returns zero otherwise a negative error code. + */ + +int snd_component_add(snd_card_t *card, const char *component) +{ + char *ptr; + int len = strlen(component); + + ptr = strstr(card->components, component); + if (ptr != NULL) { + if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ + return 1; + } + if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { + snd_BUG(); + return -ENOMEM; + } + if (card->components[0] != '\0') + strcat(card->components, " "); + strcat(card->components, component); + return 0; +} + +/** + * snd_card_file_add - add the file to the file list of the card + * @card: soundcard structure + * @file: file pointer + * + * This function adds the file to the file linked-list of the card. + * This linked-list is used to keep tracking the connection state, + * and to avoid the release of busy resources by hotplug. + * + * Returns zero or a negative error code. + */ +int snd_card_file_add(snd_card_t *card, struct file *file) +{ + struct snd_monitor_file *mfile; + + mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); + if (mfile == NULL) + return -ENOMEM; + mfile->file = file; + mfile->next = NULL; + spin_lock(&card->files_lock); + if (card->shutdown) { + spin_unlock(&card->files_lock); + kfree(mfile); + return -ENODEV; + } + mfile->next = card->files; + card->files = mfile; + spin_unlock(&card->files_lock); + return 0; +} + +/** + * snd_card_file_remove - remove the file from the file list + * @card: soundcard structure + * @file: file pointer + * + * This function removes the file formerly added to the card via + * snd_card_file_add() function. + * If all files are removed and the release of the card is + * scheduled, it will wake up the the thread to call snd_card_free() + * (see snd_card_free_in_thread() function). + * + * Returns zero or a negative error code. + */ +int snd_card_file_remove(snd_card_t *card, struct file *file) +{ + struct snd_monitor_file *mfile, *pfile = NULL; + + spin_lock(&card->files_lock); + mfile = card->files; + while (mfile) { + if (mfile->file == file) { + if (pfile) + pfile->next = mfile->next; + else + card->files = mfile->next; + break; + } + pfile = mfile; + mfile = mfile->next; + } + spin_unlock(&card->files_lock); + if (card->files == NULL) + wake_up(&card->shutdown_sleep); + if (mfile) { + kfree(mfile); + } else { + snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); + return -ENOENT; + } + return 0; +} + +#ifdef CONFIG_PM +/** + * snd_power_wait - wait until the power-state is changed. + * @card: soundcard structure + * + * Waits until the power-state is changed. + * + * Note: the power lock must be active before call. + */ +void snd_power_wait(snd_card_t *card) +{ + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&card->power_sleep, &wait); + snd_power_unlock(card); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(30 * HZ); + remove_wait_queue(&card->power_sleep, &wait); + snd_power_lock(card); +} +#endif /* CONFIG_PM */ diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/Makefile linux/sound/core/ioctl32/Makefile --- linux-2.4.21-rc1.orig/sound/core/ioctl32/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,20 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := _ioctl32.o + +list-multi := snd-ioctl32.o + +snd-ioctl32-objs := ioctl32.o pcm32.o rawmidi32.o timer32.o hwdep32.o +ifneq ($(CONFIG_SND_SEQUENCER),n) + snd-ioctl32-objs += seq32.o +endif + +obj-$(CONFIG_SND_BIT32_EMUL) += snd-ioctl32.o + +include $(TOPDIR)/Rules.make + +snd-ioctl32.o: $(snd-ioctl32-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ioctl32-objs) diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/hwdep32.c linux/sound/core/ioctl32/hwdep32.c --- linux-2.4.21-rc1.orig/sound/core/ioctl32/hwdep32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/hwdep32.c 2003-03-05 08:31:16.000000000 -0700 @@ -0,0 +1,73 @@ +/* + * 32bit -> 64bit ioctl wrapper for hwdep API + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_hwdep_dsp_image32 { + u32 index; + unsigned char name[64]; + u32 image; /* pointer */ + u32 length; + u32 driver_data; +} /* don't set packed attribute here */; + +static int _snd_ioctl32_hwdep_dsp_image(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + struct sndrv_hwdep_dsp_image data; + struct sndrv_hwdep_dsp_image data32; + mm_segment_t oldseg; + int err; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.index = data32.index; + memcpy(data.name, data32.name, sizeof(data.name)); + data.image = A(data32.image); + data.length = data32.length; + data.driver_data = data32.driver_data; + oldseg = get_fs(); + set_fs(KERNEL_DS); + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); + return err; +} + +DEFINE_ALSA_IOCTL_ENTRY(hwdep_dsp_image, hwdep_dsp_image, SNDRV_HWDEP_IOCTL_DSP_LOAD); + +#define AP(x) snd_ioctl32_##x + +enum { + SNDRV_HWDEP_IOCTL_DSP_LOAD32 = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32) +}; + +struct ioctl32_mapper hwdep_mappers[] = { + MAP_COMPAT(SNDRV_HWDEP_IOCTL_PVERSION), + MAP_COMPAT(SNDRV_HWDEP_IOCTL_INFO), + MAP_COMPAT(SNDRV_HWDEP_IOCTL_DSP_STATUS), + { SNDRV_HWDEP_IOCTL_DSP_LOAD32, AP(hwdep_dsp_image) }, + { 0 }, +}; diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/ioctl32.c linux/sound/core/ioctl32/ioctl32.c --- linux-2.4.21-rc1.orig/sound/core/ioctl32/ioctl32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/ioctl32.c 2003-02-06 10:59:15.000000000 -0700 @@ -0,0 +1,452 @@ +/* + * 32bit -> 64bit ioctl wrapper for control API + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +/* + * register/unregister mappers + * exported for other modules + */ + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ioctl32 wrapper for ALSA"); +MODULE_LICENSE("GPL"); + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); +int unregister_ioctl32_conversion(unsigned int cmd); + + +int snd_ioctl32_register(struct ioctl32_mapper *mappers) +{ + int err; + struct ioctl32_mapper *m; + + for (m = mappers; m->cmd; m++) { + err = register_ioctl32_conversion(m->cmd, m->handler); + if (err >= 0) + m->registered++; + } + return 0; +} + +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers) +{ + struct ioctl32_mapper *m; + + for (m = mappers; m->cmd; m++) { + if (m->registered) { + unregister_ioctl32_conversion(m->cmd); + m->registered = 0; + } + } +} + + +/* + * compatible wrapper + */ +int snd_ioctl32_compat(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *filp) +{ + if (! filp->f_op || ! filp->f_op->ioctl) + return -ENOTTY; + return filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); +} + + +/* + * Controls + */ + +struct sndrv_ctl_elem_list32 { + u32 offset; + u32 space; + u32 used; + u32 count; + u32 pids; + unsigned char reserved[50]; +} /* don't set packed attribute here */; + +#define CVT_sndrv_ctl_elem_list()\ +{\ + COPY(offset);\ + COPY(space);\ + COPY(used);\ + COPY(count);\ + CPTR(pids);\ +} + +static int _snd_ioctl32_ctl_elem_list(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + struct sndrv_ctl_elem_list32 data32; + struct sndrv_ctl_elem_list data; + mm_segment_t oldseg; + int err; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.offset = data32.offset; + data.space = data32.space; + data.used = data32.used; + data.count = data32.count; + data.pids = A(data32.pids); + oldseg = get_fs(); + set_fs(KERNEL_DS); + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); + if (err < 0) + return err; + /* copy the result */ + data32.offset = data.offset; + data32.space = data.space; + data32.used = data.used; + data32.count = data.count; + //data.pids = data.pids; + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return 0; +} + +DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST); + +/* + * control element info + * it uses union, so the things are not easy.. + */ + +struct sndrv_ctl_elem_info32 { + struct sndrv_ctl_elem_id id; // the size of struct is same + s32 type; + u32 access; + u32 count; + s32 owner; + union { + struct { + s32 min; + s32 max; + s32 step; + } integer; + struct { + u64 min; + u64 max; + u64 step; + } integer64; + struct { + u32 items; + u32 item; + char name[64]; + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +} __attribute__((packed)); + +static int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + struct sndrv_ctl_elem_info data; + struct sndrv_ctl_elem_info32 data32; + int err; + mm_segment_t oldseg; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + /* we need to copy the item index. + * hope this doesn't break anything.. + */ + data.value.enumerated.item = data32.value.enumerated.item; + oldseg = get_fs(); + set_fs(KERNEL_DS); + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); + if (err < 0) + return err; + /* restore info to 32bit */ + data32.id = data.id; + data32.type = data.type; + data32.access = data.access; + data32.count = data.count; + data32.owner = data.owner; + switch (data.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + data32.value.integer.min = data.value.integer.min; + data32.value.integer.max = data.value.integer.max; + data32.value.integer.step = data.value.integer.step; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + data32.value.integer64.min = data.value.integer64.min; + data32.value.integer64.max = data.value.integer64.max; + data32.value.integer64.step = data.value.integer64.step; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + data32.value.enumerated.items = data.value.enumerated.items; + data32.value.enumerated.item = data.value.enumerated.item; + memcpy(data32.value.enumerated.name, data.value.enumerated.name, + sizeof(data.value.enumerated.name)); + break; + default: + break; + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return 0; +} + +DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO); + +struct sndrv_ctl_elem_value32 { + struct sndrv_ctl_elem_id id; + unsigned int indirect; /* bit-field causes misalignment */ + union { + union { + s32 value[128]; + u32 value_ptr; + } integer; + union { + s64 value[64]; + u32 value_ptr; + } integer64; + union { + u32 item[128]; + u32 item_ptr; + } enumerated; + union { + unsigned char data[512]; + u32 data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; + unsigned char reserved[128]; +} __attribute__((packed)); + + +/* hmm, it's so hard to retrieve the value type from the control id.. */ +static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id) +{ + snd_ctl_file_t *ctl; + snd_kcontrol_t *kctl; + snd_ctl_elem_info_t info; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + + kctl = snd_ctl_find_id(ctl->card, id); + if (! kctl) { + return -ENXIO; + } + info.id = *id; + err = kctl->info(kctl, &info); + if (err >= 0) + err = info.type; + return err; +} + + +static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + struct sndrv_ctl_elem_value *data; + struct sndrv_ctl_elem_value32 *data32; + int err, i; + int type; + mm_segment_t oldseg; + + /* FIXME: check the sane ioctl.. */ + + data = kmalloc(sizeof(*data), GFP_KERNEL); + data32 = kmalloc(sizeof(*data32), GFP_KERNEL); + if (data == NULL || data32 == NULL) { + err = -ENOMEM; + goto __end; + } + + if (copy_from_user(data32, (void*)arg, sizeof(*data32))) { + err = -EFAULT; + goto __end; + } + memset(data, 0, sizeof(*data)); + data->id = data32->id; + data->indirect = data32->indirect; + if (data->indirect) /* FIXME: this is not correct for long arrays */ + data->value.integer.value_ptr = (void*)TO_PTR(data32->value.integer.value_ptr); + type = get_ctl_type(file, &data->id); + if (type < 0) { + err = type; + goto __end; + } + if (! data->indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data->value.integer.value[i] = data32->value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + for (i = 0; i < 64; i++) + data->value.integer64.value[i] = data32->value.integer64.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data->value.enumerated.item[i] = data32->value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data->value.bytes.data, data32->value.bytes.data, + sizeof(data->value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data->value.iec958 = data32->value.iec958; + break; + default: + printk("unknown type %d\n", type); + break; + } + } + + oldseg = get_fs(); + set_fs(KERNEL_DS); + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); + set_fs(oldseg); + if (err < 0) + goto __end; + /* restore info to 32bit */ + if (! data->indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data32->value.integer.value[i] = data->value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + for (i = 0; i < 64; i++) + data32->value.integer64.value[i] = data->value.integer64.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data32->value.enumerated.item[i] = data->value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data32->value.bytes.data, data->value.bytes.data, + sizeof(data->value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data32->value.iec958 = data->value.iec958; + break; + default: + break; + } + } + err = 0; + if (copy_to_user((void*)arg, data32, sizeof(*data32))) + err = -EFAULT; + __end: + if (data32) + kfree(data32); + if (data) + kfree(data); + return err; +} + +DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ); +DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_write, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_WRITE); + +/* + */ + +#define AP(x) snd_ioctl32_##x + +enum { + SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32), + SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32), + SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32), + SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32), +}; + +static struct ioctl32_mapper control_mappers[] = { + /* controls (without rawmidi, hwdep, timer releated ones) */ + MAP_COMPAT(SNDRV_CTL_IOCTL_PVERSION), + MAP_COMPAT(SNDRV_CTL_IOCTL_CARD_INFO), + { SNDRV_CTL_IOCTL_ELEM_LIST32, AP(ctl_elem_list) }, + { SNDRV_CTL_IOCTL_ELEM_INFO32, AP(ctl_elem_info) }, + { SNDRV_CTL_IOCTL_ELEM_READ32, AP(ctl_elem_read) }, + { SNDRV_CTL_IOCTL_ELEM_WRITE32, AP(ctl_elem_write) }, + MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_LOCK), + MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_UNLOCK), + MAP_COMPAT(SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS), + MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_INFO), + MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE), + MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE), + MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_INFO), + MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE), + MAP_COMPAT(SNDRV_CTL_IOCTL_POWER), + MAP_COMPAT(SNDRV_CTL_IOCTL_POWER_STATE), + { 0 } +}; + + +/* + */ + +extern struct ioctl32_mapper pcm_mappers[]; +extern struct ioctl32_mapper rawmidi_mappers[]; +extern struct ioctl32_mapper timer_mappers[]; +extern struct ioctl32_mapper hwdep_mappers[]; +#ifdef CONFIG_SND_SEQUENCER +extern struct ioctl32_mapper seq_mappers[]; +#endif + +static void snd_ioctl32_done(void) +{ +#ifdef CONFIG_SND_SEQUENCER + snd_ioctl32_unregister(seq_mappers); +#endif + snd_ioctl32_unregister(hwdep_mappers); + snd_ioctl32_unregister(timer_mappers); + snd_ioctl32_unregister(rawmidi_mappers); + snd_ioctl32_unregister(pcm_mappers); + snd_ioctl32_unregister(control_mappers); +} + +static int __init snd_ioctl32_init(void) +{ + snd_ioctl32_register(control_mappers); + snd_ioctl32_register(pcm_mappers); + snd_ioctl32_register(rawmidi_mappers); + snd_ioctl32_register(timer_mappers); + snd_ioctl32_register(hwdep_mappers); +#ifdef CONFIG_SND_SEQUENCER + snd_ioctl32_register(seq_mappers); +#endif + return 0; +} + +module_init(snd_ioctl32_init) +module_exit(snd_ioctl32_done) diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/ioctl32.h linux/sound/core/ioctl32/ioctl32.h --- linux-2.4.21-rc1.orig/sound/core/ioctl32/ioctl32.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/ioctl32.h 2003-02-06 10:59:15.000000000 -0700 @@ -0,0 +1,140 @@ +/* + * 32bit -> 64bit ioctl helpers + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This file registers the converters from 32-bit ioctls to 64-bit ones. + * The converter assumes that a 32-bit user-pointer can be casted by A(x) + * macro to a valid 64-bit pointer which is accessible via copy_from/to_user. + * + */ + +#ifndef __ALSA_IOCTL32_H +#define __ALSA_IOCTL32_H + +#ifndef A +#ifdef CONFIG_PPC64 +#include +#else +/* x86-64, sparc64 */ +#define A(__x) ((void *)(unsigned long)(__x)) +#endif +#endif + +#define TO_PTR(x) A(x) + +#define COPY(x) (dst->x = src->x) +#define CPTR(x) (dst->x = (typeof(dst->x))A(src->x)) + +#define convert_from_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *dst = dstp;\ + struct sndrv_##type##32 *src = srcp;\ + CVT_##sndrv_##type();\ +} + +#define convert_to_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *src = srcp;\ + struct sndrv_##type##32 *dst = dstp;\ + CVT_##sndrv_##type();\ +} + + +#define DEFINE_ALSA_IOCTL(type) \ +static int _snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)\ +{\ + struct sndrv_##type##32 data32;\ + struct sndrv_##type data;\ + mm_segment_t oldseg;\ + int err;\ + if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\ + return -EFAULT;\ + memset(&data, 0, sizeof(data));\ + convert_from_32(type, &data, &data32);\ + oldseg = get_fs();\ + set_fs(KERNEL_DS);\ + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);\ + set_fs(oldseg);\ + if (err < 0) \ + return err;\ + if (native_ctl & (_IOC_READ << _IOC_DIRSHIFT)) {\ + convert_to_32(type, &data32, &data);\ + if (copy_to_user((void*)arg, &data32, sizeof(data32)))\ + return -EFAULT;\ + }\ + return 0;\ +} + +#define DEFINE_ALSA_IOCTL_BIG(type) \ +static int _snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)\ +{\ + struct sndrv_##type##32 *data32;\ + struct sndrv_##type *data;\ + mm_segment_t oldseg;\ + int err;\ + data32 = kmalloc(sizeof(*data32), GFP_KERNEL); \ + data = kmalloc(sizeof(*data), GFP_KERNEL); \ + if (data32 == NULL || data == NULL) { \ + err = -ENOMEM; \ + goto __end; \ + }\ + if (copy_from_user(data32, (void*)arg, sizeof(*data32))) { \ + err = -EFAULT; \ + goto __end; \ + }\ + memset(data, 0, sizeof(*data));\ + convert_from_32(type, data, data32);\ + oldseg = get_fs();\ + set_fs(KERNEL_DS);\ + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);\ + set_fs(oldseg);\ + if (err < 0) \ + goto __end;\ + err = 0;\ + if (native_ctl & (_IOC_READ << _IOC_DIRSHIFT)) {\ + convert_to_32(type, data32, data);\ + if (copy_to_user((void*)arg, data32, sizeof(*data32)))\ + err = -EFAULT;\ + }\ + __end:\ + if (data)\ + kfree(data);\ + if (data32)\ + kfree(data32);\ + return err;\ +} + +#define DEFINE_ALSA_IOCTL_ENTRY(name,type,native_ctl) \ +static int snd_ioctl32_##name(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) {\ + return _snd_ioctl32_##type(fd, cmd, arg, file, native_ctl);\ +} + +#define MAP_COMPAT(ctl) { ctl, snd_ioctl32_compat } + +struct ioctl32_mapper { + unsigned int cmd; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + int registered; +}; + +int snd_ioctl32_compat(unsigned int, unsigned int, unsigned long, struct file *); + +int snd_ioctl32_register(struct ioctl32_mapper *mappers); +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers); + +#endif /* __ALSA_IOCTL32_H */ diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/pcm32.c linux/sound/core/ioctl32/pcm32.c --- linux-2.4.21-rc1.orig/sound/core/ioctl32/pcm32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/pcm32.c 2003-02-06 10:59:15.000000000 -0700 @@ -0,0 +1,454 @@ +/* + * 32bit -> 64bit ioctl wrapper for PCM API + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include "ioctl32.h" + + +/* wrapper for sndrv_pcm_[us]frames */ +struct sndrv_pcm_sframes_str { + sndrv_pcm_sframes_t val; +}; +struct sndrv_pcm_sframes_str32 { + s32 val; +}; +struct sndrv_pcm_uframes_str { + sndrv_pcm_uframes_t val; +}; +struct sndrv_pcm_uframes_str32 { + u32 val; +}; + +#define CVT_sndrv_pcm_sframes_str() { COPY(val); } +#define CVT_sndrv_pcm_uframes_str() { COPY(val); } + + +struct sndrv_interval32 { + u32 min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +struct sndrv_pcm_hw_params32 { + u32 flags; + struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */ + struct sndrv_mask mres[5]; /* reserved masks */ + struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct sndrv_interval ires[9]; /* reserved intervals */ + u32 rmask; + u32 cmask; + u32 info; + u32 msbits; + u32 rate_num; + u32 rate_den; + u32 fifo_size; + unsigned char reserved[64]; +} __attribute__((packed)); + +#define numberof(array) (sizeof(array)/sizeof(array[0])) + +#define CVT_sndrv_pcm_hw_params()\ +{\ + unsigned int i;\ + COPY(flags);\ + for (i = 0; i < numberof(dst->masks); i++)\ + COPY(masks[i]);\ + for (i = 0; i < numberof(dst->intervals); i++) {\ + COPY(intervals[i].min);\ + COPY(intervals[i].max);\ + COPY(intervals[i].openmin);\ + COPY(intervals[i].openmax);\ + COPY(intervals[i].integer);\ + COPY(intervals[i].empty);\ + }\ + COPY(rmask);\ + COPY(cmask);\ + COPY(info);\ + COPY(msbits);\ + COPY(rate_num);\ + COPY(rate_den);\ + COPY(fifo_size);\ +} + +struct sndrv_pcm_sw_params32 { + s32 tstamp_mode; + u32 period_step; + u32 sleep_min; + u32 avail_min; + u32 xfer_align; + u32 start_threshold; + u32 stop_threshold; + u32 silence_threshold; + u32 silence_size; + u32 boundary; + unsigned char reserved[64]; +} __attribute__((packed)); + +#define CVT_sndrv_pcm_sw_params()\ +{\ + COPY(tstamp_mode);\ + COPY(period_step);\ + COPY(sleep_min);\ + COPY(avail_min);\ + COPY(xfer_align);\ + COPY(start_threshold);\ + COPY(stop_threshold);\ + COPY(silence_threshold);\ + COPY(silence_size);\ + COPY(boundary);\ +} + +struct sndrv_pcm_channel_info32 { + u32 channel; + u32 offset; + u32 first; + u32 step; +} __attribute__((packed)); + +#define CVT_sndrv_pcm_channel_info()\ +{\ + COPY(channel);\ + COPY(offset);\ + COPY(first);\ + COPY(step);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +} __attribute__((packed)); + +struct sndrv_pcm_status32 { + s32 state; + struct timeval32 trigger_tstamp; + struct timeval32 tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + unsigned char reserved[60]; +} __attribute__((packed)); + +#define CVT_sndrv_pcm_status()\ +{\ + COPY(state);\ + COPY(trigger_tstamp.tv_sec);\ + COPY(trigger_tstamp.tv_usec);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(appl_ptr);\ + COPY(hw_ptr);\ + COPY(delay);\ + COPY(avail);\ + COPY(avail_max);\ + COPY(overrange);\ + COPY(suspended_state);\ +} + +DEFINE_ALSA_IOCTL(pcm_uframes_str); +DEFINE_ALSA_IOCTL(pcm_sframes_str); +DEFINE_ALSA_IOCTL_BIG(pcm_hw_params); +DEFINE_ALSA_IOCTL(pcm_sw_params); +DEFINE_ALSA_IOCTL(pcm_channel_info); +DEFINE_ALSA_IOCTL(pcm_status); + +/* + */ +struct sndrv_xferi32 { + s32 result; + u32 buf; + u32 frames; +} __attribute__((packed)); + +static int _snd_ioctl32_xferi(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + struct sndrv_xferi32 data32; + struct sndrv_xferi data; + mm_segment_t oldseg; + int err; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.result = data32.result; + data.buf = A(data32.buf); + data.frames = data32.frames; + oldseg = get_fs(); + set_fs(KERNEL_DS); + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); + if (err < 0) + return err; + /* copy the result */ + data32.result = data.result; + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return 0; +} + + +/* snd_xfern needs remapping of bufs */ +struct sndrv_xfern32 { + s32 result; + u32 bufs; /* this is void **; */ + u32 frames; +} __attribute__((packed)); + +/* + * xfern ioctl nees to copy (up to) 128 pointers on stack. + * although we may pass the copied pointers through f_op->ioctl, but the ioctl + * handler there expands again the same 128 pointers on stack, so it is better + * to handle the function (calling pcm_readv/writev) directly in this handler. + */ +static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + struct sndrv_xfern32 data32, *srcptr = (struct sndrv_xfern32*)arg; + void **bufs = NULL; + int err = 0, ch, i; + u32 *bufptr; + mm_segment_t oldseg; + + /* FIXME: need to check whether fop->ioctl is sane */ + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL && substream->runtime, return -ENXIO); + + /* check validty of the command */ + switch (native_ctl) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + break; + case SNDRV_PCM_IOCTL_READN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + break; + } + if ((ch = substream->runtime->channels) > 128) + return -EINVAL; + if (get_user(data32.frames, &srcptr->frames)) + return -EFAULT; + __get_user(data32.bufs, &srcptr->bufs); + bufptr = (u32*)TO_PTR(data32.bufs); + bufs = kmalloc(sizeof(void *) * 128, GFP_KERNEL); + if (bufs == NULL) + return -ENOMEM; + for (i = 0; i < ch; i++) { + u32 ptr; + if (get_user(ptr, bufptr)) + return -EFAULT; + bufs[ch] = (void*)TO_PTR(ptr); + bufptr++; + } + oldseg = get_fs(); + set_fs(KERNEL_DS); + switch (native_ctl) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + err = snd_pcm_lib_writev(substream, bufs, data32.frames); + break; + case SNDRV_PCM_IOCTL_READN_FRAMES: + err = snd_pcm_lib_readv(substream, bufs, data32.frames); + break; + } + set_fs(oldseg); + if (err >= 0) { + if (put_user(err, &srcptr->result)) + err = -EFAULT; + } + kfree(bufs); + return 0; +} + + +struct sndrv_pcm_hw_params_old32 { + u32 flags; + u32 masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - + SNDRV_PCM_HW_PARAM_ACCESS + 1]; + struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_TICK_TIME - + SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1]; + u32 rmask; + u32 cmask; + u32 info; + u32 msbits; + u32 rate_num; + u32 rate_den; + u32 fifo_size; + unsigned char reserved[64]; +} __attribute__((packed)); + +#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5)) +#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5)) + +static void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params, struct sndrv_pcm_hw_params_old32 *oparams) +{ + unsigned int i; + + memset(params, 0, sizeof(*params)); + params->flags = oparams->flags; + for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + params->masks[i].bits[0] = oparams->masks[i]; + memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals)); + params->rmask = __OLD_TO_NEW_MASK(oparams->rmask); + params->cmask = __OLD_TO_NEW_MASK(oparams->cmask); + params->info = oparams->info; + params->msbits = oparams->msbits; + params->rate_num = oparams->rate_num; + params->rate_den = oparams->rate_den; + params->fifo_size = oparams->fifo_size; +} + +static void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old32 *oparams, snd_pcm_hw_params_t *params) +{ + unsigned int i; + + memset(oparams, 0, sizeof(*oparams)); + oparams->flags = params->flags; + for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + oparams->masks[i] = params->masks[i].bits[0]; + memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals)); + oparams->rmask = __NEW_TO_OLD_MASK(params->rmask); + oparams->cmask = __NEW_TO_OLD_MASK(params->cmask); + oparams->info = params->info; + oparams->msbits = params->msbits; + oparams->rate_num = params->rate_num; + oparams->rate_den = params->rate_den; + oparams->fifo_size = params->fifo_size; +} + +static int _snd_ioctl32_pcm_hw_params_old(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) +{ + struct sndrv_pcm_hw_params_old32 *data32; + struct sndrv_pcm_hw_params *data; + mm_segment_t oldseg; + int err; + + data32 = snd_kcalloc(sizeof(*data32), GFP_KERNEL); + data = snd_kcalloc(sizeof(*data), GFP_KERNEL); + if (data32 == NULL || data == NULL) { + err = -ENOMEM; + goto __end; + } + if (copy_from_user(data32, (void*)arg, sizeof(*data32))) { + err = -EFAULT; + goto __end; + } + snd_pcm_hw_convert_from_old_params(data, data32); + oldseg = get_fs(); + set_fs(KERNEL_DS); + err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); + set_fs(oldseg); + if (err < 0) + goto __end; + snd_pcm_hw_convert_to_old_params(data32, data); + err = 0; + if (copy_to_user((void*)arg, data32, sizeof(*data32))) + err = -EFAULT; + __end: + if (data) + kfree(data); + if (data32) + kfree(data32); + return err; +} + + +/* + */ + +DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_refine, pcm_hw_params, SNDRV_PCM_IOCTL_HW_REFINE); +DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_params, pcm_hw_params, SNDRV_PCM_IOCTL_HW_PARAMS); +DEFINE_ALSA_IOCTL_ENTRY(pcm_sw_params, pcm_sw_params, SNDRV_PCM_IOCTL_SW_PARAMS); +DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_refine_old, pcm_hw_params_old, SNDRV_PCM_IOCTL_HW_REFINE); +DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_params_old, pcm_hw_params_old, SNDRV_PCM_IOCTL_HW_PARAMS); +DEFINE_ALSA_IOCTL_ENTRY(pcm_status, pcm_status, SNDRV_PCM_IOCTL_STATUS); +DEFINE_ALSA_IOCTL_ENTRY(pcm_delay, pcm_sframes_str, SNDRV_PCM_IOCTL_DELAY); +DEFINE_ALSA_IOCTL_ENTRY(pcm_channel_info, pcm_channel_info, SNDRV_PCM_IOCTL_CHANNEL_INFO); +DEFINE_ALSA_IOCTL_ENTRY(pcm_rewind, pcm_uframes_str, SNDRV_PCM_IOCTL_REWIND); +DEFINE_ALSA_IOCTL_ENTRY(pcm_readi, xferi, SNDRV_PCM_IOCTL_READI_FRAMES); +DEFINE_ALSA_IOCTL_ENTRY(pcm_writei, xferi, SNDRV_PCM_IOCTL_WRITEI_FRAMES); +DEFINE_ALSA_IOCTL_ENTRY(pcm_readn, xfern, SNDRV_PCM_IOCTL_READN_FRAMES); +DEFINE_ALSA_IOCTL_ENTRY(pcm_writen, xfern, SNDRV_PCM_IOCTL_WRITEN_FRAMES); + + +/* + */ +#define AP(x) snd_ioctl32_##x + +enum { + SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params32), + SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params32), + SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct sndrv_pcm_sw_params32), + SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct sndrv_pcm_status32), + SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), + SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32), + SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), + SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32), + SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32), + SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32), + SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32), + SNDRV_PCM_IOCTL_HW_REFINE_OLD32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old32), + SNDRV_PCM_IOCTL_HW_PARAMS_OLD32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old32), + +}; + +struct ioctl32_mapper pcm_mappers[] = { + MAP_COMPAT(SNDRV_PCM_IOCTL_PVERSION), + MAP_COMPAT(SNDRV_PCM_IOCTL_INFO), + { SNDRV_PCM_IOCTL_HW_REFINE32, AP(pcm_hw_refine) }, + { SNDRV_PCM_IOCTL_HW_PARAMS32, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_REFINE_OLD32, AP(pcm_hw_refine_old) }, + { SNDRV_PCM_IOCTL_HW_PARAMS_OLD32, AP(pcm_hw_params_old) }, + MAP_COMPAT(SNDRV_PCM_IOCTL_HW_FREE), + { SNDRV_PCM_IOCTL_SW_PARAMS32, AP(pcm_sw_params) }, + { SNDRV_PCM_IOCTL_STATUS32, AP(pcm_status) }, + { SNDRV_PCM_IOCTL_DELAY32, AP(pcm_delay) }, + { SNDRV_PCM_IOCTL_CHANNEL_INFO32, AP(pcm_channel_info) }, + MAP_COMPAT(SNDRV_PCM_IOCTL_PREPARE), + MAP_COMPAT(SNDRV_PCM_IOCTL_RESET), + MAP_COMPAT(SNDRV_PCM_IOCTL_START), + MAP_COMPAT(SNDRV_PCM_IOCTL_DROP), + MAP_COMPAT(SNDRV_PCM_IOCTL_DRAIN), + MAP_COMPAT(SNDRV_PCM_IOCTL_PAUSE), + { SNDRV_PCM_IOCTL_REWIND32, AP(pcm_rewind) }, + MAP_COMPAT(SNDRV_PCM_IOCTL_RESUME), + MAP_COMPAT(SNDRV_PCM_IOCTL_XRUN), + { SNDRV_PCM_IOCTL_WRITEI_FRAMES32, AP(pcm_writei) }, + { SNDRV_PCM_IOCTL_READI_FRAMES32, AP(pcm_readi) }, + { SNDRV_PCM_IOCTL_WRITEN_FRAMES32, AP(pcm_writen) }, + { SNDRV_PCM_IOCTL_READN_FRAMES32, AP(pcm_readn) }, + MAP_COMPAT(SNDRV_PCM_IOCTL_LINK), + MAP_COMPAT(SNDRV_PCM_IOCTL_UNLINK), + + { 0 }, +}; diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/rawmidi32.c linux/sound/core/ioctl32/rawmidi32.c --- linux-2.4.21-rc1.orig/sound/core/ioctl32/rawmidi32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/rawmidi32.c 2003-02-06 10:59:15.000000000 -0700 @@ -0,0 +1,93 @@ +/* + * 32bit -> 64bit ioctl wrapper for raw MIDI API + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_rawmidi_params32 { + s32 stream; + u32 buffer_size; + u32 avail_min; + unsigned int no_active_sensing; /* avoid bit-field */ + unsigned char reserved[16]; +} __attribute__((packed)); + +#define CVT_sndrv_rawmidi_params()\ +{\ + COPY(stream);\ + COPY(buffer_size);\ + COPY(avail_min);\ + COPY(no_active_sensing);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +} __attribute__((packed)); + +struct sndrv_rawmidi_status32 { + s32 stream; + struct timeval32 tstamp; + u32 avail; + u32 xruns; + unsigned char reserved[16]; +} __attribute__((packed)); + +#define CVT_sndrv_rawmidi_status()\ +{\ + COPY(stream);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(avail);\ + COPY(xruns);\ +} + +DEFINE_ALSA_IOCTL(rawmidi_params); +DEFINE_ALSA_IOCTL(rawmidi_status); + +DEFINE_ALSA_IOCTL_ENTRY(rawmidi_params, rawmidi_params, SNDRV_RAWMIDI_IOCTL_PARAMS); +DEFINE_ALSA_IOCTL_ENTRY(rawmidi_status, rawmidi_status, SNDRV_RAWMIDI_IOCTL_STATUS); + +#define AP(x) snd_ioctl32_##x + +enum { + SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct sndrv_rawmidi_params32), + SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct sndrv_rawmidi_status32), +}; + +struct ioctl32_mapper rawmidi_mappers[] = { + MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_PVERSION), + MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_INFO), + { SNDRV_RAWMIDI_IOCTL_PARAMS32, AP(rawmidi_params) }, + { SNDRV_RAWMIDI_IOCTL_STATUS32, AP(rawmidi_status) }, + MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_DROP), + MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_DRAIN), + + MAP_COMPAT(SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE), + MAP_COMPAT(SNDRV_CTL_IOCTL_RAWMIDI_INFO), + MAP_COMPAT(SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE), + + { 0 }, +}; diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/seq32.c linux/sound/core/ioctl32/seq32.c --- linux-2.4.21-rc1.orig/sound/core/ioctl32/seq32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/seq32.c 2003-02-06 10:59:16.000000000 -0700 @@ -0,0 +1,114 @@ +/* + * 32bit -> 64bit ioctl wrapper for sequencer API + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_seq_port_info32 { + struct sndrv_seq_addr addr; /* client/port numbers */ + char name[64]; /* port name */ + + u32 capability; /* port capability bits */ + u32 type; /* port type bits */ + s32 midi_channels; /* channels per MIDI port */ + s32 midi_voices; /* voices per MIDI port */ + s32 synth_voices; /* voices per SYNTH port */ + + s32 read_use; /* R/O: subscribers for output (from this port) */ + s32 write_use; /* R/O: subscribers for input (to this port) */ + + u32 kernel; /* reserved for kernel use (must be NULL) */ + u32 flags; /* misc. conditioning */ + char reserved[60]; /* for future use */ +}; + +#define CVT_sndrv_seq_port_info()\ +{\ + COPY(addr);\ + memcpy(dst->name, src->name, sizeof(dst->name));\ + COPY(capability);\ + COPY(type);\ + COPY(midi_channels);\ + COPY(midi_voices);\ + COPY(synth_voices);\ + COPY(read_use);\ + COPY(write_use);\ + COPY(flags);\ +} + +DEFINE_ALSA_IOCTL(seq_port_info); +DEFINE_ALSA_IOCTL_ENTRY(create_port, seq_port_info, SNDRV_SEQ_IOCTL_CREATE_PORT); +DEFINE_ALSA_IOCTL_ENTRY(delete_port, seq_port_info, SNDRV_SEQ_IOCTL_DELETE_PORT); +DEFINE_ALSA_IOCTL_ENTRY(get_port_info, seq_port_info, SNDRV_SEQ_IOCTL_GET_PORT_INFO); +DEFINE_ALSA_IOCTL_ENTRY(set_port_info, seq_port_info, SNDRV_SEQ_IOCTL_SET_PORT_INFO); +DEFINE_ALSA_IOCTL_ENTRY(query_next_port, seq_port_info, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT); + +/* + */ +#define AP(x) snd_ioctl32_##x + +enum { + SNDRV_SEQ_IOCTL_CREATE_PORT32 = _IOWR('S', 0x20, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_DELETE_PORT32 = _IOW ('S', 0x21, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_GET_PORT_INFO32 = _IOWR('S', 0x22, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_SET_PORT_INFO32 = _IOW ('S', 0x23, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32 = _IOWR('S', 0x52, struct sndrv_seq_port_info32), +}; + +struct ioctl32_mapper seq_mappers[] = { + MAP_COMPAT(SNDRV_SEQ_IOCTL_PVERSION), + MAP_COMPAT(SNDRV_SEQ_IOCTL_CLIENT_ID), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SYSTEM_INFO), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_CLIENT_INFO), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO), + { SNDRV_SEQ_IOCTL_CREATE_PORT32, AP(create_port) }, + { SNDRV_SEQ_IOCTL_DELETE_PORT32, AP(delete_port) }, + { SNDRV_SEQ_IOCTL_GET_PORT_INFO32, AP(get_port_info) }, + { SNDRV_SEQ_IOCTL_SET_PORT_INFO32, AP(set_port_info) }, + MAP_COMPAT(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT), + MAP_COMPAT(SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT), + MAP_COMPAT(SNDRV_SEQ_IOCTL_CREATE_QUEUE), + MAP_COMPAT(SNDRV_SEQ_IOCTL_DELETE_QUEUE), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_INFO), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_INFO), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_CLIENT_POOL), + MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_CLIENT_POOL), + MAP_COMPAT(SNDRV_SEQ_IOCTL_REMOVE_EVENTS), + MAP_COMPAT(SNDRV_SEQ_IOCTL_QUERY_SUBS), + MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION), + MAP_COMPAT(SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT), + { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32, AP(query_next_port) }, + MAP_COMPAT(SNDRV_SEQ_IOCTL_RUNNING_MODE), + { 0 }, +}; diff -urN linux-2.4.21-rc1.orig/sound/core/ioctl32/timer32.c linux/sound/core/ioctl32/timer32.c --- linux-2.4.21-rc1.orig/sound/core/ioctl32/timer32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/ioctl32/timer32.c 2003-02-06 10:59:16.000000000 -0700 @@ -0,0 +1,100 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_timer_info32 { + u32 flags; + s32 card; + unsigned char id[64]; + unsigned char name[80]; + u32 ticks; + u32 resolution; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_info()\ +{\ + COPY(flags);\ + COPY(card);\ + memcpy(dst->id, src->id, sizeof(src->id));\ + memcpy(dst->name, src->name, sizeof(src->name));\ + COPY(ticks);\ + COPY(resolution);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_timer_status32 { + struct timeval32 tstamp; + u32 resolution; + u32 lost; + u32 overrun; + u32 queue; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_status()\ +{\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(resolution);\ + COPY(lost);\ + COPY(overrun);\ + COPY(queue);\ +} + +DEFINE_ALSA_IOCTL(timer_info); +DEFINE_ALSA_IOCTL(timer_status); + +DEFINE_ALSA_IOCTL_ENTRY(timer_info, timer_info, SNDRV_TIMER_IOCTL_INFO); +DEFINE_ALSA_IOCTL_ENTRY(timer_status, timer_status, SNDRV_TIMER_IOCTL_STATUS); + +/* + */ + +#define AP(x) snd_ioctl32_##x + +enum { + SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct sndrv_timer_info32), + SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct sndrv_timer_status32), +}; + +struct ioctl32_mapper timer_mappers[] = { + MAP_COMPAT(SNDRV_TIMER_IOCTL_PVERSION), + MAP_COMPAT(SNDRV_TIMER_IOCTL_NEXT_DEVICE), + MAP_COMPAT(SNDRV_TIMER_IOCTL_SELECT), + { SNDRV_TIMER_IOCTL_INFO32, AP(timer_info) }, + MAP_COMPAT(SNDRV_TIMER_IOCTL_PARAMS), + { SNDRV_TIMER_IOCTL_STATUS32, AP(timer_status) }, + MAP_COMPAT(SNDRV_TIMER_IOCTL_START), + MAP_COMPAT(SNDRV_TIMER_IOCTL_STOP), + MAP_COMPAT(SNDRV_TIMER_IOCTL_CONTINUE), + { 0 }, +}; diff -urN linux-2.4.21-rc1.orig/sound/core/isadma.c linux/sound/core/isadma.c --- linux-2.4.21-rc1.orig/sound/core/isadma.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/isadma.c 2003-03-17 02:11:16.000000000 -0700 @@ -0,0 +1,103 @@ +/* + * ISA DMA support functions + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Defining following add some delay. Maybe this helps for some broken + * ISA DMA controllers. + */ + +#undef HAVE_REALLY_SLOW_DMA_CONTROLLER + +#include +#include +#include + +/** + * snd_dma_program - program an ISA DMA transfer + * @dma: the dma number + * @addr: the physical address of the buffer + * @size: the DMA transfer size + * @mode: the DMA transfer mode, DMA_MODE_XXX + * + * Programs an ISA DMA transfer for the given buffer. + */ +void snd_dma_program(unsigned long dma, + unsigned long addr, unsigned int size, + unsigned short mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma, mode); + set_dma_addr(dma, addr); + set_dma_count(dma, size); + if (!(mode & DMA_MODE_NO_ENABLE)) + enable_dma(dma); + release_dma_lock(flags); +} + +/** + * snd_dma_disable - stop the ISA DMA transfer + * @dma: the dma number + * + * Stops the ISA DMA transfer. + */ +void snd_dma_disable(unsigned long dma) +{ + unsigned long flags; + + flags = claim_dma_lock(); + clear_dma_ff(dma); + disable_dma(dma); + release_dma_lock(flags); +} + +/** + * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes + * @dma: the dma number + * @size: the dma transfer size + * + * Returns the current pointer in DMA tranfer buffer in bytes + */ +unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) +{ + unsigned long flags; + unsigned int result; + + flags = claim_dma_lock(); + clear_dma_ff(dma); + if (!isa_dma_bridge_buggy) + disable_dma(dma); + result = get_dma_residue(dma); + if (!isa_dma_bridge_buggy) + enable_dma(dma); + release_dma_lock(flags); +#ifdef CONFIG_SND_DEBUG + if (result > size) + snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); +#endif + if (result >= size || result == 0) + return 0; + else + return size - result; +} diff -urN linux-2.4.21-rc1.orig/sound/core/memalloc.c linux/sound/core/memalloc.c --- linux-2.4.21-rc1.orig/sound/core/memalloc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/memalloc.c 2003-03-05 04:30:15.000000000 -0700 @@ -0,0 +1,785 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Takashi Iwai + * + * Generic memory allocators + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Takashi Iwai , Jaroslav Kysela "); +MODULE_DESCRIPTION("Memory allocator for ALSA system."); +MODULE_LICENSE("GPL"); + + +/* + */ + +static DECLARE_MUTEX(list_mutex); +static LIST_HEAD(mem_list_head); + +/* buffer preservation list */ +struct snd_mem_list { + struct snd_dma_device dev; + struct snd_dma_buffer buffer; + int used; + struct list_head list; +}; + + +#ifdef CONFIG_SND_DEBUG +#define __ASTRING__(x) #x +#define snd_assert(expr, args...) do {\ + if (!(expr)) {\ + printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) +#else +#define snd_assert(expr, args...) /**/ +#endif + +/* redefine pci_alloc_consistent for some architectures */ +#ifdef HACK_PCI_ALLOC_CONSISTENT +#undef pci_alloc_consistent +#define pci_alloc_consistent snd_pci_hack_alloc_consistent +#endif + + +/* + * compare the two devices + * returns non-zero if matched. + */ +static int compare_device(const struct snd_dma_device *a, const struct snd_dma_device *b) +{ + if (a->type != b->type) + return 0; + if (a->id != b->id) + return 0; + switch (a->type) { + case SNDRV_DMA_TYPE_CONTINUOUS: +#ifdef CONFIG_ISA + case SNDRV_DMA_TYPE_ISA: +#endif + return a->dev.flags == b->dev.flags; +#ifdef CONFIG_PCI + case SNDRV_DMA_TYPE_PCI: + return a->dev.pci == b->dev.pci; +#endif +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + return a->dev.sbus == b->dev.sbus; +#endif + } + return 0; +} + +/** + * snd_dma_alloc_pages - allocate the buffer area according to the given type + * @dev: the buffer device info + * @size: the buffer size to allocate + * @dmab: buffer allocation record to store the allocated data + * + * Calls the memory-allocator function for the corresponding + * buffer type. + * + * Returns zero if the buffer with the given size is allocated successfuly, + * other a negative value at error. + */ +int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab) +{ + snd_assert(dev != NULL, return -ENXIO); + snd_assert(size > 0, return -ENXIO); + snd_assert(dmab != NULL, return -ENXIO); + + dmab->bytes = 0; + switch (dev->type) { + case SNDRV_DMA_TYPE_CONTINUOUS: + dmab->area = snd_malloc_pages(size, dev->dev.flags); + dmab->addr = 0; + break; +#ifdef CONFIG_ISA + case SNDRV_DMA_TYPE_ISA: + dmab->area = snd_malloc_isa_pages(size, &dmab->addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_DMA_TYPE_PCI: + dmab->area = snd_malloc_pci_pages(dev->dev.pci, size, &dmab->addr); + break; + case SNDRV_DMA_TYPE_PCI_SG: + snd_malloc_sgbuf_pages(dev->dev.pci, size, dmab); + break; +#endif +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + dmab->area = snd_malloc_pci_pages(dev->dev.sbus, size, &dmab->addr); + break; +#endif + default: + printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); + dmab->area = NULL; + dmab->addr = 0; + return -ENXIO; + } + if (dmab->area) + dmab->bytes = size; + return 0; +} + + +/** + * snd_dma_free_pages - release the allocated buffer + * @dev: the buffer device info + * @dmbab: the buffer allocation record to release + * + * Releases the allocated buffer via snd_dma_alloc_pages(). + */ +void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab) +{ + switch (dev->type) { + case SNDRV_DMA_TYPE_CONTINUOUS: + snd_free_pages(dmab->area, dmab->bytes); + break; +#ifdef CONFIG_ISA + case SNDRV_DMA_TYPE_ISA: + snd_free_isa_pages(dmab->bytes, dmab->area, dmab->addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_DMA_TYPE_PCI: + snd_free_pci_pages(dev->dev.pci, dmab->bytes, dmab->area, dmab->addr); + break; + case SNDRV_DMA_TYPE_PCI_SG: + snd_free_sgbuf_pages(dmab); + break; +#endif +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + snd_free_sbus_pages(dev->dev.sbus, dmab->size, dmab->are, dmab->addr); + break; +#endif + default: + printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); + } +} + + +/* + * search for the device + */ +static struct snd_mem_list *mem_list_find(const struct snd_dma_device *dev) +{ + struct list_head *p; + struct snd_mem_list *mem; + + list_for_each(p, &mem_list_head) { + mem = list_entry(p, struct snd_mem_list, list); + if (compare_device(&mem->dev, dev)) + return mem; + } + return NULL; +} + +/** + * snd_dma_get_reserved - get the reserved buffer for the given device + * @dev: the buffer device info + * @dmab: the buffer allocation record to store + * + * Looks for the reserved-buffer list and re-uses if the same buffer + * is found in the list. When the buffer is found, it's marked as used. + * For unmarking the buffer, call snd_dma_free_reserved(). + * + * Returns the size of buffer if the buffer is found, or zero if not found. + */ +size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab) +{ + struct snd_mem_list *mem; + + snd_assert(dev && dmab, return 0); + + down(&list_mutex); + mem = mem_list_find(dev); + if (mem) { + mem->used = 1; + *dmab = mem->buffer; + up(&list_mutex); + return dmab->bytes; + } + up(&list_mutex); + return 0; +} + +/** + * snd_dma_free_reserved - unmark the reserved buffer + * @dev: the buffer device info + * + * Looks for the matching reserved buffer and erases the mark on it + * if found. + * + * Returns zero. + */ +int snd_dma_free_reserved(const struct snd_dma_device *dev) +{ + struct snd_mem_list *mem; + + snd_assert(dev, return -EINVAL); + down(&list_mutex); + mem = mem_list_find(dev); + if (mem) + mem->used = 0; + up(&list_mutex); + return 0; +} + +/** + * snd_dma_set_reserved - reserve the buffer + * @dev: the buffer device info + * @dmab: the buffer to reserve + * + * Reserves the given buffer as a reserved buffer. + * When an old reserved buffer already exists, the old one is released + * and replaced with the new one. + * + * When NULL buffer pointer or zero buffer size is given, the existing + * release buffer is released and the entry is removed. + * + * Returns zero if successful, or a negative code at error. + */ +int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab) +{ + struct snd_mem_list *mem; + + snd_assert(dev, return -EINVAL); + down(&list_mutex); + mem = mem_list_find(dev); + if (mem) { + snd_dma_free_pages(dev, &mem->buffer); + if (! dmab || ! dmab->bytes) { + /* remove the entry */ + list_del(&mem->list); + kfree(mem); + up(&list_mutex); + return 0; + } + } else { + if (! dmab || ! dmab->bytes) { + up(&list_mutex); + return 0; + } + mem = kmalloc(sizeof(*mem), GFP_KERNEL); + mem->dev = *dev; + list_add(&mem->list, &mem_list_head); + } + /* store the entry */ + mem->used = 1; + mem->buffer = *dmab; + up(&list_mutex); + return 0; +} + +/* + * purge all reserved buffers + */ +static void free_all_reserved_pages(void) +{ + struct list_head *p; + struct snd_mem_list *mem; + + down(&list_mutex); + while (! list_empty(&mem_list_head)) { + p = mem_list_head.next; + mem = list_entry(p, struct snd_mem_list, list); + list_del(p); + snd_dma_free_pages(&mem->dev, &mem->buffer); + kfree(mem); + } + up(&list_mutex); +} + + +/* + * + * Generic memory allocators + * + */ + +static long snd_allocated_pages; /* holding the number of allocated pages */ + +static void mark_pages(void *res, int order) +{ + struct page *page = virt_to_page(res); + struct page *last_page = page + (1 << order); + while (page < last_page) + SetPageReserved(page++); + snd_allocated_pages += 1 << order; +} + +static void unmark_pages(void *res, int order) +{ + struct page *page = virt_to_page(res); + struct page *last_page = page + (1 << order); + while (page < last_page) + ClearPageReserved(page++); + snd_allocated_pages -= 1 << order; +} + +/** + * snd_malloc_pages - allocate pages with the given size + * @size: the size to allocate in bytes + * @gfp_flags: the allocation conditions, GFP_XXX + * + * Allocates the physically contiguous pages with the given size. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pages(size_t size, unsigned int gfp_flags) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(gfp_flags != 0, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { + mark_pages(res, pg); + } + return res; +} + +/** + * snd_malloc_pages_fallback - allocate pages with the given size with fallback + * @size: the requested size to allocate in bytes + * @gfp_flags: the allocation conditions, GFP_XXX + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +/** + * snd_free_pages - release the pages + * @ptr: the buffer pointer to release + * @size: the allocated buffer size + * + * Releases the buffer allocated via snd_malloc_pages(). + */ +void snd_free_pages(void *ptr, size_t size) +{ + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + free_pages((unsigned long) ptr, pg); +} + +#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) + +/** + * snd_malloc_isa_pages - allocate pages for ISA bus with the given size + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * ISA bus. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr) +{ + void *dma_area; + dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA); + *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; + return dma_area; +} + +/** + * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for PCI bus. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_isa_pages_fallback(size_t size, + dma_addr_t *dma_addr, + size_t *res_size) +{ + void *dma_area; + dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size); + *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; + return dma_area; +} + +#endif /* CONFIG_ISA && !CONFIG_PCI */ + +#ifdef CONFIG_PCI + +/** + * snd_malloc_pci_pages - allocate pages for PCI bus with the given size + * @pci: the pci device pointer + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * PCI bus. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pci_pages(struct pci_dev *pci, + size_t size, + dma_addr_t *dma_addr) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_addr != NULL, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr); + if (res != NULL) { + mark_pages(res, pg); + } + return res; +} + +/** + * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus + * @pci: pci device pointer + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for PCI bus. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, + size_t size, + dma_addr_t *dma_addr, + size_t *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +/** + * snd_free_pci_pages - release the pages + * @pci: pci device pointer + * @size: the allocated buffer size + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_pages(). + */ +void snd_free_pci_pages(struct pci_dev *pci, + size_t size, + void *ptr, + dma_addr_t dma_addr) +{ + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr); +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(__i386__) +/* + * on ix86, we allocate a page with GFP_KERNEL to assure the + * allocation. the code is almost same with kernel/i386/pci-dma.c but + * it allocates only a single page and checks the validity of the + * page address with the given pci dma mask. + */ + +/** + * snd_malloc_pci_page - allocate a page in the valid pci dma mask + * @pci: pci device pointer + * @addrp: the pointer to store the physical address of the buffer + * + * Allocates a single page for the given PCI device and returns + * the virtual address and stores the physical address on addrp. + * + * This function cannot be called from interrupt handlers or + * within spinlocks. + */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) +{ + void *ptr; + dma_addr_t addr; + unsigned long rmask; + + rmask = ~(unsigned long)(pci ? pci->dma_mask : 0x00ffffff); + ptr = (void *)__get_free_page(GFP_KERNEL); + if (ptr) { + addr = virt_to_phys(ptr); + if (((unsigned long)addr + PAGE_SIZE - 1) & rmask) { + /* try to reallocate with the GFP_DMA */ + free_page((unsigned long)ptr); + /* use GFP_ATOMIC for the DMA zone to avoid stall */ + ptr = (void *)__get_free_page(GFP_ATOMIC | GFP_DMA); + if (ptr) /* ok, the address must be within lower 16MB... */ + addr = virt_to_phys(ptr); + else + addr = 0; + } + } else + addr = 0; + if (ptr) { + memset(ptr, 0, PAGE_SIZE); + mark_pages(ptr, 0); + } + *addrp = addr; + return ptr; +} +#else + +/* on other architectures, call snd_malloc_pci_pages() helper function + * which uses pci_alloc_consistent(). + */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) +{ + return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp); +} + +#endif + +#if 0 /* for kernel-doc */ +/** + * snd_free_pci_page - release a page + * @pci: pci device pointer + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_page(). + */ +void snd_free_pci_page(struct pci_dev *pci, void *ptr, dma_addr_t dma_addr); +#endif /* for kernel-doc */ + +#endif /* CONFIG_PCI */ + +#ifdef CONFIG_SBUS + +/** + * snd_malloc_sbus_pages - allocate pages for SBUS with the given size + * @sdev: sbus device pointer + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * SBUS. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_sbus_pages(struct sbus_dev *sdev, + size_t size, + dma_addr_t *dma_addr) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_addr != NULL, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); + if (res != NULL) { + mark_pages(res, pg); + } + return res; +} + +/** + * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS + * @sdev: sbus device pointer + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for SBUS. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, + size_t size, + dma_addr_t *dma_addr, + size_t *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_sbus_pages(sdev, size, dma_addr)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +/** + * snd_free_sbus_pages - release the pages + * @sdev: sbus device pointer + * @size: the allocated buffer size + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_pages(). + */ +void snd_free_sbus_pages(struct sbus_dev *sdev, + size_t size, + void *ptr, + dma_addr_t dma_addr) +{ + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); +} + +#endif /* CONFIG_SBUS */ + + +#ifdef CONFIG_PROC_FS +/* + * proc file interface + */ +static int snd_mem_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + long pages = snd_allocated_pages >> (PAGE_SHIFT-12); + + len += sprintf(page + len, "pages : %li bytes (%li pages per %likB)\n", + pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); + return len; +} +#endif /* CONFIG_PROC_FS */ + +/* + * module entry + */ + +static int __init snd_mem_init(void) +{ + create_proc_read_entry("driver/snd-page-alloc", 0, 0, snd_mem_proc_read, NULL); + return 0; +} + +static void __exit snd_mem_exit(void) +{ + remove_proc_entry("driver/snd-page-alloc", NULL); + free_all_reserved_pages(); + if (snd_allocated_pages > 0) + printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages); +} + + +module_init(snd_mem_init) +module_exit(snd_mem_exit) + + +/* + * exports + */ +EXPORT_SYMBOL(snd_dma_alloc_pages); +EXPORT_SYMBOL(snd_dma_free_pages); +EXPORT_SYMBOL(snd_dma_get_reserved); +EXPORT_SYMBOL(snd_dma_free_reserved); +EXPORT_SYMBOL(snd_dma_set_reserved); + +EXPORT_SYMBOL(snd_malloc_pages); +EXPORT_SYMBOL(snd_malloc_pages_fallback); +EXPORT_SYMBOL(snd_free_pages); +#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) +EXPORT_SYMBOL(snd_malloc_isa_pages); +EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(snd_malloc_pci_pages); +EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); +EXPORT_SYMBOL(snd_malloc_pci_page); +EXPORT_SYMBOL(snd_free_pci_pages); +EXPORT_SYMBOL(snd_malloc_sgbuf_pages); +EXPORT_SYMBOL(snd_free_sgbuf_pages); +#ifdef HACK_PCI_ALLOC_CONSISTENT +EXPORT_SYMBOL(snd_pci_hack_alloc_consistent); +#endif +#endif +#ifdef CONFIG_SBUS +EXPORT_SYMBOL(snd_malloc_sbus_pages); +EXPORT_SYMBOL(snd_malloc_sbus_pages_fallback); +EXPORT_SYMBOL(snd_free_sbus_pages); +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/memory.c linux/sound/core/memory.c --- linux-2.4.21-rc1.orig/sound/core/memory.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/memory.c 2003-02-28 07:29:17.000000000 -0700 @@ -0,0 +1,361 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * Memory allocation helpers. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * memory allocation helpers and debug routines + */ + +#ifdef CONFIG_SND_DEBUG_MEMORY + +struct snd_alloc_track { + unsigned long magic; + void *caller; + size_t size; + struct list_head list; + long data[0]; +}; + +#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data) + +static long snd_alloc_kmalloc; +static long snd_alloc_vmalloc; +static LIST_HEAD(snd_alloc_kmalloc_list); +static LIST_HEAD(snd_alloc_vmalloc_list); +static spinlock_t snd_alloc_kmalloc_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t snd_alloc_vmalloc_lock = SPIN_LOCK_UNLOCKED; +#define KMALLOC_MAGIC 0x87654321 +#define VMALLOC_MAGIC 0x87654320 +static snd_info_entry_t *snd_memory_info_entry; + +void snd_memory_init(void) +{ + snd_alloc_kmalloc = 0; + snd_alloc_vmalloc = 0; +} + +void snd_memory_done(void) +{ + struct list_head *head; + struct snd_alloc_track *t; + + if (snd_alloc_kmalloc > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); + if (snd_alloc_vmalloc > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); + for (head = snd_alloc_kmalloc_list.prev; + head != &snd_alloc_kmalloc_list; head = head->prev) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_ERR "Corrupted kmalloc\n"); + break; + } + snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } + for (head = snd_alloc_vmalloc_list.prev; + head != &snd_alloc_vmalloc_list; head = head->prev) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != VMALLOC_MAGIC) { + snd_printk(KERN_ERR "Corrupted vmalloc\n"); + break; + } + snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } +} + +void *__snd_kmalloc(size_t size, int flags, void *caller) +{ + unsigned long cpu_flags; + struct snd_alloc_track *t; + void *ptr; + + ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags); + if (ptr != NULL) { + t = (struct snd_alloc_track *)ptr; + t->magic = KMALLOC_MAGIC; + t->caller = caller; + spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags); + list_add_tail(&t->list, &snd_alloc_kmalloc_list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags); + t->size = size; + snd_alloc_kmalloc += size; + ptr = t->data; + } + return ptr; +} + +#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); +void *snd_hidden_kmalloc(size_t size, int flags) +{ + return _snd_kmalloc(size, flags); +} + +void snd_hidden_kfree(const void *obj) +{ + unsigned long flags; + struct snd_alloc_track *t; + if (obj == NULL) { + snd_printk(KERN_WARNING "null kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + t = snd_alloc_track_entry(obj); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); + list_del(&t->list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags); + t->magic = 0; + snd_alloc_kmalloc -= t->size; + obj = t; + snd_wrapper_kfree(obj); +} + +void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags) +{ + unsigned long *ptr; + ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); + if (ptr) { + *ptr++ = magic; + memset(ptr, 0, size); + } + return ptr; +} + +void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags) +{ + unsigned long *ptr; + ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); + if (ptr) + *ptr++ = magic; + return ptr; +} + +void snd_magic_kfree(void *_ptr) +{ + unsigned long *ptr = _ptr; + if (ptr == NULL) { + snd_printk(KERN_WARNING "null snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + *--ptr = 0; + { + struct snd_alloc_track *t; + t = snd_alloc_track_entry(ptr); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_ERR "bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + } + snd_hidden_kfree(ptr); + return; +} + +void *snd_hidden_vmalloc(unsigned long size) +{ + void *ptr; + ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track)); + if (ptr) { + struct snd_alloc_track *t = (struct snd_alloc_track *)ptr; + t->magic = VMALLOC_MAGIC; + t->caller = __builtin_return_address(0); + spin_lock(&snd_alloc_vmalloc_lock); + list_add_tail(&t->list, &snd_alloc_vmalloc_list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->size = size; + snd_alloc_vmalloc += size; + ptr = t->data; + } + return ptr; +} + +void snd_hidden_vfree(void *obj) +{ + struct snd_alloc_track *t; + if (obj == NULL) { + snd_printk(KERN_WARNING "null vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + t = snd_alloc_track_entry(obj); + if (t->magic != VMALLOC_MAGIC) { + snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock(&snd_alloc_vmalloc_lock); + list_del(&t->list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->magic = 0; + snd_alloc_vmalloc -= t->size; + obj = t; + snd_wrapper_vfree(obj); +} + +static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); + snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc); +} + +int __init snd_memory_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256; + entry->c.text.read = snd_memory_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_memory_info_entry = entry; + return 0; +} + +int __exit snd_memory_info_done(void) +{ + if (snd_memory_info_entry) + snd_info_unregister(snd_memory_info_entry); + return 0; +} + +#else + +#define _snd_kmalloc kmalloc + +#endif /* CONFIG_SND_DEBUG_MEMORY */ + +/** + * snd_kcalloc - memory allocation and zero-clear + * @size: the size to allocate in bytes + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and initializes it to zero. + * + * Returns the pointer, or NULL if no enoguh memory. + */ +void *snd_kcalloc(size_t size, int flags) +{ + void *ptr; + + ptr = _snd_kmalloc(size, flags); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +/** + * snd_kmalloc_strdup - copy the string + * @string: the original string + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and copies the string to it. + * + * Returns the pointer, or NULL if no enoguh memory. + */ +char *snd_kmalloc_strdup(const char *string, int flags) +{ + size_t len; + char *ptr; + + if (!string) + return NULL; + len = strlen(string) + 1; + ptr = _snd_kmalloc(len, flags); + if (ptr) + memcpy(ptr, string, len); + return ptr; +} + +/** + * copy_to_user_fromio - copy data from mmio-space to user-space + * @dst: the destination pointer on user-space + * @src: the source pointer on mmio + * @count: the data size to copy in bytes + * + * Copies the data from mmio-space to user-space. + * + * Returns zero if successful, or non-zero on failure. + */ +int copy_to_user_fromio(void *dst, unsigned long src, size_t count) +{ +#if defined(__i386__) || defined(CONFIG_SPARC32) + return copy_to_user(dst, (const void*)src, count) ? -EFAULT : 0; +#else + char buf[256]; + while (count) { + size_t c = count; + if (c > sizeof(buf)) + c = sizeof(buf); + memcpy_fromio(buf, (void*)src, c); + if (copy_to_user(dst, buf, c)) + return -EFAULT; + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} + +/** + * copy_from_user_toio - copy data from user-space to mmio-space + * @dst: the destination pointer on mmio-space + * @src: the source pointer on user-space + * @count: the data size to copy in bytes + * + * Copies the data from user-space to mmio-space. + * + * Returns zero if successful, or non-zero on failure. + */ +int copy_from_user_toio(unsigned long dst, const void *src, size_t count) +{ +#if defined(__i386__) || defined(CONFIG_SPARC32) + return copy_from_user((void*)dst, src, count) ? -EFAULT : 0; +#else + char buf[256]; + while (count) { + size_t c = count; + if (c > sizeof(buf)) + c = sizeof(buf); + if (copy_from_user(buf, src, c)) + return -EFAULT; + memcpy_toio((void*)dst, buf, c); + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} diff -urN linux-2.4.21-rc1.orig/sound/core/memory_wrapper.c linux/sound/core/memory_wrapper.c --- linux-2.4.21-rc1.orig/sound/core/memory_wrapper.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/memory_wrapper.c 2003-02-28 07:29:18.000000000 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright (c) by Jaroslav Kysela + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + + +#ifdef HACK_PCI_ALLOC_CONSISTENT +/* + * A dirty hack... when the kernel code is fixed this should be removed. + * + * since pci_alloc_consistent always tries GFP_DMA when the requested + * pci memory region is below 32bit, it happens quite often that even + * 2 order of pages cannot be allocated. + * + * so in the following, we allocate at first without dma_mask, so that + * allocation will be done without GFP_DMA. if the area doesn't match + * with the requested region, then realloate with the original dma_mask + * again. + */ + +void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + u64 dma_mask; + unsigned long rmask; + + if (hwdev == NULL) + return pci_alloc_consistent(hwdev, size, dma_handle); + dma_mask = hwdev->dma_mask; + rmask = ~((unsigned long)dma_mask); + hwdev->dma_mask = 0xffffffff; /* do without masking */ + ret = pci_alloc_consistent(hwdev, size, dma_handle); + hwdev->dma_mask = dma_mask; /* restore */ + if (ret) { + /* obtained address is out of range? */ + if (((unsigned long)*dma_handle + size - 1) & rmask) { + /* reallocate with the proper mask */ + pci_free_consistent(hwdev, size, ret, *dma_handle); + ret = pci_alloc_consistent(hwdev, size, dma_handle); + } + } else { + /* wish to success now with the proper mask... */ + if (dma_mask != 0xffffffff) + ret = pci_alloc_consistent(hwdev, size, dma_handle); + } + return ret; +} + +#endif /* HACK_PCI_ALLOC_CONSISTENT */ diff -urN linux-2.4.21-rc1.orig/sound/core/misc.c linux/sound/core/misc.c --- linux-2.4.21-rc1.orig/sound/core/misc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/misc.c 2003-04-29 23:51:38.000000000 -0600 @@ -0,0 +1,347 @@ +/* + * Misc and compatibility things + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +int snd_task_name(struct task_struct *task, char *name, size_t size) +{ + unsigned int idx; + + snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL); + for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++) + name[idx] = task->comm[idx]; + name[idx] = '\0'; + return 0; +} + +#ifdef CONFIG_SND_VERBOSE_PRINTK +void snd_verbose_printk(const char *file, int line, const char *format, ...) +{ + va_list args; + char tmpbuf[512]; + + if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { + char tmp[] = "<0>"; + tmp[1] = format[1]; + printk("%sALSA %s:%d: ", tmp, file, line); + format += 3; + } else { + printk("ALSA %s:%d: ", file, line); + } + va_start(args, format); + vsnprintf(tmpbuf, sizeof(tmpbuf)-1, format, args); + va_end(args); + tmpbuf[sizeof(tmpbuf)-1] = '\0'; + printk(tmpbuf); +} +#endif + +#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) +void snd_verbose_printd(const char *file, int line, const char *format, ...) +{ + va_list args; + char tmpbuf[512]; + + if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { + char tmp[] = "<0>"; + tmp[1] = format[1]; + printk("%sALSA %s:%d: ", tmp, file, line); + format += 3; + } else { + printk(KERN_DEBUG "ALSA %s:%d: ", file, line); + } + va_start(args, format); + vsnprintf(tmpbuf, sizeof(tmpbuf)-1, format, args); + va_end(args); + tmpbuf[sizeof(tmpbuf)-1] = '\0'; + printk(tmpbuf); + +} +#endif + + +/* Compatibility stuff ripped from acore/misc.c */ + + +#if defined(CONFIG_DEVFS_FS) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,29) + +#include + +void snd_compat_devfs_remove(const char *fmt, ...) +{ + char buf[64]; + va_list args; + int n; + + va_start(args, fmt); + n = vsnprintf(buf, 64, fmt, args); + if (n < 64) { + devfs_handle_t de = devfs_get_handle(NULL, buf, 0, 0, 0, 0); + devfs_unregister(de); + devfs_put(de); + } +} + +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + +#include +#include + +int try_inc_mod_count(struct module *module) +{ + __MOD_INC_USE_COUNT(module); + return 1; +} + +struct resource *snd_compat_request_region(unsigned long start, unsigned long size, const char *name, int is_memory) +{ + struct resource *resource; + +#ifdef CONFIG_SND_DEBUG_MEMORY + /* DON'T use kmalloc here; the allocated resource is released + * by kfree without wrapper in each driver + */ + resource = snd_wrapper_kmalloc(sizeof(struct resource), GFP_KERNEL); +#else + resource = kmalloc(sizeof(struct resource), GFP_KERNEL); +#endif + if (resource == NULL) + return NULL; + if (! is_memory && check_region(start, size)) { + kfree_nocheck(resource); + return NULL; + } + memset(resource, 0, sizeof(struct resource)); + snd_wrapper_request_region(start, size, name); + resource->name = name; + resource->start = start; + resource->end = start + size - 1; + resource->flags = is_memory ? IORESOURCE_MEM : IORESOURCE_IO; + return resource; +} + +int snd_compat_release_resource(struct resource *resource) +{ + snd_runtime_check(resource != NULL, return -EINVAL); + if (resource->flags & IORESOURCE_MEM) + return 0; + release_region(resource->start, (resource->end - resource->start) + 1); + return 0; +} + +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + +#include + +static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(pm_devs); + +#ifdef CONFIG_PCI +static struct pm_dev *pci_compat_pm_dev; +static int pci_compat_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data) +{ + struct pci_dev *dev; + switch (rqst) { + case PM_SUSPEND: + pci_for_each_dev(dev) { + struct pci_driver *drv = snd_pci_compat_get_pci_driver(dev); + if (drv && drv->suspend) + drv->suspend(dev); + } + break; + case PM_RESUME: + pci_for_each_dev(dev) { + struct pci_driver *drv = snd_pci_compat_get_pci_driver(dev); + if (drv && drv->resume) + drv->resume(dev); + } + break; + } + return 0; +} +#endif + +static int snd_apm_callback(apm_event_t ev) +{ + struct list_head *entry; + pm_request_t rqst; + void *data; + int status; + + switch (ev) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + case APM_CRITICAL_SUSPEND: + rqst = PM_SUSPEND; + data = (void *)3; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + case APM_STANDBY_RESUME: /* ??? */ + rqst = PM_RESUME; + data = (void *)0; + break; + default: + return 0; + } + for (entry = pm_devs.next; entry != &pm_devs; entry = entry->next) { + struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); + if ((status = pm_send(dev, rqst, data))) + return status; + } + return 0; +} + +int __init pm_init(void) +{ + if (apm_register_callback(snd_apm_callback)) + snd_printk("apm_register_callback failure!\n"); +#ifdef CONFIG_PCI + pci_compat_pm_dev = pm_register(PM_PCI_DEV, 0, pci_compat_pm_callback); +#endif + return 0; +} + +void __exit pm_done(void) +{ +#ifdef CONFIG_PCI + if (pci_compat_pm_dev) + pm_unregister(pci_compat_pm_dev); +#endif + apm_unregister_callback(snd_apm_callback); +} + +struct pm_dev *pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback) +{ + struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); + + if (dev) { + unsigned long flags; + + memset(dev, 0, sizeof(*dev)); + dev->type = type; + dev->id = id; + dev->callback = callback; + + spin_lock_irqsave(&pm_devs_lock, flags); + list_add(&dev->entry, &pm_devs); + spin_unlock_irqrestore(&pm_devs_lock, flags); + } + return dev; +} + +void pm_unregister(struct pm_dev *dev) +{ + if (dev) { + unsigned long flags; + + spin_lock_irqsave(&pm_devs_lock, flags); + list_del(&dev->entry); + spin_unlock_irqrestore(&pm_devs_lock, flags); + + kfree(dev); + } +} + +int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + int status = 0; + int prev_state, next_state; + + switch (rqst) { + case PM_SUSPEND: + case PM_RESUME: + prev_state = dev->state; + next_state = (int) data; + if (prev_state != next_state) { + if (dev->callback) + status = (*dev->callback)(dev, rqst, data); + if (!status) { + dev->state = next_state; + dev->prev_state = prev_state; + } + } else { + dev->prev_state = prev_state; + } + break; + default: + if (dev->callback) + status = (*dev->callback)(dev, rqst, data); + break; + } + return status; +} + +#endif /* kernel version < 2.3.0 && CONFIG_APM */ + +/* workqueue-alike; 2.5.45 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 45) + +static int work_caller(void *data) +{ + struct work_struct *works = data; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) + lock_kernel(); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 18) + daemonize(); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 8) + reparent_to_init(); +#endif + strcpy(current->comm, "snd-free"); /* FIXME: different names? */ + + works->func(works->data); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) + unlock_kernel(); +#endif + kfree(works); + + return 0; +} + +int snd_compat_schedule_work(struct work_struct *works) +{ + struct work_struct *wp = kmalloc(sizeof(*wp), GFP_KERNEL); + if (! wp) + return 0; + *wp = *works; + return kernel_thread(work_caller, wp, 0) >= 0; +} + +#endif + diff -urN linux-2.4.21-rc1.orig/sound/core/oss/Makefile linux/sound/core/oss/Makefile --- linux-2.4.21-rc1.orig/sound/core/oss/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,26 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := oss.o + +export-objs := mixer_oss.o + +list-multi := snd-mixer-oss.o snd-pcm-oss.o + +snd-mixer-oss-objs := mixer_oss.o + +snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \ + io.o copy.o linear.o mulaw.o route.o rate.o + +obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o +obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o + +include $(TOPDIR)/Rules.make + +snd-mixer-oss.o: $(snd-mixer-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mixer-oss-objs) + +snd-pcm-oss.o: $(snd-pcm-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-oss-objs) diff -urN linux-2.4.21-rc1.orig/sound/core/oss/copy.c linux/sound/core/oss/copy.c --- linux-2.4.21-rc1.orig/sound/core/oss/copy.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/copy.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,87 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "pcm_plugin.h" + +static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + unsigned int channel; + unsigned int nchannels; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; + nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; channel++) { + snd_assert(src_channels->area.first % 8 == 0 && + src_channels->area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels->area.first % 8 == 0 && + dst_channels->area.step % 8 == 0, + return -ENXIO); + if (!src_channels->enabled) { + if (dst_channels->wanted) + snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); + dst_channels->enabled = 0; + continue; + } + dst_channels->enabled = 1; + snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format); + src_channels++; + dst_channels++; + } + return frames; +} + +int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + snd_pcm_plugin_t *plugin; + int width; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->format == dst_format->format, return -ENXIO); + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + + width = snd_pcm_format_physical_width(src_format->format); + snd_assert(width > 0, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format, + 0, &plugin); + if (err < 0) + return err; + plugin->transfer = copy_transfer; + *r_plugin = plugin; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/oss/io.c linux/sound/core/oss/io.c --- linux-2.4.21-rc1.orig/sound/core/oss/io.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/io.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,134 @@ +/* + * PCM I/O Plug-In Interface + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) +#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) +#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) +#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) + +/* + * Basic io plugin + */ + +static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, + snd_pcm_uframes_t frames) +{ + snd_assert(plugin != NULL, return -ENXIO); + snd_assert(src_channels != NULL, return -ENXIO); + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + return pcm_write(plugin->plug, src_channels->area.addr, frames); + } else { + int channel, channels = plugin->dst_format.channels; + void **bufs = (void**)plugin->extra_data; + snd_assert(bufs != NULL, return -ENXIO); + for (channel = 0; channel < channels; channel++) { + if (src_channels[channel].enabled) + bufs[channel] = src_channels[channel].area.addr; + else + bufs[channel] = NULL; + } + return pcm_writev(plugin->plug, bufs, frames); + } +} + +static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + snd_assert(plugin != NULL, return -ENXIO); + snd_assert(dst_channels != NULL, return -ENXIO); + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + return pcm_read(plugin->plug, dst_channels->area.addr, frames); + } else { + int channel, channels = plugin->dst_format.channels; + void **bufs = (void**)plugin->extra_data; + snd_assert(bufs != NULL, return -ENXIO); + for (channel = 0; channel < channels; channel++) { + if (dst_channels[channel].enabled) + bufs[channel] = dst_channels[channel].area.addr; + else + bufs[channel] = NULL; + } + return pcm_readv(plugin->plug, bufs, frames); + } + return 0; +} + +static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels) +{ + int err; + unsigned int channel; + snd_pcm_plugin_channel_t *v; + err = snd_pcm_plugin_client_channels(plugin, frames, &v); + if (err < 0) + return err; + *channels = v; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v) + v->wanted = 1; + } + return frames; +} + +int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug, + snd_pcm_hw_params_t *params, + snd_pcm_plugin_t **r_plugin) +{ + int err; + snd_pcm_plugin_format_t format; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + snd_assert(plug != NULL && params != NULL, return -ENXIO); + format.format = params_format(params); + format.rate = params_rate(params); + format.channels = params_channels(params); + err = snd_pcm_plugin_build(plug, "I/O io", + &format, &format, + sizeof(void *) * format.channels, + &plugin); + if (err < 0) + return err; + plugin->access = params_access(params); + if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { + plugin->transfer = io_playback_transfer; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) + plugin->client_channels = io_src_channels; + } else { + plugin->transfer = io_capture_transfer; + } + + *r_plugin = plugin; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/oss/linear.c linux/sound/core/oss/linear.c --- linux-2.4.21-rc1.orig/sound/core/oss/linear.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/linear.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,158 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 1999 by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "pcm_plugin.h" + +/* + * Basic linear conversion plugin + */ + +typedef struct linear_private_data { + int conv; +} linear_t; + +static void convert(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + linear_t *data = (linear_t *)plugin->extra_data; + void *conv = conv_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + linear_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + data = (linear_t *)plugin->extra_data; + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + convert(plugin, src_channels, dst_channels, frames); + return frames; +} + +int conv_index(int src_format, int dst_format) +{ + int src_endian, dst_endian, sign, src_width, dst_width; + + sign = (snd_pcm_format_signed(src_format) != + snd_pcm_format_signed(dst_format)); +#ifdef SNDRV_LITTLE_ENDIAN + src_endian = snd_pcm_format_big_endian(src_format); + dst_endian = snd_pcm_format_big_endian(dst_format); +#else + src_endian = snd_pcm_format_little_endian(src_format); + dst_endian = snd_pcm_format_little_endian(dst_format); +#endif + + if (src_endian < 0) + src_endian = 0; + if (dst_endian < 0) + dst_endian = 0; + + src_width = snd_pcm_format_width(src_format) / 8 - 1; + dst_width = snd_pcm_format_width(dst_format) / 8 - 1; + + return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; +} + +int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + struct linear_private_data *data; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) && + snd_pcm_format_linear(dst_format->format), return -ENXIO); + + err = snd_pcm_plugin_build(plug, "linear format conversion", + src_format, dst_format, + sizeof(linear_t), &plugin); + if (err < 0) + return err; + data = (linear_t *)plugin->extra_data; + data->conv = conv_index(src_format->format, dst_format->format); + plugin->transfer = linear_transfer; + *r_plugin = plugin; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/oss/mixer_oss.c linux/sound/core/oss/mixer_oss.c --- linux-2.4.21-rc1.orig/sound/core/oss/mixer_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/mixer_oss.c 2003-01-31 08:19:31.000000000 -0700 @@ -0,0 +1,1279 @@ +/* + * OSS emulation layer for the mixer interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Mixer OSS emulation for ALSA."); +MODULE_LICENSE("GPL"); + +static int snd_mixer_oss_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + snd_card_t *card; + snd_mixer_oss_file_t *fmixer; + int err; + + if ((card = snd_cards[cardnum]) == NULL) + return -ENODEV; + if (card->mixer_oss == NULL) + return -ENODEV; + err = snd_card_file_add(card, file); + if (err < 0) + return err; + fmixer = (snd_mixer_oss_file_t *)snd_kcalloc(sizeof(*fmixer), GFP_KERNEL); + if (fmixer == NULL) { + snd_card_file_remove(card, file); + return -ENOMEM; + } + fmixer->card = card; + fmixer->mixer = card->mixer_oss; + file->private_data = fmixer; + if (!try_module_get(card->module)) { + kfree(fmixer); + snd_card_file_remove(card, file); + return -EFAULT; + } + return 0; +} + +static int snd_mixer_oss_release(struct inode *inode, struct file *file) +{ + snd_mixer_oss_file_t *fmixer; + + if (file->private_data) { + fmixer = (snd_mixer_oss_file_t *) file->private_data; + module_put(fmixer->card->module); + snd_card_file_remove(fmixer->card, file); + kfree(fmixer); + } + return 0; +} + +static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer, + mixer_info *_info) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + struct mixer_info info; + + memset(&info, 0, sizeof(info)); + strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1); + strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1); + info.modify_counter = card->mixer_oss_change_count; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer, + _old_mixer_info *_info) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + _old_mixer_info info; + + memset(&info, 0, sizeof(info)); + strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1); + strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->get_recsrc && mixer->put_recsrc) + result |= SOUND_CAP_EXCL_INPUT; + return result; +} + +static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, chn; + + if (mixer == NULL) + return -EIO; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_volume || pslot->put_recsrc) + result |= 1 << chn; + } + return result; +} + +static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, chn; + + if (mixer == NULL) + return -EIO; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_volume && pslot->stereo) + result |= 1 << chn; + } + return result; +} + +static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ + result = mixer->mask_recsrc; + } else { + snd_mixer_oss_slot_t *pslot; + int chn; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_recsrc) + result |= 1 << chn; + } + } + return result; +} + +static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ + int err; + if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + return err; + result = 1 << result; + } else { + snd_mixer_oss_slot_t *pslot; + int chn; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->get_recsrc) { + int active = 0; + pslot->get_recsrc(fmixer, pslot, &active); + if (active) + result |= 1 << chn; + } + } + } + return mixer->oss_recsrc = result; +} + +static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ + if (recsrc & ~mixer->oss_recsrc) + recsrc &= ~mixer->oss_recsrc; + mixer->put_recsrc(fmixer, ffz(~recsrc)); + mixer->get_recsrc(fmixer, &result); + result = 1 << result; + } else { + snd_mixer_oss_slot_t *pslot; + int chn, active; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_recsrc) { + active = (recsrc & (1 << chn)) ? 1 : 0; + pslot->put_recsrc(fmixer, pslot, active); + } + } + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->get_recsrc) { + active = 0; + pslot->get_recsrc(fmixer, pslot, &active); + if (active) + result |= 1 << chn; + } + } + } + return result; +} + +static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, left, right; + + if (mixer == NULL || slot > 30) + return -EIO; + pslot = &mixer->slots[slot]; + left = pslot->volume[0]; + right = pslot->volume[1]; + if (pslot->get_volume) + result = pslot->get_volume(fmixer, pslot, &left, &right); + if (!pslot->stereo) + right = left; + snd_assert(left >= 0 && left <= 100, return -EIO); + snd_assert(right >= 0 && right <= 100, return -EIO); + if (result >= 0) { + pslot->volume[0] = left; + pslot->volume[1] = right; + result = (left & 0xff) | ((right & 0xff) << 8); + } + return result; +} + +static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer, + int slot, int volume) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff; + + if (mixer == NULL || slot > 30) + return -EIO; + pslot = &mixer->slots[slot]; + if (left > 100) + left = 100; + if (right > 100) + right = 100; + if (!pslot->stereo) + right = left; + if (pslot->put_volume) + result = pslot->put_volume(fmixer, pslot, left, right); + if (result < 0) + return result; + pslot->volume[0] = left; + pslot->volume[1] = right; + return (left & 0xff) | ((right & 0xff) << 8); +} + +static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg) +{ + int tmp; + + snd_assert(fmixer != NULL, return -ENXIO); + if (((cmd >> 8) & 0xff) == 'M') { + switch (cmd) { + case SOUND_MIXER_INFO: + return snd_mixer_oss_info(fmixer, (mixer_info *)arg); + case SOUND_OLD_MIXER_INFO: + return snd_mixer_oss_info_obsolete(fmixer, (_old_mixer_info *)arg); + case SOUND_MIXER_WRITE_RECSRC: + if (get_user(tmp, (int *)arg)) + return -EFAULT; + tmp = snd_mixer_oss_set_recsrc(fmixer, tmp); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case OSS_GETVERSION: + return put_user(SNDRV_OSS_VERSION, (int *) arg); + case SOUND_MIXER_READ_DEVMASK: + tmp = snd_mixer_oss_devmask(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_STEREODEVS: + tmp = snd_mixer_oss_stereodevs(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_RECMASK: + tmp = snd_mixer_oss_recmask(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_CAPS: + tmp = snd_mixer_oss_caps(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_RECSRC: + tmp = snd_mixer_oss_get_recsrc(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } + } + if (cmd & SIOC_IN) { + if (get_user(tmp, (int *)arg)) + return -EFAULT; + tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } else if (cmd & SIOC_OUT) { + tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } + return -ENXIO; +} + +int snd_mixer_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); +} + +int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg) +{ + snd_mixer_oss_file_t fmixer; + + snd_assert(card != NULL, return -ENXIO); + if (card->mixer_oss == NULL) + return -ENXIO; + memset(&fmixer, 0, sizeof(fmixer)); + fmixer.card = card; + fmixer.mixer = card->mixer_oss; + return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); +} + +/* + * REGISTRATION PART + */ + +static struct file_operations snd_mixer_oss_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .open = snd_mixer_oss_open, + .release = snd_mixer_oss_release, + .ioctl = snd_mixer_oss_ioctl, +}; + +static snd_minor_t snd_mixer_oss_reg = +{ + .comment = "mixer", + .f_ops = &snd_mixer_oss_f_ops, +}; + +/* + * utilities + */ + +static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax) +{ + long orange = omax - omin, nrange = nmax - nmin; + + if (orange == 0) + return 0; + return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin; +} + +/* convert from alsa native to oss values (0-100) */ +static long snd_mixer_oss_conv1(long val, long min, long max, int *old) +{ + if (val == snd_mixer_oss_conv(*old, 0, 100, min, max)) + return *old; + return snd_mixer_oss_conv(val, min, max, 0, 100); +} + +/* convert from oss to alsa native values */ +static long snd_mixer_oss_conv2(long val, long min, long max) +{ + return snd_mixer_oss_conv(val, 0, 100, min, max); +} + +#if 0 +static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot) +{ + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer) + mixer->mask_recsrc |= 1 << slot; +} + +static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot) +{ + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer && (mixer->mask_recsrc & (1 << slot))) + return 1; + return 0; +} +#endif + +#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250 + +#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0 +#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1 +#define SNDRV_MIXER_OSS_ITEM_GROUTE 2 +#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3 +#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4 +#define SNDRV_MIXER_OSS_ITEM_PROUTE 5 +#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6 +#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7 +#define SNDRV_MIXER_OSS_ITEM_CROUTE 8 +#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9 +#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10 + +#define SNDRV_MIXER_OSS_ITEM_COUNT 11 + +#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0) +#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1) +#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2) +#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3) +#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4) +#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5) +#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6) +#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7) +#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8) +#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9) +#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10) + +struct slot { + unsigned int signature; + unsigned int present; + unsigned int channels; + snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT]; + unsigned int capture_item; + struct snd_mixer_oss_assign_table *assigned; + unsigned int allocated: 1; +}; + +static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index) +{ + snd_card_t * card = mixer->card; + snd_ctl_elem_id_t id; + + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, name); + id.index = index; + return snd_ctl_find_id(card, &id); +} + +static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int *left, int *right) +{ + snd_ctl_elem_info_t *uinfo; + snd_ctl_elem_value_t *uctl; + + snd_runtime_check(kctl != NULL, return); + uinfo = snd_kcalloc(sizeof(*uinfo), GFP_ATOMIC); + uctl = snd_kcalloc(sizeof(*uctl), GFP_ATOMIC); + if (uinfo == NULL || uctl == NULL) + goto __unalloc; + snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); + snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc); + snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, return); + *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]); + if (uinfo->count > 1) + *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]); + __unalloc: + if (uctl) + kfree(uctl); + if (uinfo) + kfree(uinfo); +} + +static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int *left, int *right, + int route) +{ + snd_ctl_elem_info_t *uinfo; + snd_ctl_elem_value_t *uctl; + + snd_runtime_check(kctl != NULL, return); + uinfo = snd_kcalloc(sizeof(*uinfo), GFP_ATOMIC); + uctl = snd_kcalloc(sizeof(*uctl), GFP_ATOMIC); + if (uinfo == NULL || uctl == NULL) + goto __unalloc; + snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); + snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc); + if (!uctl->value.integer.value[0]) { + *left = 0; + if (uinfo->count == 1) + *right = 0; + } + if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1]) + *right = 0; + __unalloc: + if (uctl) + kfree(uctl); + if (uinfo) + kfree(uinfo); +} + +static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *left, int *right) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + *left = *right = 100; + down_read(&card->controls_rwsem); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); + } + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } + up_read(&card->controls_rwsem); + return 0; +} + +static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int left, int right) +{ + snd_ctl_elem_info_t *uinfo; + snd_ctl_elem_value_t *uctl; + int res; + + snd_runtime_check(kctl != NULL, return); + uinfo = snd_kcalloc(sizeof(*uinfo), GFP_ATOMIC); + uctl = snd_kcalloc(sizeof(*uctl), GFP_ATOMIC); + if (uinfo == NULL || uctl == NULL) + goto __unalloc; + snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); + snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, return); + uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max); + if (uinfo->count > 1) + uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max); + snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc); + if (res > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + __unalloc: + if (uctl) + kfree(uctl); + if (uinfo) + kfree(uinfo); +} + +static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int left, int right, + int route) +{ + snd_ctl_elem_info_t *uinfo; + snd_ctl_elem_value_t *uctl; + int res; + + snd_runtime_check(kctl != NULL, return); + uinfo = snd_kcalloc(sizeof(*uinfo), GFP_ATOMIC); + uctl = snd_kcalloc(sizeof(*uctl), GFP_ATOMIC); + if (uinfo == NULL || uctl == NULL) + goto __unalloc; + snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); + if (uinfo->count > 1) { + uctl->value.integer.value[0] = left > 0 ? 1 : 0; + uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0; + if (route) { + uctl->value.integer.value[1] = + uctl->value.integer.value[2] = 0; + } + } else { + uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0; + } + snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc); + if (res > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + __unalloc: + if (uctl) + kfree(uctl); + if (uinfo) + kfree(uinfo); +} + +static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int left, int right) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + down_read(&card->controls_rwsem); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); + } + if (left || right) { + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } else { + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } + } + up_read(&card->controls_rwsem); + return 0; +} + +static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + int left, right; + + left = right = 1; + down_read(&card->controls_rwsem); + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0); + up_read(&card->controls_rwsem); + *active = (left || right) ? 1 : 0; + return 0; +} + +static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + int left, right; + + left = right = 1; + down_read(&card->controls_rwsem); + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1); + up_read(&card->controls_rwsem); + *active = (left || right) ? 1 : 0; + return 0; +} + +static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + down_read(&card->controls_rwsem); + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); + up_read(&card->controls_rwsem); + return 0; +} + +static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + down_read(&card->controls_rwsem); + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); + up_read(&card->controls_rwsem); + return 0; +} + +static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int *active_index) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *pslot; + struct slot *slot; + snd_ctl_elem_info_t *uinfo; + snd_ctl_elem_value_t *uctl; + int err, idx; + + uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); + uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + if (uinfo == NULL || uctl == NULL) { + err = -ENOMEM; + goto __unlock; + } + down_read(&card->controls_rwsem); + kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); + snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock); + snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock); + snd_runtime_check(!(err = kctl->get(kctl, uctl)), goto __unlock); + for (idx = 0; idx < 32; idx++) { + if (!(mixer->mask_recsrc & (1 << idx))) + continue; + pslot = &mixer->slots[idx]; + slot = (struct slot *)pslot->private_data; + if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) + continue; + if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) + continue; + if (slot->capture_item == uctl->value.enumerated.item[0]) { + *active_index = idx; + break; + } + } + err = 0; + __unlock: + up_read(&card->controls_rwsem); + if (uctl) + kfree(uctl); + if (uinfo) + kfree(uinfo); + return err; +} + +static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int active_index) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *pslot; + struct slot *slot = NULL; + snd_ctl_elem_info_t *uinfo; + snd_ctl_elem_value_t *uctl; + int err; + unsigned int idx; + + uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); + uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + if (uinfo == NULL || uctl == NULL) { + err = -ENOMEM; + goto __unlock; + } + down_read(&card->controls_rwsem); + kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); + snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock); + snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock); + for (idx = 0; idx < 32; idx++) { + if (!(mixer->mask_recsrc & (1 << idx))) + continue; + pslot = &mixer->slots[idx]; + slot = (struct slot *)pslot->private_data; + if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) + continue; + if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) + continue; + if (idx == active_index) + break; + slot = NULL; + } + snd_runtime_check(slot != NULL, goto __unlock); + for (idx = 0; idx < uinfo->count; idx++) + uctl->value.enumerated.item[idx] = slot->capture_item; + snd_runtime_check((err = kctl->put(kctl, uctl)) >= 0, ); + if (err > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + err = 0; + __unlock: + up_read(&card->controls_rwsem); + if (uctl) + kfree(uctl); + if (uinfo) + kfree(uinfo); + return err; +} + +struct snd_mixer_oss_assign_table { + int oss_id; + const char *name; + int index; +}; + +static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item) +{ + snd_ctl_elem_info_t info; + snd_kcontrol_t *kcontrol; + int err; + + kcontrol = snd_mixer_oss_test_id(mixer, name, index); + if (kcontrol == NULL) + return 0; + snd_runtime_check((err = kcontrol->info(kcontrol, &info)) >= 0, return err); + slot->kcontrol[item] = kcontrol; + if (info.count > slot->channels) + slot->channels = info.count; + slot->present |= 1 << item; + return 0; +} + +static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn) +{ + struct slot *p = (struct slot *)chn->private_data; + if (p) { + if (p->allocated && p->assigned) { + kfree(p->assigned->name); + kfree(p->assigned); + } + kfree(p); + } +} + +static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot) +{ + int idx = rslot->number; /* remember this */ + if (rslot->private_free) + rslot->private_free(rslot); + memset(rslot, 0, sizeof(*rslot)); + rslot->number = idx; +} + +static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated) +{ + struct slot slot; + struct slot *pslot; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *rslot; + char str[64]; + + memset(&slot, 0, sizeof(slot)); + if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index, + SNDRV_MIXER_OSS_ITEM_GLOBAL)) + return 0; + sprintf(str, "%s Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GSWITCH)) + return 0; + sprintf(str, "%s Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GROUTE)) + return 0; + sprintf(str, "%s Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GVOLUME)) + return 0; + sprintf(str, "%s Playback Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PSWITCH)) + return 0; + sprintf(str, "%s Playback Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PROUTE)) + return 0; + sprintf(str, "%s Playback Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PVOLUME)) + return 0; + sprintf(str, "%s Capture Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CSWITCH)) + return 0; + sprintf(str, "%s Capture Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CROUTE)) + return 0; + sprintf(str, "%s Capture Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CVOLUME)) + return 0; + if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) { + snd_ctl_elem_info_t uinfo; + + memset(&uinfo, 0, sizeof(uinfo)); + if (kctl->info(kctl, &uinfo)) + return 0; + strcpy(str, ptr->name); + if (!strcmp(str, "Master")) + strcpy(str, "Mix"); + if (!strcmp(str, "Master Mono")) + strcpy(str, "Mix Mono"); + slot.capture_item = 0; + if (!strcmp(uinfo.value.enumerated.name, str)) { + slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; + } else { + for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) { + uinfo.value.enumerated.item = slot.capture_item; + if (kctl->info(kctl, &uinfo)) + return 0; + if (!strcmp(uinfo.value.enumerated.name, str)) { + slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; + break; + } + } + } + } + if (slot.present != 0) { + pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL); + snd_runtime_check(pslot != NULL, return -ENOMEM); + *pslot = slot; + pslot->signature = SNDRV_MIXER_OSS_SIGNATURE; + pslot->assigned = ptr; + pslot->allocated = ptr_allocated; + rslot = &mixer->slots[ptr->oss_id]; + mixer_slot_clear(rslot); + rslot->stereo = slot.channels > 1 ? 1 : 0; + rslot->get_volume = snd_mixer_oss_get_volume1; + rslot->put_volume = snd_mixer_oss_put_volume1; + /* note: ES18xx have both Capture Source and XX Capture Volume !!! */ + if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { + rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw; + rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw; + } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { + rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route; + rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route; + } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) { + mixer->mask_recsrc |= 1 << ptr->oss_id; + } + rslot->private_data = pslot; + rslot->private_free = snd_mixer_oss_slot_free; + return 1; + } + return 0; +} + +/* + */ +#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name +static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = { + MIXER_VOL(VOLUME), + MIXER_VOL(BASS), + MIXER_VOL(TREBLE), + MIXER_VOL(SYNTH), + MIXER_VOL(PCM), + MIXER_VOL(SPEAKER), + MIXER_VOL(LINE), + MIXER_VOL(MIC), + MIXER_VOL(CD), + MIXER_VOL(IMIX), + MIXER_VOL(ALTPCM), + MIXER_VOL(RECLEV), + MIXER_VOL(IGAIN), + MIXER_VOL(OGAIN), + MIXER_VOL(LINE1), + MIXER_VOL(LINE2), + MIXER_VOL(LINE3), + MIXER_VOL(DIGITAL1), + MIXER_VOL(DIGITAL2), + MIXER_VOL(DIGITAL3), + MIXER_VOL(PHONEIN), + MIXER_VOL(PHONEOUT), + MIXER_VOL(VIDEO), + MIXER_VOL(RADIO), + MIXER_VOL(MONITOR), +}; + +/* + * /proc interface + */ + +static void snd_mixer_oss_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return); + int i; + + down(&mixer->reg_mutex); + for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) { + struct slot *p; + + if (! oss_mixer_names[i]) + continue; + p = (struct slot *)mixer->slots[i].private_data; + snd_iprintf(buffer, "%s ", oss_mixer_names[i]); + if (p && p->assigned) + snd_iprintf(buffer, "\"%s\" %d\n", + p->assigned->name, + p->assigned->index); + else + snd_iprintf(buffer, "\"\" 0\n"); + } + up(&mixer->reg_mutex); +} + +static void snd_mixer_oss_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return); + char line[128], str[32], idxstr[16], *cptr; + int ch, idx; + struct snd_mixer_oss_assign_table *tbl; + struct slot *slot; + + while (!snd_info_get_line(buffer, line, sizeof(line))) { + cptr = snd_info_get_str(str, line, sizeof(str)); + for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++) + if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0) + break; + if (ch >= SNDRV_OSS_MAX_MIXERS) { + snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str); + continue; + } + cptr = snd_info_get_str(str, cptr, sizeof(str)); + if (! *str) { + /* remove the entry */ + down(&mixer->reg_mutex); + mixer_slot_clear(&mixer->slots[ch]); + up(&mixer->reg_mutex); + continue; + } + snd_info_get_str(idxstr, cptr, sizeof(idxstr)); + idx = simple_strtoul(idxstr, NULL, 10); + if (idx >= 0x4000) { /* too big */ + snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx); + continue; + } + down(&mixer->reg_mutex); + slot = (struct slot *)mixer->slots[ch].private_data; + if (slot && slot->assigned && + slot->assigned->index == idx && ! strcmp(slot->assigned->name, str)) + /* not changed */ + goto __unlock; + tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); + if (! tbl) { + snd_printk(KERN_ERR "mixer_oss: no memory\n"); + goto __unlock; + } + tbl->oss_id = ch; + tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL); + if (! tbl->name) { + kfree(tbl); + goto __unlock; + } + tbl->index = idx; + if (snd_mixer_oss_build_input(mixer, tbl, 1) <= 0) { + kfree(tbl->name); + kfree(tbl); + } + __unlock: + up(&mixer->reg_mutex); + } +} + +static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_card_entry(mixer->card, "oss_mixer", + mixer->card->proc_root); + if (! entry) + return; + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 8192; + entry->c.text.read = snd_mixer_oss_proc_read; + entry->c.text.write_size = 8192; + entry->c.text.write = snd_mixer_oss_proc_write; + entry->private_data = mixer; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + mixer->proc_entry = entry; +} + +static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer) +{ + if (mixer->proc_entry) { + snd_info_unregister(mixer->proc_entry); + mixer->proc_entry = NULL; + } +} + +static void snd_mixer_oss_build(snd_mixer_oss_t *mixer) +{ + static struct snd_mixer_oss_assign_table table[] = { + { SOUND_MIXER_VOLUME, "Master", 0 }, + { SOUND_MIXER_BASS, "Tone Control - Bass", 0 }, + { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 }, + { SOUND_MIXER_SYNTH, "Synth", 0 }, + { SOUND_MIXER_PCM, "PCM", 0 }, + { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, + { SOUND_MIXER_LINE, "Line", 0 }, + { SOUND_MIXER_MIC, "Mic", 0 }, + { SOUND_MIXER_CD, "CD", 0 }, + { SOUND_MIXER_IMIX, "Monitor Mix", 0 }, + { SOUND_MIXER_ALTPCM, "PCM", 1 }, + { SOUND_MIXER_RECLEV, "-- nothing --", 0 }, + { SOUND_MIXER_IGAIN, "Capture", 0 }, + { SOUND_MIXER_OGAIN, "Playback", 0 }, + { SOUND_MIXER_LINE1, "Aux", 0 }, + { SOUND_MIXER_LINE2, "Aux", 1 }, + { SOUND_MIXER_LINE3, "Aux", 2 }, + { SOUND_MIXER_DIGITAL1, "Digital", 0 }, + { SOUND_MIXER_DIGITAL2, "Digital", 1 }, + { SOUND_MIXER_DIGITAL3, "Digital", 2 }, + { SOUND_MIXER_PHONEIN, "Phone", 0 }, + { SOUND_MIXER_PHONEOUT, "Phone", 1 }, + { SOUND_MIXER_VIDEO, "Video", 0 }, + { SOUND_MIXER_RADIO, "Radio", 0 }, + { SOUND_MIXER_MONITOR, "Monitor", 0 } + }; + static struct snd_mixer_oss_assign_table fm_table = { + SOUND_MIXER_SYNTH, "FM", 0 + }; + unsigned int idx; + + for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++) + snd_mixer_oss_build_input(mixer, &table[idx], 0); + if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL) + snd_mixer_oss_build_input(mixer, &fm_table, 0); + if (mixer->mask_recsrc) { + mixer->get_recsrc = snd_mixer_oss_get_recsrc2; + mixer->put_recsrc = snd_mixer_oss_put_recsrc2; + } +} + +/* + * + */ + +static int snd_mixer_oss_free1(void *private) +{ + snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, private, return -ENXIO); + snd_card_t * card; + int idx; + + snd_assert(mixer != NULL, return -ENXIO); + card = mixer->card; + snd_assert(mixer == card->mixer_oss, return -ENXIO); + card->mixer_oss = NULL; + for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) { + snd_mixer_oss_slot_t *chn = &mixer->slots[idx]; + if (chn->private_free) + chn->private_free(chn); + } + snd_magic_kfree(mixer); + return 0; +} + +static int snd_mixer_oss_notify_handler(snd_card_t * card, int cmd) +{ + snd_mixer_oss_t *mixer; + + if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) { + char name[128]; + int idx, err; + + mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL); + if (mixer == NULL) + return -ENOMEM; + init_MUTEX(&mixer->reg_mutex); + sprintf(name, "mixer%i%i", card->number, 0); + if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, + card, 0, + &snd_mixer_oss_reg, + name)) < 0) { + snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0); + snd_magic_kfree(mixer); + return err; + } + mixer->oss_dev_alloc = 1; + mixer->card = card; + if (*card->mixername) { + strncpy(mixer->name, card->mixername, sizeof(mixer->name) - 1); + mixer->name[sizeof(mixer->name)-1] = 0; + } else + strcpy(mixer->name, name); +#ifdef SNDRV_OSS_INFO_DEV_MIXERS + snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, + card->number, + mixer->name); +#endif + for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) + mixer->slots[idx].number = idx; + card->mixer_oss = mixer; + snd_mixer_oss_build(mixer); + snd_mixer_oss_proc_init(mixer); + } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) { + mixer = card->mixer_oss; + if (mixer == NULL || !mixer->oss_dev_alloc) + return 0; + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); + mixer->oss_dev_alloc = 0; + } else { /* free */ + mixer = card->mixer_oss; + if (mixer == NULL) + return 0; +#ifdef SNDRV_OSS_INFO_DEV_MIXERS + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); +#endif + if (mixer->oss_dev_alloc) + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); + snd_mixer_oss_proc_done(mixer); + return snd_mixer_oss_free1(mixer); + } + return 0; +} + +static int __init alsa_mixer_oss_init(void) +{ + int idx; + + snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler; + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (snd_cards[idx]) + snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER); + } + return 0; +} + +static void __exit alsa_mixer_oss_exit(void) +{ + int idx; + + snd_mixer_oss_notify_callback = NULL; + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (snd_cards[idx]) + snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE); + } +} + +module_init(alsa_mixer_oss_init) +module_exit(alsa_mixer_oss_exit) + +EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); diff -urN linux-2.4.21-rc1.orig/sound/core/oss/mulaw.c linux/sound/core/oss/mulaw.c --- linux-2.4.21-rc1.orig/sound/core/oss/mulaw.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/mulaw.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,307 @@ +/* + * Mu-Law conversion Plug-In Interface + * Copyright (c) 1999 by Jaroslav Kysela + * Uros Bizjak + * + * Based on reference implementation by Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of u-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + if (pcm_val > 0x7FFF) + pcm_val = 0x7FFF; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return uval ^ mask; +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static int ulaw2linear(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* + * Basic Mu-Law plugin + */ + +typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames); + +typedef struct mulaw_private_data { + mulaw_f func; + int conv; +} mulaw_t; + +static void mulaw_decode(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef PUT_S16_LABELS + mulaw_t *data = (mulaw_t *)plugin->extra_data; + void *put = put_s16_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + signed short sample = ulaw2linear(*src); + goto *put; +#define PUT_S16_END after +#include "plugin_ops.h" +#undef PUT_S16_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static void mulaw_encode(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define GET_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS + mulaw_t *data = (mulaw_t *)plugin->extra_data; + void *get = get_s16_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + signed short sample = 0; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + goto *get; +#define GET_S16_END after +#include "plugin_ops.h" +#undef GET_S16_END + after: + *dst = linear2ulaw(sample); + src += src_step; + dst += dst_step; + } + } +} + +static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + mulaw_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + data = (mulaw_t *)plugin->extra_data; + data->func(plugin, src_channels, dst_channels, frames); + return frames; +} + +int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + mulaw_t *data; + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_format_t *format; + mulaw_f func; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + + if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) { + format = src_format; + func = mulaw_encode; + } + else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) { + format = dst_format; + func = mulaw_decode; + } + else { + snd_BUG(); + return -EINVAL; + } + snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion", + src_format, dst_format, + sizeof(mulaw_t), &plugin); + if (err < 0) + return err; + data = (mulaw_t*)plugin->extra_data; + data->func = func; + data->conv = getput_index(format->format); + plugin->transfer = mulaw_transfer; + *r_plugin = plugin; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/oss/pcm_oss.c linux/sound/core/oss/pcm_oss.c --- linux-2.4.21-rc1.orig/sound/core/oss/pcm_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/pcm_oss.c 2003-03-12 05:14:03.000000000 -0700 @@ -0,0 +1,2276 @@ +/* + * Digital Audio (PCM) abstract layer / OSS compatible + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +#define PLUGIN_DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcm_plugin.h" +#include +#include +#include + +static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +static int nonblock_open; + +MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); +MODULE_DESCRIPTION("PCM OSS emulation for ALSA."); +MODULE_LICENSE("GPL"); +MODULE_PARM(dsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device."); +MODULE_PARM_SYNTAX(dsp_map, "default:0,skill:advanced"); +MODULE_PARM(adsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device."); +MODULE_PARM_SYNTAX(adsp_map, "default:1,skill:advanced"); +MODULE_PARM(nonblock_open, "i"); +MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); +MODULE_PARM_SYNTAX(nonblock_open, "default:0,skill:advanced"); + +extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg); +static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file); +static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file); +static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file); + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_plugin_t *plugin, *next; + + plugin = runtime->oss.plugin_first; + while (plugin) { + next = plugin->next; + snd_pcm_plugin_free(plugin); + plugin = next; + } + runtime->oss.plugin_first = runtime->oss.plugin_last = NULL; + return 0; +} + +int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin) +{ + snd_pcm_runtime_t *runtime = plugin->plug->runtime; + plugin->next = runtime->oss.plugin_first; + plugin->prev = NULL; + if (runtime->oss.plugin_first) { + runtime->oss.plugin_first->prev = plugin; + runtime->oss.plugin_first = plugin; + } else { + runtime->oss.plugin_last = + runtime->oss.plugin_first = plugin; + } + return 0; +} + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin) +{ + snd_pcm_runtime_t *runtime = plugin->plug->runtime; + plugin->next = NULL; + plugin->prev = runtime->oss.plugin_last; + if (runtime->oss.plugin_last) { + runtime->oss.plugin_last->next = plugin; + runtime->oss.plugin_last = plugin; + } else { + runtime->oss.plugin_last = + runtime->oss.plugin_first = plugin; + } + return 0; +} + +static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->period_size == runtime->oss.period_bytes) + return frames; + if (runtime->period_size < runtime->oss.period_bytes) + return frames * (runtime->oss.period_bytes / runtime->period_size); + return frames / (runtime->period_size / runtime->oss.period_bytes); +} + +static int snd_pcm_oss_format_from(int format) +{ + switch (format) { + case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; + case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW; + case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM; + case AFMT_U8: return SNDRV_PCM_FORMAT_U8; + case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE; + case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE; + case AFMT_S8: return SNDRV_PCM_FORMAT_S8; + case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; + case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; + case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; + default: return SNDRV_PCM_FORMAT_U8; + } +} + +static int snd_pcm_oss_format_to(int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; + case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW; + case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; + case SNDRV_PCM_FORMAT_U8: return AFMT_U8; + case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE; + case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE; + case SNDRV_PCM_FORMAT_S8: return AFMT_S8; + case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; + case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; + case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; + default: return -EINVAL; + } +} + +static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *oss_params, + snd_pcm_hw_params_t *slave_params) +{ + size_t s; + size_t oss_buffer_size, oss_period_size, oss_periods; + size_t min_period_size, max_period_size; + snd_pcm_runtime_t *runtime = substream->runtime; + size_t oss_frame_size; + + oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) * + params_channels(oss_params) / 8; + + oss_buffer_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0)) * oss_frame_size; + oss_buffer_size = 1 << ld2(oss_buffer_size); + if (atomic_read(&runtime->mmap_count)) { + if (oss_buffer_size > runtime->oss.mmap_bytes) + oss_buffer_size = runtime->oss.mmap_bytes; + } + + if (substream->oss.setup && + substream->oss.setup->period_size > 16) + oss_period_size = substream->oss.setup->period_size; + else if (runtime->oss.fragshift) { + oss_period_size = 1 << runtime->oss.fragshift; + if (oss_period_size > oss_buffer_size / 2) + oss_period_size = oss_buffer_size / 2; + } else { + int sd; + size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8; + + oss_period_size = oss_buffer_size; + do { + oss_period_size /= 2; + } while (oss_period_size > bytes_per_sec); + if (runtime->oss.subdivision == 0) { + sd = 4; + if (oss_period_size / sd > 4096) + sd *= 2; + if (oss_period_size / sd < 4096) + sd = 1; + } else + sd = runtime->oss.subdivision; + oss_period_size /= sd; + if (oss_period_size < 16) + oss_period_size = 16; + } + + min_period_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); + min_period_size *= oss_frame_size; + min_period_size = 1 << (ld2(min_period_size - 1) + 1); + if (oss_period_size < min_period_size) + oss_period_size = min_period_size; + + max_period_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); + max_period_size *= oss_frame_size; + max_period_size = 1 << ld2(max_period_size); + if (oss_period_size > max_period_size) + oss_period_size = max_period_size; + + oss_periods = oss_buffer_size / oss_period_size; + + if (substream->oss.setup) { + if (substream->oss.setup->periods > 1) + oss_periods = substream->oss.setup->periods; + } + + s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); + if (runtime->oss.maxfrags && s > runtime->oss.maxfrags) + s = runtime->oss.maxfrags; + if (oss_periods > s) + oss_periods = s; + + s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); + if (s < 2) + s = 2; + if (oss_periods < s) + oss_periods = s; + + while (oss_period_size * oss_periods > oss_buffer_size) + oss_period_size /= 2; + + snd_assert(oss_period_size >= 16, return -EINVAL); + runtime->oss.period_bytes = oss_period_size; + runtime->oss.periods = oss_periods; + return 0; +} + +static int choose_rate(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, unsigned int best_rate) +{ + snd_interval_t *it; + snd_pcm_hw_params_t *save; + unsigned int rate, prev; + + save = kmalloc(sizeof(*save), GFP_KERNEL); + if (save == NULL) + return -ENOMEM; + *save = *params; + it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE); + + /* try multiples of the best rate */ + rate = best_rate; + for (;;) { + if (it->max < rate || (it->max == rate && it->openmax)) + break; + if (it->min < rate || (it->min == rate && !it->openmin)) { + int ret; + ret = snd_pcm_hw_param_set(substream, params, + SNDRV_PCM_HW_PARAM_RATE, + rate, 0); + if (ret == (int)rate) { + kfree(save); + return rate; + } + *params = *save; + } + prev = rate; + rate += best_rate; + if (rate <= prev) + break; + } + + /* not found, use the nearest rate */ + kfree(save); + return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, 0); +} + +static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hw_params_t *params, *sparams; + snd_pcm_sw_params_t *sw_params; + ssize_t oss_buffer_size, oss_period_size; + size_t oss_frame_size; + int err; + int direct; + int format, sformat, n; + snd_mask_t sformat_mask; + snd_mask_t mask; + + sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL); + params = kmalloc(sizeof(*params), GFP_KERNEL); + sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); + if (!sw_params || !params || !sparams) { + snd_printd("No memory\n"); + err = -ENOMEM; + goto failure; + } + + if (atomic_read(&runtime->mmap_count)) { + direct = 1; + } else { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + direct = (setup != NULL && setup->direct); + } + + _snd_pcm_hw_params_any(sparams); + _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS); + _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); + snd_mask_none(&mask); + if (atomic_read(&runtime->mmap_count)) + snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + else { + snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); + if (!direct) + snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + } + err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); + if (err < 0) { + snd_printd("No usable accesses\n"); + err = -EINVAL; + goto failure; + } + choose_rate(substream, sparams, runtime->oss.rate); + snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); + + format = snd_pcm_oss_format_from(runtime->oss.format); + + sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT); + if (direct) + sformat = format; + else + sformat = snd_pcm_plug_slave_format(format, &sformat_mask); + + if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) { + for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { + if (snd_mask_test(&sformat_mask, sformat) && + snd_pcm_oss_format_to(sformat) >= 0) + break; + } + if (sformat > SNDRV_PCM_FORMAT_LAST) { + snd_printd("Cannot find a format!!!\n"); + err = -EINVAL; + goto failure; + } + } + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); + snd_assert(err >= 0, goto failure); + + if (direct) { + memcpy(params, sparams, sizeof(*params)); + } else { + _snd_pcm_hw_params_any(params); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_oss_format_from(runtime->oss.format), 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, + runtime->oss.channels, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, + runtime->oss.rate, 0); + pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n", + params_access(params), params_format(params), + params_channels(params), params_rate(params)); + } + pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n", + params_access(sparams), params_format(sparams), + params_channels(sparams), params_rate(sparams)); + + oss_frame_size = snd_pcm_format_physical_width(params_format(params)) * + params_channels(params) / 8; + + snd_pcm_oss_plugin_clear(substream); + if (!direct) { + /* add necessary plugins */ + snd_pcm_oss_plugin_clear(substream); + if ((err = snd_pcm_plug_format_plugins(substream, + params, + sparams)) < 0) { + snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err); + snd_pcm_oss_plugin_clear(substream); + goto failure; + } + if (runtime->oss.plugin_first) { + snd_pcm_plugin_t *plugin; + if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) { + snd_printd("snd_pcm_plugin_build_io failed: %i\n", err); + snd_pcm_oss_plugin_clear(substream); + goto failure; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + err = snd_pcm_plugin_append(plugin); + } else { + err = snd_pcm_plugin_insert(plugin); + } + if (err < 0) { + snd_pcm_oss_plugin_clear(substream); + goto failure; + } + } + } + + err = snd_pcm_oss_period_size(substream, params, sparams); + if (err < 0) + goto failure; + + n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size); + err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, 0); + snd_assert(err >= 0, goto failure); + + err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS, + runtime->oss.periods, 0); + snd_assert(err >= 0, goto failure); + + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) { + snd_printd("HW_PARAMS failed: %i\n", err); + goto failure; + } + + memset(sw_params, 0, sizeof(*sw_params)); + if (runtime->oss.trigger) { + sw_params->start_threshold = 1; + } else { + sw_params->start_threshold = runtime->boundary; + } + if (atomic_read(&runtime->mmap_count)) + sw_params->stop_threshold = runtime->boundary; + else + sw_params->stop_threshold = runtime->buffer_size; + sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + sw_params->period_step = 1; + sw_params->sleep_min = 0; + sw_params->avail_min = runtime->period_size; + sw_params->xfer_align = 1; + sw_params->silence_threshold = 0; + sw_params->silence_size = 0; + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) { + snd_printd("SW_PARAMS failed: %i\n", err); + goto failure; + } + runtime->control->avail_min = runtime->period_size; + + runtime->oss.periods = params_periods(sparams); + oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams)); + snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure); + if (runtime->oss.plugin_first) { + err = snd_pcm_plug_alloc(substream, oss_period_size); + if (err < 0) + goto failure; + } + oss_period_size *= oss_frame_size; + + oss_buffer_size = oss_period_size * runtime->oss.periods; + snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure); + + runtime->oss.period_bytes = oss_period_size; + runtime->oss.buffer_bytes = oss_buffer_size; + + pdprintf("oss: period bytes = %i, buffer bytes = %i\n", + runtime->oss.period_bytes, + runtime->oss.buffer_bytes); + pdprintf("slave: period_size = %i, buffer_size = %i\n", + params_period_size(sparams), + params_buffer_size(sparams)); + + runtime->oss.format = snd_pcm_oss_format_to(params_format(params)); + runtime->oss.channels = params_channels(params); + runtime->oss.rate = params_rate(params); + + runtime->oss.params = 0; + runtime->oss.prepare = 1; + if (runtime->oss.buffer != NULL) + vfree(runtime->oss.buffer); + runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); + runtime->oss.buffer_used = 0; + if (runtime->dma_area) + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); + + err = 0; +failure: + if (sw_params) + kfree(sw_params); + if (params) + kfree(params); + if (sparams) + kfree(sparams); + return err; +} + +static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream) +{ + int idx, err; + snd_pcm_substream_t *asubstream = NULL, *substream; + + for (idx = 0; idx < 2; idx++) { + substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if (asubstream == NULL) + asubstream = substream; + if (substream->runtime->oss.params) { + err = snd_pcm_oss_change_params(substream); + if (err < 0) + return err; + } + } + snd_assert(asubstream != NULL, return -EIO); + if (r_substream) + *r_substream = asubstream; + return 0; +} + +static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream) +{ + int err; + snd_pcm_runtime_t *runtime = substream->runtime; + + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, 0); + if (err < 0) { + snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n"); + return err; + } + runtime->oss.prepare = 0; + runtime->oss.prev_hw_ptr_interrupt = 0; + + return 0; +} + +static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + int err; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (runtime->oss.params) { + err = snd_pcm_oss_change_params(substream); + if (err < 0) + return err; + } + if (runtime->oss.prepare) { + err = snd_pcm_oss_prepare(substream); + if (err < 0) + return err; + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_write(substream, ptr, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_write(substream, ptr, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + /* test, if we can't store new data, because the stream */ + /* has not been started */ + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) + return -EAGAIN; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + if (ret < 0) + break; + } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_read(substream, ptr, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_read(substream, ptr, frames); + } + if (ret == -EPIPE) { + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + if (ret < 0) + break; + } + continue; + } + if (ret != -ESTRPIPE) + break; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_writev(substream, bufs, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_writev(substream, bufs, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + + /* test, if we can't store new data, because the stream */ + /* has not been started */ + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) + return -EAGAIN; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + if (ret < 0) + break; + } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_readv(substream, bufs, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_readv(substream, bufs, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + } + return ret; +} + +static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t frames, frames1; + if (runtime->oss.plugin_first) { + snd_pcm_plugin_channel_t *channels; + size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; + if (!in_kernel) { + if (copy_from_user(runtime->oss.buffer, buf, bytes)) + return -EFAULT; + buf = runtime->oss.buffer; + } + frames = bytes / oss_frame_bytes; + frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels); + if (frames1 < 0) + return frames1; + frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1); + if (frames1 <= 0) + return frames1; + bytes = frames1 * oss_frame_bytes; + } else { + frames = bytes_to_frames(runtime, bytes); + frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel); + if (frames1 <= 0) + return frames1; + bytes = frames_to_bytes(runtime, frames1); + } + return bytes; +} + +static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char *buf, size_t bytes) +{ + size_t xfer = 0; + ssize_t tmp; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (atomic_read(&runtime->mmap_count)) + return -ENXIO; + + if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) + return tmp; + while (bytes > 0) { + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + tmp = bytes; + if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) + tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; + if (tmp > 0) { + if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) + return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; + } + runtime->oss.buffer_used += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + if (runtime->oss.buffer_used == runtime->oss.period_bytes) { + tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (tmp <= 0) + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; + runtime->oss.bytes += tmp; + runtime->oss.buffer_used = 0; + } + } else { + tmp = snd_pcm_oss_write2(substream, (char *)buf, runtime->oss.period_bytes, 0); + if (tmp <= 0) + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; + runtime->oss.bytes += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + } + } + return xfer; +} + +static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t frames, frames1; + char *final_dst = buf; + if (runtime->oss.plugin_first) { + snd_pcm_plugin_channel_t *channels; + size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; + if (!in_kernel) + buf = runtime->oss.buffer; + frames = bytes / oss_frame_bytes; + frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels); + if (frames1 < 0) + return frames1; + frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1); + if (frames1 <= 0) + return frames1; + bytes = frames1 * oss_frame_bytes; + if (!in_kernel && copy_to_user(final_dst, buf, bytes)) + return -EFAULT; + } else { + frames = bytes_to_frames(runtime, bytes); + frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel); + if (frames1 <= 0) + return frames1; + bytes = frames_to_bytes(runtime, frames1); + } + return bytes; +} + +static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char *buf, size_t bytes) +{ + size_t xfer = 0; + ssize_t tmp; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (atomic_read(&runtime->mmap_count)) + return -ENXIO; + + if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) + return tmp; + while (bytes > 0) { + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + if (runtime->oss.buffer_used == 0) { + tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (tmp <= 0) + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; + runtime->oss.bytes += tmp; + runtime->oss.buffer_used = runtime->oss.period_bytes; + } + tmp = bytes; + if ((size_t) tmp > runtime->oss.buffer_used) + tmp = runtime->oss.buffer_used; + if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_bytes - runtime->oss.buffer_used), tmp)) + return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; + buf += tmp; + bytes -= tmp; + xfer += tmp; + runtime->oss.buffer_used -= tmp; + } else { + tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0); + if (tmp <= 0) + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; + runtime->oss.bytes += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + } + } + return xfer; +} + +static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream != NULL) { + snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + substream->runtime->oss.prepare = 1; + } + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream != NULL) { + snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + substream->runtime->oss.prepare = 1; + } + return 0; +} + +static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file) +{ + int err = 0; + unsigned int saved_f_flags; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + ssize_t result; + wait_queue_t wait; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream != NULL) { + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + + runtime = substream->runtime; + if (runtime->oss.buffer_used > 0) { + snd_pcm_format_set_silence(runtime->format, + runtime->oss.buffer + runtime->oss.buffer_used, + bytes_to_samples(runtime, runtime->oss.period_bytes - runtime->oss.buffer_used)); + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + result = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (result > 0) { + runtime->oss.buffer_used = 0; + break; + } + if (result != 0 && result != -EAGAIN) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + result = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (result < 0) + return result; + } + saved_f_flags = substream->ffile->f_flags; + substream->ffile->f_flags &= ~O_NONBLOCK; + err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + substream->ffile->f_flags = saved_f_flags; + if (err < 0) + return err; + runtime->oss.prepare = 1; + } + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream != NULL) { + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + if (err < 0) + return err; + runtime->oss.buffer_used = 0; + runtime->oss.prepare = 1; + } + return 0; +} + +static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate) +{ + int idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (rate < 1000) + rate = 1000; + else if (rate > 48000) + rate = 48000; + if (runtime->oss.rate != rate) { + runtime->oss.params = 1; + runtime->oss.rate = rate; + } + } + return snd_pcm_oss_get_rate(pcm_oss_file); +} + +static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.rate; +} + +static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, unsigned int channels) +{ + int idx; + if (channels < 1) + channels = 1; + if (channels > 128) + return -EINVAL; + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (runtime->oss.channels != channels) { + runtime->oss.params = 1; + runtime->oss.channels = channels; + } + } + return snd_pcm_oss_get_channels(pcm_oss_file); +} + +static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.channels; +} + +static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.period_bytes; +} + +static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + int direct; + snd_pcm_hw_params_t params; + unsigned int formats = 0; + snd_mask_t format_mask; + int fmt; + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + if (atomic_read(&substream->runtime->mmap_count)) { + direct = 1; + } else { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + direct = (setup != NULL && setup->direct); + } + if (!direct) + return AFMT_MU_LAW | AFMT_U8 | + AFMT_S16_LE | AFMT_S16_BE | + AFMT_S8 | AFMT_U16_LE | + AFMT_U16_BE; + _snd_pcm_hw_params_any(¶ms); + err = snd_pcm_hw_refine(substream, ¶ms); + snd_assert(err >= 0, return err); + format_mask = *hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT); + for (fmt = 0; fmt < 32; ++fmt) { + if (snd_mask_test(&format_mask, fmt)) { + int f = snd_pcm_oss_format_to(fmt); + if (f >= 0) + formats |= f; + } + } + return formats; +} + +static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format) +{ + int formats, idx; + + if (format != AFMT_QUERY) { + formats = snd_pcm_oss_get_formats(pcm_oss_file); + if (!(formats & format)) + format = AFMT_U8; + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (runtime->oss.format != format) { + runtime->oss.params = 1; + runtime->oss.format = format; + } + } + } + return snd_pcm_oss_get_format(pcm_oss_file); +} + +static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.format; +} + +static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide) +{ + snd_pcm_runtime_t *runtime; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (subdivide == 0) { + subdivide = runtime->oss.subdivision; + if (subdivide == 0) + subdivide = 1; + return subdivide; + } + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; + if (subdivide != 1 && subdivide != 2 && subdivide != 4 && + subdivide != 8 && subdivide != 16) + return -EINVAL; + runtime->oss.subdivision = subdivide; + runtime->oss.params = 1; + return subdivide; +} + +static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide) +{ + int err = -EINVAL, idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0) + return err; + } + return err; +} + +static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val) +{ + snd_pcm_runtime_t *runtime; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; + runtime->oss.fragshift = val & 0xffff; + runtime->oss.maxfrags = (val >> 16) & 0xffff; + if (runtime->oss.fragshift < 4) /* < 16 */ + runtime->oss.fragshift = 4; + if (runtime->oss.maxfrags < 2) + runtime->oss.maxfrags = 2; + runtime->oss.params = 1; + return 0; +} + +static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val) +{ + int err = -EINVAL, idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0) + return err; + } + return err; +} + +static int snd_pcm_oss_nonblock(struct file * file) +{ + file->f_flags |= O_NONBLOCK; + return 0; +} + +static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res) +{ + + if (substream == NULL) { + res &= ~DSP_CAP_DUPLEX; + return res; + } +#ifdef DSP_CAP_MULTI + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->pstr->substream_count > 1) + res |= DSP_CAP_MULTI; +#endif + /* DSP_CAP_REALTIME is set all times: */ + /* all ALSA drivers can return actual pointer in ring buffer */ +#if defined(DSP_CAP_REALTIME) && 0 + { + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH)) + res &= ~DSP_CAP_REALTIME; + } +#endif + return res; +} + +static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file) +{ + int result, idx; + + result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME; + for (idx = 0; idx < 2; idx++) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + result = snd_pcm_oss_get_caps1(substream, result); + } + result |= 0x0001; /* revision - same as SB AWE 64 */ + return result; +} + +static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr; + appl_ptr = runtime->hw_ptr_interrupt + runtime->buffer_size; + appl_ptr %= runtime->boundary; + runtime->control->appl_ptr = appl_ptr; +} + +static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger) +{ + snd_pcm_runtime_t *runtime; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + int err, cmd; + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + + if (psubstream) { + if ((err = snd_pcm_oss_make_ready(psubstream)) < 0) + return err; + if (atomic_read(&psubstream->runtime->mmap_count)) + snd_pcm_oss_simulate_fill(psubstream); + } + if (csubstream) { + if ((err = snd_pcm_oss_make_ready(csubstream)) < 0) + return err; + } + if (psubstream) { + runtime = psubstream->runtime; + if (trigger & PCM_ENABLE_OUTPUT) { + if (runtime->oss.trigger) + goto _skip1; + runtime->oss.trigger = 1; + runtime->start_threshold = 1; + cmd = SNDRV_PCM_IOCTL_START; + } else { + if (!runtime->oss.trigger) + goto _skip1; + runtime->oss.trigger = 0; + runtime->start_threshold = runtime->boundary; + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } + err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, 0); + if (err < 0) + return err; + } + _skip1: + if (csubstream) { + runtime = csubstream->runtime; + if (trigger & PCM_ENABLE_INPUT) { + if (runtime->oss.trigger) + goto _skip2; + runtime->oss.trigger = 1; + runtime->start_threshold = 1; + cmd = SNDRV_PCM_IOCTL_START; + } else { + if (!runtime->oss.trigger) + goto _skip2; + runtime->oss.trigger = 0; + runtime->start_threshold = runtime->boundary; + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } + err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, 0); + if (err < 0) + return err; + } + _skip2: + return 0; +} + +static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + int result = 0; + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger) + result |= PCM_ENABLE_OUTPUT; + if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger) + result |= PCM_ENABLE_INPUT; + return result; +} + +static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t delay; + int err; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + return -EINVAL; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + if (runtime->oss.params || runtime->oss.prepare) + return 0; + err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay); + if (err == -EPIPE) + delay = 0; /* hack for broken OSS applications */ + else if (err < 0) + return err; + return snd_pcm_oss_bytes(substream, delay); +} + +static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info * _info) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_status_t status; + struct count_info info; + int err; + + if (_info == NULL) + return -EFAULT; + substream = pcm_oss_file->streams[stream]; + if (substream == NULL) + return -EINVAL; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + if (runtime->oss.params || runtime->oss.prepare) { + memset(&info, 0, sizeof(info)); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; + } + memset(&status, 0, sizeof(status)); + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status); + if (err < 0) + return err; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + info.bytes = runtime->oss.bytes - snd_pcm_oss_bytes(substream, runtime->buffer_size - status.avail); + } else { + info.bytes = runtime->oss.bytes + snd_pcm_oss_bytes(substream, status.avail); + } + info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); + if (atomic_read(&runtime->mmap_count)) { + snd_pcm_sframes_t n; + n = runtime->hw_ptr_interrupt - runtime->oss.prev_hw_ptr_interrupt; + if (n < 0) + n += runtime->boundary; + info.blocks = n / runtime->period_size; + runtime->oss.prev_hw_ptr_interrupt = runtime->hw_ptr_interrupt; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_oss_simulate_fill(substream); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + info.blocks = (runtime->buffer_size - status.avail) / runtime->period_size; + else + info.blocks = status.avail / runtime->period_size; + } + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info *_info) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_status_t status; + struct audio_buf_info info; + int err; + + if (_info == NULL) + return -EFAULT; + substream = pcm_oss_file->streams[stream]; + if (substream == NULL) + return -EINVAL; + runtime = substream->runtime; + + if (runtime->oss.params && + (err = snd_pcm_oss_change_params(substream)) < 0) + return err; + + info.fragsize = runtime->oss.period_bytes; + info.fragstotal = runtime->periods; + memset(&status, 0, sizeof(status)); + if (runtime->oss.prepare) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + info.bytes = runtime->oss.period_bytes * runtime->periods; + info.fragments = runtime->periods; + } else { + info.bytes = 0; + info.fragments = 0; + } + } else { + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status); + if (err < 0) + return err; + info.bytes = snd_pcm_oss_bytes(substream, status.avail); + info.fragments = status.avail / runtime->period_size; + } + +#if 0 + /* very experimental stuff to get Quake2 working */ + runtime->oss.period = (info.periods - 1) << 16; + for (tmp = info.fragsize; tmp > 1; tmp >>= 1) + runtime->oss.period++; + runtime->oss.subdivision = 1; /* disable SUBDIVIDE */ +#endif + // printk("space: bytes = %i, periods = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.periods, info.fragstotal, info.fragsize); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc * _info) +{ + // it won't be probably implemented + // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n"); + return -EINVAL; +} + +static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name) +{ + const char *ptr, *ptrl; + snd_pcm_oss_setup_t *setup; + + down(&pcm->streams[stream].oss.setup_mutex); + for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, task_name)) { + up(&pcm->streams[stream].oss.setup_mutex); + return setup; + } + } + ptr = ptrl = task_name; + while (*ptr) { + if (*ptr == '/') + ptrl = ptr + 1; + ptr++; + } + if (ptrl == task_name) { + goto __not_found; + return NULL; + } + for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, ptrl)) { + up(&pcm->streams[stream].oss.setup_mutex); + return setup; + } + } + __not_found: + up(&pcm->streams[stream].oss.setup_mutex); + return NULL; +} + +static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream, + snd_pcm_oss_setup_t *setup, + int minor) +{ + snd_pcm_runtime_t *runtime; + + substream->oss.oss = 1; + substream->oss.setup = setup; + runtime = substream->runtime; + runtime->oss.params = 1; + runtime->oss.trigger = 1; + runtime->oss.rate = 8000; + switch (SNDRV_MINOR_OSS_DEVICE(minor)) { + case SNDRV_MINOR_OSS_PCM_8: + runtime->oss.format = AFMT_U8; + break; + case SNDRV_MINOR_OSS_PCM_16: + runtime->oss.format = AFMT_S16_LE; + break; + default: + runtime->oss.format = AFMT_MU_LAW; + } + runtime->oss.channels = 1; + runtime->oss.fragshift = 0; + runtime->oss.maxfrags = 0; + runtime->oss.subdivision = 0; +} + +static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + runtime = substream->runtime; + if (runtime->oss.buffer) + vfree(runtime->oss.buffer); + snd_pcm_oss_plugin_clear(substream); + substream->oss.file = NULL; + substream->oss.oss = 0; +} + +static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file) +{ + int cidx; + snd_assert(pcm_oss_file != NULL, return -ENXIO); + for (cidx = 0; cidx < 2; ++cidx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + + spin_lock_irq(&runtime->lock); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + spin_unlock_irq(&runtime->lock); + if (substream->open_flag) { + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + substream->ops->close(substream); + substream->open_flag = 0; + } + substream->ffile = NULL; + snd_pcm_oss_release_substream(substream); + snd_pcm_release_substream(substream); + } + snd_magic_kfree(pcm_oss_file); + return 0; +} + +static int snd_pcm_oss_open_file(struct file *file, + snd_pcm_t *pcm, + snd_pcm_oss_file_t **rpcm_oss_file, + int minor, + snd_pcm_oss_setup_t *psetup, + snd_pcm_oss_setup_t *csetup) +{ + int err = 0; + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + unsigned int f_mode = file->f_mode; + + snd_assert(rpcm_oss_file != NULL, return -EINVAL); + *rpcm_oss_file = NULL; + + pcm_oss_file = snd_magic_kcalloc(snd_pcm_oss_file_t, 0, GFP_KERNEL); + if (pcm_oss_file == NULL) + return -ENOMEM; + + if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) && + (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)) + f_mode = FMODE_WRITE; + if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) { + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &psubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream; + } + if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) { + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, + &csubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream; + } + + if (psubstream == NULL && csubstream == NULL) { + snd_pcm_oss_release_file(pcm_oss_file); + return -EINVAL; + } + if (psubstream != NULL) { + psubstream->oss.file = pcm_oss_file; + err = snd_pcm_hw_constraints_init(psubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_init failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + if ((err = psubstream->ops->open(psubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + psubstream->open_flag = 1; + err = snd_pcm_hw_constraints_complete(psubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_complete failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + psubstream->ffile = file; + snd_pcm_oss_init_substream(psubstream, psetup, minor); + } + if (csubstream != NULL) { + csubstream->oss.file = pcm_oss_file; + err = snd_pcm_hw_constraints_init(csubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_init failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + if ((err = csubstream->ops->open(csubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + csubstream->open_flag = 1; + err = snd_pcm_hw_constraints_complete(csubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_complete failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + csubstream->ffile = file; + snd_pcm_oss_init_substream(csubstream, csetup, minor); + } + + file->private_data = pcm_oss_file; + *rpcm_oss_file = pcm_oss_file; + return 0; +} + + +static int snd_pcm_oss_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + int cardnum = SNDRV_MINOR_OSS_CARD(minor); + int device; + int err; + char task_name[32]; + snd_pcm_t *pcm; + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL; + int nonblock; + wait_queue_t wait; + + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ? + adsp_map[cardnum] : dsp_map[cardnum]; + + pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device]; + if (pcm == NULL) { + err = -ENODEV; + goto __error1; + } + err = snd_card_file_add(pcm->card, file); + if (err < 0) + goto __error1; + if (!try_module_get(pcm->card->module)) { + err = -EFAULT; + goto __error2; + } + if (snd_task_name(current, task_name, sizeof(task_name)) < 0) { + err = -EFAULT; + goto __error; + } + if (file->f_mode & FMODE_WRITE) + psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name); + if (file->f_mode & FMODE_READ) + csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name); + + nonblock = !!(file->f_flags & O_NONBLOCK); + if (psetup && !psetup->disable) { + if (psetup->nonblock) + nonblock = 1; + else if (psetup->block) + nonblock = 0; + } else if (csetup && !csetup->disable) { + if (csetup->nonblock) + nonblock = 1; + else if (csetup->block) + nonblock = 0; + } + if (!nonblock) + nonblock = nonblock_open; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&pcm->open_wait, &wait); + down(&pcm->open_mutex); + while (1) { + err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file, + minor, psetup, csetup); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (nonblock) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + up(&pcm->open_mutex); + schedule(); + down(&pcm->open_mutex); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pcm->open_wait, &wait); + up(&pcm->open_mutex); + if (err < 0) + goto __error; + return err; + + __error: + module_put(pcm->card->module); + __error2: + snd_card_file_remove(pcm->card, file); + __error1: + return err; +} + +static int snd_pcm_oss_release(struct inode *inode, struct file *file) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + snd_pcm_oss_file_t *pcm_oss_file; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + snd_assert(substream != NULL, return -ENXIO); + pcm = substream->pcm; + snd_pcm_oss_sync(pcm_oss_file); + down(&pcm->open_mutex); + snd_pcm_oss_release_file(pcm_oss_file); + up(&pcm->open_mutex); + wake_up(&pcm->open_wait); + module_put(pcm->card->module); + snd_card_file_remove(pcm->card, file); + return 0; +} + +static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_oss_file_t *pcm_oss_file; + int res; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + if (cmd == OSS_GETVERSION) + return put_user(SNDRV_OSS_VERSION, (int *)arg) ? -EFAULT : 0; +#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE)) + if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */ + snd_pcm_substream_t *substream; + int idx; + for (idx = 0; idx < 2; ++idx) { + substream = pcm_oss_file->streams[idx]; + if (substream != NULL) + break; + } + snd_assert(substream != NULL, return -ENXIO); + return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg); + } +#endif + if (((cmd >> 8) & 0xff) != 'P') + return -EINVAL; + switch (cmd) { + case SNDCTL_DSP_RESET: + return snd_pcm_oss_reset(pcm_oss_file); + case SNDCTL_DSP_SYNC: + return snd_pcm_oss_sync(pcm_oss_file); + case SNDCTL_DSP_SPEED: + if (get_user(res, (int *)arg)) + return -EFAULT; + if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_RATE: + res = snd_pcm_oss_get_rate(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_STEREO: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = res > 0 ? 2 : 1; + if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0) + return res; + return put_user(--res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETBLKSIZE: + res = snd_pcm_oss_get_block_size(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETFMT: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_format(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_BITS: + res = snd_pcm_oss_get_format(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_CHANNELS: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_channels(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_CHANNELS: + res = snd_pcm_oss_get_channels(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EIO; + case SNDCTL_DSP_POST: /* to do */ + return 0; + case SNDCTL_DSP_SUBDIVIDE: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_subdivide(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(res, (int *)arg)) + return -EFAULT; + return snd_pcm_oss_set_fragment(pcm_oss_file, res); + case SNDCTL_DSP_GETFMTS: + res = snd_pcm_oss_get_formats(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + return snd_pcm_oss_get_space(pcm_oss_file, + cmd == SNDCTL_DSP_GETISPACE ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct audio_buf_info *) arg); + case SNDCTL_DSP_NONBLOCK: + return snd_pcm_oss_nonblock(file); + case SNDCTL_DSP_GETCAPS: + res = snd_pcm_oss_get_caps(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETTRIGGER: + res = snd_pcm_oss_get_trigger(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETTRIGGER: + if (get_user(res, (int *)arg)) + return -EFAULT; + return snd_pcm_oss_set_trigger(pcm_oss_file, res); + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + return snd_pcm_oss_get_ptr(pcm_oss_file, + cmd == SNDCTL_DSP_GETIPTR ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct count_info *) arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return snd_pcm_oss_get_mapbuf(pcm_oss_file, + cmd == SNDCTL_DSP_MAPINBUF ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct buffmem_desc *) arg); + case SNDCTL_DSP_SETSYNCRO: + /* stop DMA now.. */ + return 0; + case SNDCTL_DSP_SETDUPLEX: + if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX) + return 0; + return -EIO; + case SNDCTL_DSP_GETODELAY: + res = snd_pcm_oss_get_odelay(pcm_oss_file); + if (res < 0) { + /* it's for sure, some broken apps don't check for error codes */ + put_user(0, (int *)arg); + return res; + } + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_PROFILE: + return 0; /* silently ignore */ + default: + snd_printd("pcm_oss: unknown command = 0x%x\n", cmd); + } + return -EINVAL; +} + +static ssize_t snd_pcm_oss_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream == NULL) + return -ENXIO; + return snd_pcm_oss_read1(substream, buf, count); +} + +static ssize_t snd_pcm_oss_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream; + long result; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + return -ENXIO; + up(&file->f_dentry->d_inode->i_sem); + result = snd_pcm_oss_write1(substream, buf, count); + down(&file->f_dentry->d_inode->i_sem); + return result; +} + +static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (atomic_read(&runtime->mmap_count)) + return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + else + return snd_pcm_playback_ready(substream); +} + +static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (atomic_read(&runtime->mmap_count)) + return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + else + return snd_pcm_capture_ready(substream); +} + +static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) +{ + snd_pcm_oss_file_t *pcm_oss_file; + unsigned int mask; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return 0); + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + + mask = 0; + if (psubstream != NULL) { + snd_pcm_runtime_t *runtime = psubstream->runtime; + poll_wait(file, &runtime->sleep, wait); + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_DRAINING && + (runtime->status->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_playback_ready(psubstream))) + mask |= POLLOUT | POLLWRNORM; + spin_unlock_irq(&runtime->lock); + } + if (csubstream != NULL) { + snd_pcm_runtime_t *runtime = csubstream->runtime; + poll_wait(file, &runtime->sleep, wait); + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_capture_ready(csubstream)) + mask |= POLLIN | POLLRDNORM; + spin_unlock_irq(&runtime->lock); + } + + return mask; +} + +static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream = NULL; + snd_pcm_runtime_t *runtime; + int err; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + switch ((area->vm_flags & (VM_READ | VM_WRITE))) { + case VM_READ | VM_WRITE: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) + break; + /* Fall through */ + case VM_READ: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + break; + case VM_WRITE: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + break; + default: + return -EINVAL; + } + /* set VM_READ access as well to fix memset() routines that do + reads before writes (to improve performance) */ + area->vm_flags |= VM_READ; + if (substream == NULL) + return -ENXIO; + runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID)) + return -EIO; + if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED) + runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + else + return -EIO; + + if (runtime->oss.params) { + if ((err = snd_pcm_oss_change_params(substream)) < 0) + return err; + } + if (runtime->oss.plugin_first != NULL) + return -EIO; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + if (area->vm_pgoff != 0) +#else + if (area->vm_offset != 0) +#endif + return -EINVAL; + + err = snd_pcm_mmap_data(substream, file, area); + if (err < 0) + return err; + runtime->oss.mmap_bytes = area->vm_end - area->vm_start; + /* In mmap mode we never stop */ + runtime->stop_threshold = runtime->boundary; + + return 0; +} + +/* + * /proc interface + */ + +static void snd_pcm_oss_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; + snd_pcm_oss_setup_t *setup = pstr->oss.setup_list; + down(&pstr->oss.setup_mutex); + while (setup) { + snd_iprintf(buffer, "%s %u %u%s%s%s%s\n", + setup->task_name, + setup->periods, + setup->period_size, + setup->disable ? " disable" : "", + setup->direct ? " direct" : "", + setup->block ? " block" : "", + setup->nonblock ? " non-block" : ""); + setup = setup->next; + } + up(&pstr->oss.setup_mutex); +} + +static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr) +{ + unsigned int idx; + snd_pcm_substream_t *substream; + snd_pcm_oss_setup_t *setup, *setupn; + + for (idx = 0, substream = pstr->substream; + idx < pstr->substream_count; idx++, substream = substream->next) + substream->oss.setup = NULL; + for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL; + setup; setup = setupn) { + setupn = setup->next; + kfree(setup->task_name); + kfree(setup); + } + pstr->oss.setup_list = NULL; +} + +static void snd_pcm_oss_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; + char line[512], str[32], task_name[32], *ptr; + int idx1; + snd_pcm_oss_setup_t *setup, *setup1, template; + + while (!snd_info_get_line(buffer, line, sizeof(line))) { + down(&pstr->oss.setup_mutex); + memset(&template, 0, sizeof(template)); + ptr = snd_info_get_str(task_name, line, sizeof(task_name)); + if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) { + snd_pcm_oss_proc_free_setup_list(pstr); + up(&pstr->oss.setup_mutex); + continue; + } + for (setup = pstr->oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, task_name)) { + template = *setup; + break; + } + } + ptr = snd_info_get_str(str, ptr, sizeof(str)); + template.periods = simple_strtoul(str, NULL, 10); + ptr = snd_info_get_str(str, ptr, sizeof(str)); + template.period_size = simple_strtoul(str, NULL, 10); + for (idx1 = 31; idx1 >= 0; idx1--) + if (template.period_size & (1 << idx1)) + break; + for (idx1--; idx1 >= 0; idx1--) + template.period_size &= ~(1 << idx1); + do { + ptr = snd_info_get_str(str, ptr, sizeof(str)); + if (!strcmp(str, "disable")) { + template.disable = 1; + } else if (!strcmp(str, "direct")) { + template.direct = 1; + } else if (!strcmp(str, "block")) { + template.block = 1; + } else if (!strcmp(str, "non-block")) { + template.nonblock = 1; + } + } while (*str); + if (setup == NULL) { + setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL); + if (setup) { + if (pstr->oss.setup_list == NULL) { + pstr->oss.setup_list = setup; + } else { + for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next); + setup1->next = setup; + } + template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL); + } else { + buffer->error = -ENOMEM; + } + } + if (setup) + *setup = template; + up(&pstr->oss.setup_mutex); + } +} + +static void snd_pcm_oss_proc_init(snd_pcm_t *pcm) +{ + int stream; + for (stream = 0; stream < 2; ++stream) { + snd_info_entry_t *entry; + snd_pcm_str_t *pstr = &pcm->streams[stream]; + if (pstr->substream_count == 0) + continue; + if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 8192; + entry->c.text.read = snd_pcm_oss_proc_read; + entry->c.text.write_size = 8192; + entry->c.text.write = snd_pcm_oss_proc_write; + entry->private_data = pstr; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + pstr->oss.proc_entry = entry; + } +} + +static void snd_pcm_oss_proc_done(snd_pcm_t *pcm) +{ + int stream; + for (stream = 0; stream < 2; ++stream) { + snd_pcm_str_t *pstr = &pcm->streams[stream]; + if (pstr->oss.proc_entry) { + snd_info_unregister(pstr->oss.proc_entry); + pstr->oss.proc_entry = NULL; + snd_pcm_oss_proc_free_setup_list(pstr); + } + } +} + +/* + * ENTRY functions + */ + +static struct file_operations snd_pcm_oss_f_reg = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = snd_pcm_oss_read, + .write = snd_pcm_oss_write, + .open = snd_pcm_oss_open, + .release = snd_pcm_oss_release, + .poll = snd_pcm_oss_poll, + .ioctl = snd_pcm_oss_ioctl, + .mmap = snd_pcm_oss_mmap, +}; + +static snd_minor_t snd_pcm_oss_reg = +{ + .comment = "digital audio", + .f_ops = &snd_pcm_oss_f_reg, +}; + +static void register_oss_dsp(snd_pcm_t *pcm, int index) +{ + char name[128]; + sprintf(name, "dsp%i%i", pcm->card->number, pcm->device); + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, index, &snd_pcm_oss_reg, + name) < 0) { + snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device); + } +} + +static int snd_pcm_oss_register_minor(snd_pcm_t * pcm) +{ + pcm->oss.reg = 0; + if (dsp_map[pcm->card->number] == (int)pcm->device) { + char name[128]; + int duplex; + register_oss_dsp(pcm, 0); + duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 && + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count && + !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)); + sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : ""); +#ifdef SNDRV_OSS_INFO_DEV_AUDIO + snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO, + pcm->card->number, + name); +#endif + pcm->oss.reg++; + pcm->oss.reg_mask |= 1; + } + if (adsp_map[pcm->card->number] == (int)pcm->device) { + register_oss_dsp(pcm, 1); + pcm->oss.reg++; + pcm->oss.reg_mask |= 2; + } + + if (pcm->oss.reg) + snd_pcm_oss_proc_init(pcm); + + return 0; +} + +static int snd_pcm_oss_disconnect_minor(snd_pcm_t * pcm) +{ + if (pcm->oss.reg) { + if (pcm->oss.reg_mask & 1) { + pcm->oss.reg_mask &= ~1; + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, 0); + } + if (pcm->oss.reg_mask & 2) { + pcm->oss.reg_mask &= ~2; + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, 1); + } + } + return 0; +} + +static int snd_pcm_oss_unregister_minor(snd_pcm_t * pcm) +{ + snd_pcm_oss_disconnect_minor(pcm); + if (pcm->oss.reg) { + if (dsp_map[pcm->card->number] == (int)pcm->device) { +#ifdef SNDRV_OSS_INFO_DEV_AUDIO + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number); +#endif + } + pcm->oss.reg = 0; + snd_pcm_oss_proc_done(pcm); + } + return 0; +} + +static snd_pcm_notify_t snd_pcm_oss_notify = +{ + .n_register = snd_pcm_oss_register_minor, + .n_disconnect = snd_pcm_oss_disconnect_minor, + .n_unregister = snd_pcm_oss_unregister_minor, +}; + +static int __init alsa_pcm_oss_init(void) +{ + int i; + int err; + + /* check device map table */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) { + snd_printk("invalid dsp_map[%d] = %d\n", i, dsp_map[i]); + dsp_map[i] = 0; + } + if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) { + snd_printk("invalid adsp_map[%d] = %d\n", i, adsp_map[i]); + adsp_map[i] = 1; + } + } + if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0) + return err; + return 0; +} + +static void __exit alsa_pcm_oss_exit(void) +{ + snd_pcm_notify(&snd_pcm_oss_notify, 1); +} + +module_init(alsa_pcm_oss_init) +module_exit(alsa_pcm_oss_exit) diff -urN linux-2.4.21-rc1.orig/sound/core/oss/pcm_plugin.c linux/sound/core/oss/pcm_plugin.c --- linux-2.4.21-rc1.orig/sound/core/oss/pcm_plugin.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/pcm_plugin.c 2003-02-07 01:29:08.000000000 -0700 @@ -0,0 +1,1092 @@ +/* + * PCM Plug-In shared (kernel/library) code + * Copyright (c) 1999 by Jaroslav Kysela + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +#define PLUGIN_DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) +#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) + +static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + bitset_t *vmask = plugin->src_vmask; + bitset_copy(vmask, dst_vmask, plugin->src_format.channels); + *src_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + bitset_t *vmask = plugin->dst_vmask; + bitset_copy(vmask, src_vmask, plugin->dst_format.channels); + *dst_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + snd_pcm_plugin_format_t *format; + ssize_t width; + size_t size; + unsigned int channel; + snd_pcm_plugin_channel_t *c; + + if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { + format = &plugin->src_format; + } else { + format = &plugin->dst_format; + } + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + size = frames * format->channels * width; + snd_assert((size % 8) == 0, return -ENXIO); + size /= 8; + if (plugin->buf_frames < frames) { + if (plugin->buf) + vfree(plugin->buf); + plugin->buf = vmalloc(size); + plugin->buf_frames = frames; + } + if (!plugin->buf) + return -ENOMEM; + c = plugin->buf_channels; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + for (channel = 0; channel < format->channels; channel++, c++) { + c->enabled = 1; + c->wanted = 0; + c->area.addr = plugin->buf; + c->area.first = channel * width; + c->area.step = format->channels * width; + } + } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { + snd_assert((size % format->channels) == 0,); + size /= format->channels; + for (channel = 0; channel < format->channels; channel++, c++) { + c->enabled = 1; + c->wanted = 0; + c->area.addr = plugin->buf + (channel * size); + c->area.first = 0; + c->area.step = width; + } + } else + return -EINVAL; + return 0; +} + +int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames) +{ + int err; + snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); + if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + while (plugin->next) { + if (plugin->dst_frames) + frames = plugin->dst_frames(plugin, frames); + snd_assert(frames > 0, return -ENXIO); + plugin = plugin->next; + err = snd_pcm_plugin_alloc(plugin, frames); + if (err < 0) + return err; + } + } else { + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + while (plugin->prev) { + if (plugin->src_frames) + frames = plugin->src_frames(plugin, frames); + snd_assert(frames > 0, return -ENXIO); + plugin = plugin->prev; + err = snd_pcm_plugin_alloc(plugin, frames); + if (err < 0) + return err; + } + } + return 0; +} + + +snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels) +{ + *channels = plugin->buf_channels; + return frames; +} + +int snd_pcm_plugin_build(snd_pcm_plug_t *plug, + const char *name, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + size_t extra, + snd_pcm_plugin_t **ret) +{ + snd_pcm_plugin_t *plugin; + unsigned int channels; + + snd_assert(plug != NULL, return -ENXIO); + snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); + plugin = (snd_pcm_plugin_t *)snd_kcalloc(sizeof(*plugin) + extra, GFP_KERNEL); + if (plugin == NULL) + return -ENOMEM; + plugin->name = name; + plugin->plug = plug; + plugin->stream = snd_pcm_plug_stream(plug); + plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + plugin->src_format = *src_format; + plugin->src_width = snd_pcm_format_physical_width(src_format->format); + snd_assert(plugin->src_width > 0, ); + plugin->dst_format = *dst_format; + plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); + snd_assert(plugin->dst_width > 0, ); + if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) + channels = src_format->channels; + else + channels = dst_format->channels; + plugin->buf_channels = snd_kcalloc(channels * sizeof(*plugin->buf_channels), GFP_KERNEL); + if (plugin->buf_channels == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->src_vmask = bitset_alloc(src_format->channels); + if (plugin->src_vmask == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->dst_vmask = bitset_alloc(dst_format->channels); + if (plugin->dst_vmask == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->client_channels = snd_pcm_plugin_client_channels; + plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; + plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; + *ret = plugin; + return 0; +} + +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) +{ + if (! plugin) + return 0; + if (plugin->private_free) + plugin->private_free(plugin); + if (plugin->buf_channels) + kfree(plugin->buf_channels); + if (plugin->buf) + vfree(plugin->buf); + if (plugin->src_vmask) + kfree(plugin->src_vmask); + if (plugin->dst_vmask) + kfree(plugin->dst_vmask); + kfree(plugin); + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames) +{ + snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(plug != NULL, return -ENXIO); + if (drv_frames == 0) + return 0; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_last(plug); + while (plugin && drv_frames > 0) { + plugin_prev = plugin->prev; + if (plugin->src_frames) + drv_frames = plugin->src_frames(plugin, drv_frames); + plugin = plugin_prev; + } + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + plugin = snd_pcm_plug_first(plug); + while (plugin && drv_frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) + drv_frames = plugin->dst_frames(plugin, drv_frames); + plugin = plugin_next; + } + } else + snd_BUG(); + return drv_frames; +} + +snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames) +{ + snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + snd_pcm_sframes_t frames; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(plug != NULL, return -ENXIO); + if (clt_frames == 0) + return 0; + frames = clt_frames; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) { + frames = plugin->dst_frames(plugin, frames); + if (frames < 0) + return frames; + } + plugin = plugin_next; + } + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + plugin = snd_pcm_plug_last(plug); + while (plugin) { + plugin_prev = plugin->prev; + if (plugin->src_frames) { + frames = plugin->src_frames(plugin, frames); + if (frames < 0) + return frames; + } + plugin = plugin_prev; + } + } else + snd_BUG(); + return frames; +} + +static int snd_pcm_plug_formats(snd_mask_t *mask, int format) +{ + snd_mask_t formats = *mask; + u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); + snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); + + if (formats.bits[0] & (u32)linfmts) + formats.bits[0] |= (u32)linfmts; + if (formats.bits[1] & (u32)(linfmts >> 32)) + formats.bits[1] |= (u32)(linfmts >> 32); + return snd_mask_test(&formats, format); +} + +static int preferred_formats[] = { + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_U8 +}; + +int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask) +{ + if (snd_mask_test(format_mask, format)) + return format; + if (! snd_pcm_plug_formats(format_mask, format)) + return -EINVAL; + if (snd_pcm_format_linear(format)) { + int width = snd_pcm_format_width(format); + int unsignd = snd_pcm_format_unsigned(format); + int big = snd_pcm_format_big_endian(format); + int format1; + int wid, width1=width; + int dwidth1 = 8; + for (wid = 0; wid < 4; ++wid) { + int end, big1 = big; + for (end = 0; end < 2; ++end) { + int sgn, unsignd1 = unsignd; + for (sgn = 0; sgn < 2; ++sgn) { + format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); + if (format1 >= 0 && + snd_mask_test(format_mask, format1)) + goto _found; + unsignd1 = !unsignd1; + } + big1 = !big1; + } + if (width1 == 32) { + dwidth1 = -dwidth1; + width1 = width; + } + width1 += dwidth1; + } + return -EINVAL; + _found: + return format1; + } else { + unsigned int i; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { + int format1 = preferred_formats[i]; + if (snd_mask_test(format_mask, format1)) + return format1; + } + default: + return -EINVAL; + } + } +} + +int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *slave_params) +{ + snd_pcm_plugin_format_t tmpformat; + snd_pcm_plugin_format_t dstformat; + snd_pcm_plugin_format_t srcformat; + int src_access, dst_access; + snd_pcm_plugin_t *plugin = NULL; + int err, first; + int stream = snd_pcm_plug_stream(plug); + int slave_interleaved = (params_channels(slave_params) == 1 || + params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + dstformat.format = params_format(slave_params); + dstformat.rate = params_rate(slave_params); + dstformat.channels = params_channels(slave_params); + srcformat.format = params_format(params); + srcformat.rate = params_rate(params); + srcformat.channels = params_channels(params); + src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + break; + case SNDRV_PCM_STREAM_CAPTURE: + dstformat.format = params_format(params); + dstformat.rate = params_rate(params); + dstformat.channels = params_channels(params); + srcformat.format = params_format(slave_params); + srcformat.rate = params_rate(slave_params); + srcformat.channels = params_channels(slave_params); + src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + break; + default: + snd_BUG(); + return -EINVAL; + } + tmpformat = srcformat; + + pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", + srcformat.format, + srcformat.rate, + srcformat.channels); + pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", + dstformat.format, + dstformat.rate, + dstformat.channels); + + /* Format change (linearization) */ + if ((srcformat.format != dstformat.format || + srcformat.rate != dstformat.rate || + srcformat.channels != dstformat.channels) && + !snd_pcm_format_linear(srcformat.format)) { + if (snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + else + tmpformat.format = SNDRV_PCM_FORMAT_S16; + first = plugin == NULL; + switch (srcformat.format) { + case SNDRV_PCM_FORMAT_MU_LAW: + err = snd_pcm_plugin_build_mulaw(plug, + &srcformat, &tmpformat, + &plugin); + break; + default: + return -EINVAL; + } + pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* channels reduction */ + if (srcformat.channels > dstformat.channels) { + int sv = srcformat.channels; + int dv = dstformat.channels; + route_ttable_entry_t *ttable = snd_kcalloc(dv*sv*sizeof(*ttable), GFP_KERNEL); + if (ttable == NULL) + return -ENOMEM; +#if 1 + if (sv == 2 && dv == 1) { + ttable[0] = HALF; + ttable[1] = HALF; + } else +#endif + { + int v; + for (v = 0; v < dv; ++v) + ttable[v * sv + v] = FULL; + } + tmpformat.channels = dstformat.channels; + if (srcformat.rate == dstformat.rate && + snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_route(plug, + &srcformat, &tmpformat, + ttable, &plugin); + kfree(ttable); + pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* rate resampling */ + if (srcformat.rate != dstformat.rate) { + tmpformat.rate = dstformat.rate; + if (srcformat.channels == dstformat.channels && + snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_rate(plug, + &srcformat, &tmpformat, + &plugin); + pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* channels extension */ + if (srcformat.channels < dstformat.channels) { + int sv = srcformat.channels; + int dv = dstformat.channels; + route_ttable_entry_t *ttable = snd_kcalloc(dv * sv * sizeof(*ttable), GFP_KERNEL); + if (ttable == NULL) + return -ENOMEM; +#if 0 + { + int v; + for (v = 0; v < sv; ++v) + ttable[v * sv + v] = FULL; + } +#else + { + /* Playback is spreaded on all channels */ + int vd, vs; + for (vd = 0, vs = 0; vd < dv; ++vd) { + ttable[vd * sv + vs] = FULL; + vs++; + if (vs == sv) + vs = 0; + } + } +#endif + tmpformat.channels = dstformat.channels; + if (snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_route(plug, + &srcformat, &tmpformat, + ttable, &plugin); + kfree(ttable); + pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* format change */ + if (srcformat.format != dstformat.format) { + tmpformat.format = dstformat.format; + if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { + err = snd_pcm_plugin_build_mulaw(plug, + &srcformat, &tmpformat, + &plugin); + } + else if (snd_pcm_format_linear(srcformat.format) && + snd_pcm_format_linear(tmpformat.format)) { + err = snd_pcm_plugin_build_linear(plug, + &srcformat, &tmpformat, + &plugin); + } + else + return -EINVAL; + pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* de-interleave */ + if (src_access != dst_access) { + err = snd_pcm_plugin_build_copy(plug, + &srcformat, + &tmpformat, + &plugin); + pdprintf("interleave change (copy: returns %i)\n", err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + } + + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, + char *buf, + snd_pcm_uframes_t count, + snd_pcm_plugin_channel_t **channels) +{ + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_channel_t *v; + snd_pcm_plugin_format_t *format; + int width, nchannels, channel; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(buf != NULL, return -ENXIO); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_first(plug); + format = &plugin->src_format; + } else { + plugin = snd_pcm_plug_last(plug); + format = &plugin->dst_format; + } + v = plugin->buf_channels; + *channels = v; + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + nchannels = format->channels; + snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); + for (channel = 0; channel < nchannels; channel++, v++) { + v->enabled = 1; + v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); + v->area.addr = buf; + v->area.first = channel * width; + v->area.step = nchannels * width; + } + return count; +} + +int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, + bitset_t *client_vmask) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + if (plugin == NULL) { + return 0; + } else { + int schannels = plugin->dst_format.channels; + bitset_t bs[bitset_size(schannels)]; + bitset_t *srcmask; + bitset_t *dstmask = bs; + int err; + bitset_one(dstmask, schannels); + if (plugin == NULL) { + bitset_and(client_vmask, dstmask, schannels); + return 0; + } + while (1) { + err = plugin->src_channels_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + if (plugin->prev == NULL) + break; + plugin = plugin->prev; + } + bitset_and(client_vmask, dstmask, plugin->src_format.channels); + return 0; + } +} + +int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug, + bitset_t *client_vmask) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + if (plugin == NULL) { + return 0; + } else { + int schannels = plugin->src_format.channels; + bitset_t bs[bitset_size(schannels)]; + bitset_t *srcmask = bs; + bitset_t *dstmask; + int err; + bitset_one(srcmask, schannels); + while (1) { + err = plugin->dst_channels_mask(plugin, srcmask, &dstmask); + if (err < 0) + return err; + srcmask = dstmask; + if (plugin->next == NULL) + break; + plugin = plugin->next; + } + bitset_and(client_vmask, srcmask, plugin->dst_format.channels); + return 0; + } +} + +static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, + snd_pcm_plugin_channel_t *src_channels) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + unsigned int nchannels = plugin->src_format.channels; + bitset_t bs[bitset_size(nchannels)]; + bitset_t *srcmask = bs; + int err; + unsigned int channel; + for (channel = 0; channel < nchannels; channel++) { + if (src_channels[channel].enabled) + bitset_set(srcmask, channel); + else + bitset_reset(srcmask, channel); + } + err = snd_pcm_plug_playback_channels_mask(plug, srcmask); + if (err < 0) + return err; + for (channel = 0; channel < nchannels; channel++) { + if (!bitset_get(srcmask, channel)) + src_channels[channel].enabled = 0; + } + return 0; +} + +static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, + snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *client_channels) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + unsigned int nchannels = plugin->dst_format.channels; + bitset_t bs[bitset_size(nchannels)]; + bitset_t *dstmask = bs; + bitset_t *srcmask; + int err; + unsigned int channel; + for (channel = 0; channel < nchannels; channel++) { + if (client_channels[channel].enabled) + bitset_set(dstmask, channel); + else + bitset_reset(dstmask, channel); + } + while (plugin) { + err = plugin->src_channels_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + plugin = plugin->prev; + } + plugin = snd_pcm_plug_first(plug); + nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; channel++) { + if (!bitset_get(dstmask, channel)) + src_channels[channel].enabled = 0; + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_channel_t *dst_channels; + int err; + snd_pcm_sframes_t frames = size; + + if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) + return err; + + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + if ((next = plugin->next) != NULL) { + snd_pcm_sframes_t frames1 = frames; + if (plugin->dst_frames) + frames1 = plugin->dst_frames(plugin, frames); + if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { + return err; + } + if (err != frames1) { + frames = err; + if (plugin->src_frames) + frames = plugin->src_frames(plugin, frames1); + } + } else + dst_channels = 0; + pdprintf("write plugin: %s, %li\n", plugin->name, frames); + if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) + return frames; + src_channels = dst_channels; + plugin = next; + } + return snd_pcm_plug_client_size(plug, frames); +} + +snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_channel_t *src_channels, *dst_channels; + snd_pcm_sframes_t frames = size; + int err; + + frames = snd_pcm_plug_slave_size(plug, frames); + if (frames < 0) + return frames; + + src_channels = 0; + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + if ((next = plugin->next) != NULL) { + if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { + return err; + } + frames = err; + if (!plugin->prev) { + if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0) + return err; + } + } else { + dst_channels = dst_channels_final; + } + pdprintf("read plugin: %s, %li\n", plugin->name, frames); + if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) + return frames; + plugin = next; + src_channels = dst_channels; + } + return frames; +} + +int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *dst; + unsigned int dst_step; + int width; + u_int64_t silence; + if (!dst_area->addr) + return 0; + dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; + width = snd_pcm_format_physical_width(format); + silence = snd_pcm_format_silence_64(format); + if (dst_area->step == (unsigned int) width) { + size_t dwords = samples * width / 64; + samples -= dwords * 64 / width; + while (dwords-- > 0) + *((u_int64_t*)dst)++ = silence; + if (samples == 0) + return 0; + } + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + u_int8_t s0 = silence & 0xf0; + u_int8_t s1 = silence & 0x0f; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + if (dstbit) { + *dst &= 0xf0; + *dst |= s1; + } else { + *dst &= 0x0f; + *dst |= s0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + u_int8_t sil = silence; + while (samples-- > 0) { + *dst = sil; + dst += dst_step; + } + break; + } + case 16: { + u_int16_t sil = silence; + while (samples-- > 0) { + *(u_int16_t*)dst = sil; + dst += dst_step; + } + break; + } + case 32: { + u_int32_t sil = silence; + while (samples-- > 0) { + *(u_int32_t*)dst = sil; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = silence; + dst += dst_step; + } + break; + } + default: + snd_BUG(); + } + return 0; +} + +int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + void *addr = dst_areas->addr; + unsigned int step = dst_areas->step; + const snd_pcm_channel_area_t *begin = dst_areas; + int vc = channels; + unsigned int v = 0; + int err; + while (1) { + vc--; + v++; + dst_areas++; + if (vc == 0 || + dst_areas->addr != addr || + dst_areas->step != step || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (v > 1 && v * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t d; + d.addr = begin->addr; + d.first = begin->first; + d.step = width; + err = snd_pcm_area_silence(&d, dst_offset * v, frames * v, format); + channels -= v; + } else { + err = snd_pcm_area_silence(begin, dst_offset, frames, format); + dst_areas = begin + 1; + channels--; + } + if (err < 0) + return err; + } + return 0; +} + + +int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, + const snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *src, *dst; + int width; + int src_step, dst_step; + src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; + if (!src_area->addr) + return snd_pcm_area_silence(dst_area, dst_offset, samples, format); + dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; + if (!dst_area->addr) + return 0; + width = snd_pcm_format_physical_width(format); + if (src_area->step == (unsigned int) width && + dst_area->step == (unsigned int) width) { + size_t bytes = samples * width / 8; + samples -= bytes * 8 / width; + memcpy(dst, src, bytes); + if (samples == 0) + return 0; + } + src_step = src_area->step / 8; + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + int srcbit = src_area->first % 8; + int srcbit_step = src_area->step % 8; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + unsigned char srcval; + if (srcbit) + srcval = *src & 0x0f; + else + srcval = *src & 0xf0; + if (dstbit) + *dst &= 0xf0; + else + *dst &= 0x0f; + *dst |= srcval; + src += src_step; + srcbit += srcbit_step; + if (srcbit == 8) { + src++; + srcbit = 0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + while (samples-- > 0) { + *dst = *src; + src += src_step; + dst += dst_step; + } + break; + } + case 16: { + while (samples-- > 0) { + *(u_int16_t*)dst = *(u_int16_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + case 32: { + while (samples-- > 0) { + *(u_int32_t*)dst = *(u_int32_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = *(u_int64_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + default: + snd_BUG(); + } + return 0; +} + +int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, + const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + unsigned int step = src_areas->step; + void *src_addr = src_areas->addr; + const snd_pcm_channel_area_t *src_start = src_areas; + void *dst_addr = dst_areas->addr; + const snd_pcm_channel_area_t *dst_start = dst_areas; + int vc = channels; + unsigned int v = 0; + while (dst_areas->step == step) { + vc--; + v++; + src_areas++; + dst_areas++; + if (vc == 0 || + src_areas->step != step || + src_areas->addr != src_addr || + dst_areas->addr != dst_addr || + src_areas->first != src_areas[-1].first + width || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (v > 1 && v * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t s, d; + s.addr = src_start->addr; + s.first = src_start->first; + s.step = width; + d.addr = dst_start->addr; + d.first = dst_start->first; + d.step = width; + snd_pcm_area_copy(&s, src_offset * v, &d, dst_offset * v, frames * v, format); + channels -= v; + } else { + snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format); + src_areas = src_start + 1; + dst_areas = dst_start + 1; + channels--; + } + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/oss/pcm_plugin.h linux/sound/core/oss/pcm_plugin.h --- linux-2.4.21-rc1.orig/sound/core/oss/pcm_plugin.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/pcm_plugin.h 2002-06-25 20:02:49.000000000 -0600 @@ -0,0 +1,255 @@ +#ifndef __PCM_PLUGIN_H +#define __PCM_PLUGIN_H + +/* + * Digital Audio (Plugin interface) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +typedef unsigned int bitset_t; + +static inline size_t bitset_size(int nbits) +{ + return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8); +} + +static inline bitset_t *bitset_alloc(int nbits) +{ + return snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL); +} + +static inline void bitset_set(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] |= 1 << (pos % bits); +} + +static inline void bitset_reset(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] &= ~(1 << (pos % bits)); +} + +static inline int bitset_get(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + return !!(bitmap[pos / bits] & (1 << (pos % bits))); +} + +static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits) +{ + memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t)); +} + +static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ &= *bs++; +} + +static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ |= *bs++; +} + +static inline void bitset_zero(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = 0; +} + +static inline void bitset_one(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = ~(bitset_t)0; +} + +#define snd_pcm_plug_t snd_pcm_substream_t +#define snd_pcm_plug_stream(plug) ((plug)->stream) + +typedef enum { + INIT = 0, + PREPARE = 1, +} snd_pcm_plugin_action_t; + +typedef struct _snd_pcm_channel_area { + void *addr; /* base address of channel samples */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +} snd_pcm_channel_area_t; + +typedef struct _snd_pcm_plugin_channel { + void *aptr; /* pointer to the allocated area */ + snd_pcm_channel_area_t area; + unsigned int enabled:1; /* channel need to be processed */ + unsigned int wanted:1; /* channel is wanted */ +} snd_pcm_plugin_channel_t; + +typedef struct _snd_pcm_plugin_format { + int format; + unsigned int rate; + unsigned int channels; +} snd_pcm_plugin_format_t; + +struct _snd_pcm_plugin { + const char *name; /* plug-in name */ + int stream; + snd_pcm_plugin_format_t src_format; /* source format */ + snd_pcm_plugin_format_t dst_format; /* destination format */ + int src_width; /* sample width in bits */ + int dst_width; /* sample width in bits */ + int access; + snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames); + snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames); + snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels); + int (*src_channels_mask)(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask); + int (*dst_channels_mask)(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask); + snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames); + int (*action)(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_action_t action, + unsigned long data); + snd_pcm_plugin_t *prev; + snd_pcm_plugin_t *next; + snd_pcm_plug_t *plug; + void *private_data; + void (*private_free)(snd_pcm_plugin_t *plugin); + char *buf; + snd_pcm_uframes_t buf_frames; + snd_pcm_plugin_channel_t *buf_channels; + bitset_t *src_vmask; + bitset_t *dst_vmask; + char extra_data[0]; +}; + +int snd_pcm_plugin_build(snd_pcm_plug_t *handle, + const char *name, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + size_t extra, + snd_pcm_plugin_t **ret); +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin); +int snd_pcm_plugin_clear(snd_pcm_plugin_t **first); +int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size); +snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size); + +#define ROUTE_PLUGIN_USE_FLOAT 0 +#define FULL ROUTE_PLUGIN_RESOLUTION +#define HALF ROUTE_PLUGIN_RESOLUTION / 2 +typedef int route_ttable_entry_t; + +int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle, + snd_pcm_hw_params_t *params, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + route_ttable_entry_t *ttable, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); + +int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *slave_params); + +int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask); + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin); + +snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size); + +snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle, + char *buf, snd_pcm_uframes_t count, + snd_pcm_plugin_channel_t **channels); + +snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels); + +int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format); +int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset, + const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset, + const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format); + +void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size); +void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr); +snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); + + + +#define ROUTE_PLUGIN_RESOLUTION 16 + +int getput_index(int format); +int copy_index(int format); +int conv_index(int src_format, int dst_format); + +void zero_channel(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *dst_channel, + size_t samples); + +#ifdef PLUGIN_DEBUG +#define pdprintf( args... ) printk( "plugin: " ##args) +#else +#define pdprintf( args... ) { ; } +#endif + +#endif /* __PCM_PLUGIN_H */ diff -urN linux-2.4.21-rc1.orig/sound/core/oss/plugin_ops.h linux/sound/core/oss/plugin_ops.h --- linux-2.4.21-rc1.orig/sound/core/oss/plugin_ops.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/plugin_ops.h 2001-12-30 02:26:45.000000000 -0700 @@ -0,0 +1,536 @@ +/* + * Plugin sample operators with fast switch + * Copyright (c) 2000 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#define as_u8(ptr) (*(u_int8_t*)(ptr)) +#define as_u16(ptr) (*(u_int16_t*)(ptr)) +#define as_u32(ptr) (*(u_int32_t*)(ptr)) +#define as_u64(ptr) (*(u_int64_t*)(ptr)) +#define as_s8(ptr) (*(int8_t*)(ptr)) +#define as_s16(ptr) (*(int16_t*)(ptr)) +#define as_s32(ptr) (*(int32_t*)(ptr)) +#define as_s64(ptr) (*(int64_t*)(ptr)) + +#ifdef COPY_LABELS +static void *copy_labels[4] = { + &©_8, + &©_16, + &©_32, + &©_64 +}; +#endif + +#ifdef COPY_END +while(0) { +copy_8: as_s8(dst) = as_s8(src); goto COPY_END; +copy_16: as_s16(dst) = as_s16(src); goto COPY_END; +copy_32: as_s32(dst) = as_s32(src); goto COPY_END; +copy_64: as_s64(dst) = as_s64(src); goto COPY_END; +} +#endif + +#ifdef CONV_LABELS +/* src_wid src_endswap sign_toggle dst_wid dst_endswap */ +static void *conv_labels[4 * 2 * 2 * 4 * 2] = { + &&conv_xxx1_xxx1, /* 8h -> 8h */ + &&conv_xxx1_xxx1, /* 8h -> 8s */ + &&conv_xxx1_xx10, /* 8h -> 16h */ + &&conv_xxx1_xx01, /* 8h -> 16s */ + &&conv_xxx1_x100, /* 8h -> 24h */ + &&conv_xxx1_001x, /* 8h -> 24s */ + &&conv_xxx1_1000, /* 8h -> 32h */ + &&conv_xxx1_0001, /* 8h -> 32s */ + &&conv_xxx1_xxx9, /* 8h ^> 8h */ + &&conv_xxx1_xxx9, /* 8h ^> 8s */ + &&conv_xxx1_xx90, /* 8h ^> 16h */ + &&conv_xxx1_xx09, /* 8h ^> 16s */ + &&conv_xxx1_x900, /* 8h ^> 24h */ + &&conv_xxx1_009x, /* 8h ^> 24s */ + &&conv_xxx1_9000, /* 8h ^> 32h */ + &&conv_xxx1_0009, /* 8h ^> 32s */ + &&conv_xxx1_xxx1, /* 8s -> 8h */ + &&conv_xxx1_xxx1, /* 8s -> 8s */ + &&conv_xxx1_xx10, /* 8s -> 16h */ + &&conv_xxx1_xx01, /* 8s -> 16s */ + &&conv_xxx1_x100, /* 8s -> 24h */ + &&conv_xxx1_001x, /* 8s -> 24s */ + &&conv_xxx1_1000, /* 8s -> 32h */ + &&conv_xxx1_0001, /* 8s -> 32s */ + &&conv_xxx1_xxx9, /* 8s ^> 8h */ + &&conv_xxx1_xxx9, /* 8s ^> 8s */ + &&conv_xxx1_xx90, /* 8s ^> 16h */ + &&conv_xxx1_xx09, /* 8s ^> 16s */ + &&conv_xxx1_x900, /* 8s ^> 24h */ + &&conv_xxx1_009x, /* 8s ^> 24s */ + &&conv_xxx1_9000, /* 8s ^> 32h */ + &&conv_xxx1_0009, /* 8s ^> 32s */ + &&conv_xx12_xxx1, /* 16h -> 8h */ + &&conv_xx12_xxx1, /* 16h -> 8s */ + &&conv_xx12_xx12, /* 16h -> 16h */ + &&conv_xx12_xx21, /* 16h -> 16s */ + &&conv_xx12_x120, /* 16h -> 24h */ + &&conv_xx12_021x, /* 16h -> 24s */ + &&conv_xx12_1200, /* 16h -> 32h */ + &&conv_xx12_0021, /* 16h -> 32s */ + &&conv_xx12_xxx9, /* 16h ^> 8h */ + &&conv_xx12_xxx9, /* 16h ^> 8s */ + &&conv_xx12_xx92, /* 16h ^> 16h */ + &&conv_xx12_xx29, /* 16h ^> 16s */ + &&conv_xx12_x920, /* 16h ^> 24h */ + &&conv_xx12_029x, /* 16h ^> 24s */ + &&conv_xx12_9200, /* 16h ^> 32h */ + &&conv_xx12_0029, /* 16h ^> 32s */ + &&conv_xx12_xxx2, /* 16s -> 8h */ + &&conv_xx12_xxx2, /* 16s -> 8s */ + &&conv_xx12_xx21, /* 16s -> 16h */ + &&conv_xx12_xx12, /* 16s -> 16s */ + &&conv_xx12_x210, /* 16s -> 24h */ + &&conv_xx12_012x, /* 16s -> 24s */ + &&conv_xx12_2100, /* 16s -> 32h */ + &&conv_xx12_0012, /* 16s -> 32s */ + &&conv_xx12_xxxA, /* 16s ^> 8h */ + &&conv_xx12_xxxA, /* 16s ^> 8s */ + &&conv_xx12_xxA1, /* 16s ^> 16h */ + &&conv_xx12_xx1A, /* 16s ^> 16s */ + &&conv_xx12_xA10, /* 16s ^> 24h */ + &&conv_xx12_01Ax, /* 16s ^> 24s */ + &&conv_xx12_A100, /* 16s ^> 32h */ + &&conv_xx12_001A, /* 16s ^> 32s */ + &&conv_x123_xxx1, /* 24h -> 8h */ + &&conv_x123_xxx1, /* 24h -> 8s */ + &&conv_x123_xx12, /* 24h -> 16h */ + &&conv_x123_xx21, /* 24h -> 16s */ + &&conv_x123_x123, /* 24h -> 24h */ + &&conv_x123_321x, /* 24h -> 24s */ + &&conv_x123_1230, /* 24h -> 32h */ + &&conv_x123_0321, /* 24h -> 32s */ + &&conv_x123_xxx9, /* 24h ^> 8h */ + &&conv_x123_xxx9, /* 24h ^> 8s */ + &&conv_x123_xx92, /* 24h ^> 16h */ + &&conv_x123_xx29, /* 24h ^> 16s */ + &&conv_x123_x923, /* 24h ^> 24h */ + &&conv_x123_329x, /* 24h ^> 24s */ + &&conv_x123_9230, /* 24h ^> 32h */ + &&conv_x123_0329, /* 24h ^> 32s */ + &&conv_123x_xxx3, /* 24s -> 8h */ + &&conv_123x_xxx3, /* 24s -> 8s */ + &&conv_123x_xx32, /* 24s -> 16h */ + &&conv_123x_xx23, /* 24s -> 16s */ + &&conv_123x_x321, /* 24s -> 24h */ + &&conv_123x_123x, /* 24s -> 24s */ + &&conv_123x_3210, /* 24s -> 32h */ + &&conv_123x_0123, /* 24s -> 32s */ + &&conv_123x_xxxB, /* 24s ^> 8h */ + &&conv_123x_xxxB, /* 24s ^> 8s */ + &&conv_123x_xxB2, /* 24s ^> 16h */ + &&conv_123x_xx2B, /* 24s ^> 16s */ + &&conv_123x_xB21, /* 24s ^> 24h */ + &&conv_123x_12Bx, /* 24s ^> 24s */ + &&conv_123x_B210, /* 24s ^> 32h */ + &&conv_123x_012B, /* 24s ^> 32s */ + &&conv_1234_xxx1, /* 32h -> 8h */ + &&conv_1234_xxx1, /* 32h -> 8s */ + &&conv_1234_xx12, /* 32h -> 16h */ + &&conv_1234_xx21, /* 32h -> 16s */ + &&conv_1234_x123, /* 32h -> 24h */ + &&conv_1234_321x, /* 32h -> 24s */ + &&conv_1234_1234, /* 32h -> 32h */ + &&conv_1234_4321, /* 32h -> 32s */ + &&conv_1234_xxx9, /* 32h ^> 8h */ + &&conv_1234_xxx9, /* 32h ^> 8s */ + &&conv_1234_xx92, /* 32h ^> 16h */ + &&conv_1234_xx29, /* 32h ^> 16s */ + &&conv_1234_x923, /* 32h ^> 24h */ + &&conv_1234_329x, /* 32h ^> 24s */ + &&conv_1234_9234, /* 32h ^> 32h */ + &&conv_1234_4329, /* 32h ^> 32s */ + &&conv_1234_xxx4, /* 32s -> 8h */ + &&conv_1234_xxx4, /* 32s -> 8s */ + &&conv_1234_xx43, /* 32s -> 16h */ + &&conv_1234_xx34, /* 32s -> 16s */ + &&conv_1234_x432, /* 32s -> 24h */ + &&conv_1234_234x, /* 32s -> 24s */ + &&conv_1234_4321, /* 32s -> 32h */ + &&conv_1234_1234, /* 32s -> 32s */ + &&conv_1234_xxxC, /* 32s ^> 8h */ + &&conv_1234_xxxC, /* 32s ^> 8s */ + &&conv_1234_xxC3, /* 32s ^> 16h */ + &&conv_1234_xx3C, /* 32s ^> 16s */ + &&conv_1234_xC32, /* 32s ^> 24h */ + &&conv_1234_23Cx, /* 32s ^> 24s */ + &&conv_1234_C321, /* 32s ^> 32h */ + &&conv_1234_123C, /* 32s ^> 32s */ +}; +#endif + +#ifdef CONV_END +while(0) { +conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END; +conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END; +conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END; +conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END; +conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END; +conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END; +conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END; +conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END; +conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END; +conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END; +conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END; +conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END; +conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END; +conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END; +conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END; +conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END; +conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END; +conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END; +conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END; +conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END; +conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END; +conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END; +conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; +conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END; +conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END; +conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END; +conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END; +conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END; +conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END; +conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END; +conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; +conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END; +conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; +conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END; +conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END; +conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END; +conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END; +conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END; +conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END; +conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; +conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END; +conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END; +conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; +conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END; +conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END; +conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END; +conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END; +conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END; +conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END; +conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END; +conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END; +conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END; +conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; +conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END; +conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END; +conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END; +conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END; +conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END; +conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END; +conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END; +conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END; +conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END; +conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; +conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END; +conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END; +conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END; +conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END; +} +#endif + +#ifdef GET_S16_LABELS +/* src_wid src_endswap unsigned */ +static void *get_s16_labels[4 * 2 * 2] = { + &&get_s16_xxx1_xx10, /* 8h -> 16h */ + &&get_s16_xxx1_xx90, /* 8h ^> 16h */ + &&get_s16_xxx1_xx10, /* 8s -> 16h */ + &&get_s16_xxx1_xx90, /* 8s ^> 16h */ + &&get_s16_xx12_xx12, /* 16h -> 16h */ + &&get_s16_xx12_xx92, /* 16h ^> 16h */ + &&get_s16_xx12_xx21, /* 16s -> 16h */ + &&get_s16_xx12_xxA1, /* 16s ^> 16h */ + &&get_s16_x123_xx12, /* 24h -> 16h */ + &&get_s16_x123_xx92, /* 24h ^> 16h */ + &&get_s16_123x_xx32, /* 24s -> 16h */ + &&get_s16_123x_xxB2, /* 24s ^> 16h */ + &&get_s16_1234_xx12, /* 32h -> 16h */ + &&get_s16_1234_xx92, /* 32h ^> 16h */ + &&get_s16_1234_xx43, /* 32s -> 16h */ + &&get_s16_1234_xxC3, /* 32s ^> 16h */ +}; +#endif + +#ifdef GET_S16_END +while(0) { +get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END; +get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END; +get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END; +get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END; +get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END; +get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END; +get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END; +get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END; +get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END; +get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END; +get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END; +get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END; +get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END; +get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END; +} +#endif + +#ifdef PUT_S16_LABELS +/* dst_wid dst_endswap unsigned */ +static void *put_s16_labels[4 * 2 * 2 * 4 * 2] = { + &&put_s16_xx12_xxx1, /* 16h -> 8h */ + &&put_s16_xx12_xxx9, /* 16h ^> 8h */ + &&put_s16_xx12_xxx1, /* 16h -> 8s */ + &&put_s16_xx12_xxx9, /* 16h ^> 8s */ + &&put_s16_xx12_xx12, /* 16h -> 16h */ + &&put_s16_xx12_xx92, /* 16h ^> 16h */ + &&put_s16_xx12_xx21, /* 16h -> 16s */ + &&put_s16_xx12_xx29, /* 16h ^> 16s */ + &&put_s16_xx12_x120, /* 16h -> 24h */ + &&put_s16_xx12_x920, /* 16h ^> 24h */ + &&put_s16_xx12_021x, /* 16h -> 24s */ + &&put_s16_xx12_029x, /* 16h ^> 24s */ + &&put_s16_xx12_1200, /* 16h -> 32h */ + &&put_s16_xx12_9200, /* 16h ^> 32h */ + &&put_s16_xx12_0021, /* 16h -> 32s */ + &&put_s16_xx12_0029, /* 16h ^> 32s */ +}; +#endif + +#ifdef PUT_S16_END +while (0) { +put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END; +put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END; +put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END; +put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END; +put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END; +put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END; +put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END; +put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END; +put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END; +put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END; +put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END; +put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END; +put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END; +put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END; +} +#endif + +#if 0 +#ifdef GET32_LABELS +/* src_wid src_endswap unsigned */ +static void *get32_labels[4 * 2 * 2] = { + &&get32_xxx1_1000, /* 8h -> 32h */ + &&get32_xxx1_9000, /* 8h ^> 32h */ + &&get32_xxx1_1000, /* 8s -> 32h */ + &&get32_xxx1_9000, /* 8s ^> 32h */ + &&get32_xx12_1200, /* 16h -> 32h */ + &&get32_xx12_9200, /* 16h ^> 32h */ + &&get32_xx12_2100, /* 16s -> 32h */ + &&get32_xx12_A100, /* 16s ^> 32h */ + &&get32_x123_1230, /* 24h -> 32h */ + &&get32_x123_9230, /* 24h ^> 32h */ + &&get32_123x_3210, /* 24s -> 32h */ + &&get32_123x_B210, /* 24s ^> 32h */ + &&get32_1234_1234, /* 32h -> 32h */ + &&get32_1234_9234, /* 32h ^> 32h */ + &&get32_1234_4321, /* 32s -> 32h */ + &&get32_1234_C321, /* 32s ^> 32h */ +}; +#endif + +#ifdef GET32_END +while (0) { +get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END; +get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END; +get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END; +get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END; +get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END; +get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END; +get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END; +get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END; +get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END; +get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END; +get32_1234_1234: sample = as_u32(src); goto GET32_END; +get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END; +get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END; +get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END; +} +#endif +#endif + +#ifdef PUT_U32_LABELS +/* dst_wid dst_endswap unsigned */ +static void *put_u32_labels[4 * 2 * 2] = { + &&put_u32_1234_xxx9, /* u32h -> s8h */ + &&put_u32_1234_xxx1, /* u32h -> u8h */ + &&put_u32_1234_xxx9, /* u32h -> s8s */ + &&put_u32_1234_xxx1, /* u32h -> u8s */ + &&put_u32_1234_xx92, /* u32h -> s16h */ + &&put_u32_1234_xx12, /* u32h -> u16h */ + &&put_u32_1234_xx29, /* u32h -> s16s */ + &&put_u32_1234_xx21, /* u32h -> u16s */ + &&put_u32_1234_x923, /* u32h -> s24h */ + &&put_u32_1234_x123, /* u32h -> u24h */ + &&put_u32_1234_329x, /* u32h -> s24s */ + &&put_u32_1234_321x, /* u32h -> u24s */ + &&put_u32_1234_9234, /* u32h -> s32h */ + &&put_u32_1234_1234, /* u32h -> u32h */ + &&put_u32_1234_4329, /* u32h -> s32s */ + &&put_u32_1234_4321, /* u32h -> u32s */ +}; +#endif + +#ifdef PUT_U32_END +while (0) { +put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END; +put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END; +put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END; +put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END; +put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END; +put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END; +put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END; +put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END; +put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END; +put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END; +put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END; +put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END; +put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END; +put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END; +} +#endif + +#ifdef GET_U_LABELS +/* width endswap unsigned*/ +static void *get_u_labels[4 * 2 * 2] = { + &&get_u_s8, /* s8 -> u8 */ + &&get_u_u8, /* u8 -> u8 */ + &&get_u_s8, /* s8 -> u8 */ + &&get_u_u8, /* u8 -> u8 */ + &&get_u_s16h, /* s16h -> u16h */ + &&get_u_u16h, /* u16h -> u16h */ + &&get_u_s16s, /* s16s -> u16h */ + &&get_u_u16s, /* u16s -> u16h */ + &&get_u_s24h, /* s24h -> u32h */ + &&get_u_u24h, /* u24h -> u32h */ + &&get_u_s24s, /* s24s -> u32h */ + &&get_u_u24s, /* u24s -> u32h */ + &&get_u_s32h, /* s32h -> u32h */ + &&get_u_u32h, /* u32h -> u32h */ + &&get_u_s32s, /* s32s -> u32h */ + &&get_u_u32s, /* u32s -> u32h */ +}; +#endif + +#ifdef GET_U_END +while (0) { +get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END; +get_u_u8: sample = as_u8(src); goto GET_U_END; +get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END; +get_u_u16h: sample = as_u16(src); goto GET_U_END; +get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END; +get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END; +get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END; +get_u_u24h: sample = as_u32(src); goto GET_U_END; +get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END; +get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END; +get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END; +get_u_u32h: sample = as_u32(src); goto GET_U_END; +get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END; +get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END; +} +#endif + +#if 0 +#ifdef PUT_LABELS +/* width endswap unsigned */ +static void *put_labels[4 * 2 * 2] = { + &&put_s8, /* s8 -> s8 */ + &&put_u8, /* u8 -> s8 */ + &&put_s8, /* s8 -> s8 */ + &&put_u8, /* u8 -> s8 */ + &&put_s16h, /* s16h -> s16h */ + &&put_u16h, /* u16h -> s16h */ + &&put_s16s, /* s16s -> s16h */ + &&put_u16s, /* u16s -> s16h */ + &&put_s24h, /* s24h -> s32h */ + &&put_u24h, /* u24h -> s32h */ + &&put_s24s, /* s24s -> s32h */ + &&put_u24s, /* u24s -> s32h */ + &&put_s32h, /* s32h -> s32h */ + &&put_u32h, /* u32h -> s32h */ + &&put_s32s, /* s32s -> s32h */ + &&put_u32s, /* u32s -> s32h */ +}; +#endif + +#ifdef PUT_END +put_s8: as_s8(dst) = sample; goto PUT_END; +put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END; +put_s16h: as_s16(dst) = sample; goto PUT_END; +put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END; +put_s16s: as_s16(dst) = swab16(sample); goto PUT_END; +put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END; +put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END; +put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END; +put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END; +put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END; +put_s32h: as_s32(dst) = sample; goto PUT_END; +put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END; +put_s32s: as_s32(dst) = swab32(sample); goto PUT_END; +put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END; +#endif +#endif + +#undef as_u8 +#undef as_u16 +#undef as_u32 +#undef as_s8 +#undef as_s16 +#undef as_s32 diff -urN linux-2.4.21-rc1.orig/sound/core/oss/rate.c linux/sound/core/oss/rate.c --- linux-2.4.21-rc1.orig/sound/core/oss/rate.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/rate.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,386 @@ +/* + * Rate conversion Plug-In + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define SHIFT 11 +#define BITS (1<extra_data; + data->pos = 0; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + data->channels[channel].last_S1 = 0; + data->channels[channel].last_S2 = 0; + } +} + +static void resample_expand(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + int src_frames, int dst_frames) +{ + unsigned int pos = 0; + signed int val; + signed short S1, S2; + char *src, *dst; + unsigned int channel; + int src_step, dst_step; + int src_frames1, dst_frames1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_channel_t *rchannels = data->channels; + +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[data->get]; + void *put = put_s16_labels[data->put]; + void *get_s16_end = 0; + signed short sample = 0; +#define GET_S16_END *get_s16_end +#include "plugin_ops.h" +#undef GET_S16_END + + for (channel = 0; channel < plugin->src_format.channels; channel++) { + pos = data->pos; + S1 = rchannels->last_S1; + S2 = rchannels->last_S2; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + src_frames1 = src_frames; + dst_frames1 = dst_frames; + if (pos & ~R_MASK) { + get_s16_end = &&after_get1; + goto *get; + after_get1: + pos &= R_MASK; + S1 = S2; + S2 = sample; + src += src_step; + src_frames1--; + } + while (dst_frames1-- > 0) { + if (pos & ~R_MASK) { + pos &= R_MASK; + S1 = S2; + if (src_frames1-- > 0) { + get_s16_end = &&after_get2; + goto *get; + after_get2: + S2 = sample; + src += src_step; + } + } + val = S1 + ((S2 - S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + sample = val; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + pos += data->pitch; + } + rchannels->last_S1 = S1; + rchannels->last_S2 = S2; + rchannels++; + } + data->pos = pos; +} + +static void resample_shrink(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + int src_frames, int dst_frames) +{ + unsigned int pos = 0; + signed int val; + signed short S1, S2; + char *src, *dst; + unsigned int channel; + int src_step, dst_step; + int src_frames1, dst_frames1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_channel_t *rchannels = data->channels; + +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[data->get]; + void *put = put_s16_labels[data->put]; + signed short sample = 0; + + for (channel = 0; channel < plugin->src_format.channels; ++channel) { + pos = data->pos; + S1 = rchannels->last_S1; + S2 = rchannels->last_S2; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + src_frames1 = src_frames; + dst_frames1 = dst_frames; + while (dst_frames1 > 0) { + S1 = S2; + if (src_frames1-- > 0) { + goto *get; +#define GET_S16_END after_get +#include "plugin_ops.h" +#undef GET_S16_END + after_get: + S2 = sample; + src += src_step; + } + if (pos & ~R_MASK) { + pos &= R_MASK; + val = S1 + ((S2 - S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + sample = val; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + dst_frames1--; + } + pos += data->pitch; + } + rchannels->last_S1 = S1; + rchannels->last_S2 = S2; + rchannels++; + } + data->pos = pos; +} + +static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + rate_t *data; + snd_pcm_sframes_t res; + + snd_assert(plugin != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); + } else { + res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); + } + if (data->old_src_frames > 0) { + snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames; + while (data->old_src_frames < frames1) { + frames1 >>= 1; + res1 <<= 1; + } + while (data->old_src_frames > frames1) { + frames1 <<= 1; + res1 >>= 1; + } + if (data->old_src_frames == frames1) + return res1; + } + data->old_src_frames = frames; + data->old_dst_frames = res; + return res; +} + +static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + rate_t *data; + snd_pcm_sframes_t res; + + snd_assert(plugin != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); + } else { + res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); + } + if (data->old_dst_frames > 0) { + snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames; + while (data->old_dst_frames < frames1) { + frames1 >>= 1; + res1 <<= 1; + } + while (data->old_dst_frames > frames1) { + frames1 <<= 1; + res1 >>= 1; + } + if (data->old_dst_frames == frames1) + return res1; + } + data->old_dst_frames = frames; + data->old_src_frames = res; + return res; +} + +static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + snd_pcm_uframes_t dst_frames; + rate_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + + dst_frames = rate_dst_frames(plugin, frames); + data = (rate_t *)plugin->extra_data; + data->func(plugin, src_channels, dst_channels, frames, dst_frames); + return dst_frames; +} + +static int rate_action(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_action_t action, + unsigned long udata ATTRIBUTE_UNUSED) +{ + snd_assert(plugin != NULL, return -ENXIO); + switch (action) { + case INIT: + case PREPARE: + rate_init(plugin); + break; + default: + break; + } + return 0; /* silenty ignore other actions */ +} + +int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + rate_t *data; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + snd_assert(src_format->channels > 0, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO); + snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO); + snd_assert(src_format->rate != dst_format->rate, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "rate conversion", + src_format, dst_format, + sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t), + &plugin); + if (err < 0) + return err; + data = (rate_t *)plugin->extra_data; + data->get = getput_index(src_format->format); + data->put = getput_index(dst_format->format); + + if (src_format->rate < dst_format->rate) { + data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; + data->func = resample_expand; + } else { + data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate; + data->func = resample_shrink; + } + data->pos = 0; + rate_init(plugin); + data->old_src_frames = data->old_dst_frames = 0; + plugin->transfer = rate_transfer; + plugin->src_frames = rate_src_frames; + plugin->dst_frames = rate_dst_frames; + plugin->action = rate_action; + *r_plugin = plugin; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/oss/route.c linux/sound/core/oss/route.c --- linux-2.4.21-rc1.orig/sound/core/oss/route.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/oss/route.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,580 @@ +/* + * Attenuated route Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +/* The best possible hack to support missing optimization in gcc 2.7.2.3 */ +#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 +#define div(a) a /= ROUTE_PLUGIN_RESOLUTION +#elif ROUTE_PLUGIN_RESOLUTION == 16 +#define div(a) a >>= 4 +#else +#error "Add some code here" +#endif + +typedef struct ttable_dst ttable_dst_t; +typedef struct route_private_data route_t; + +typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames); + +typedef struct { + int channel; + int as_int; +#if ROUTE_PLUGIN_USE_FLOAT + float as_float; +#endif +} ttable_src_t; + +struct ttable_dst { + int att; /* Attenuated */ + unsigned int nsrcs; + ttable_src_t* srcs; + route_channel_f func; +}; + +struct route_private_data { + enum {R_UINT32=0, R_UINT64=1, R_FLOAT=2} sum_type; + int get, put; + int conv; + int src_sample_size; + ttable_dst_t ttable[0]; +}; + +typedef union { + u_int32_t as_uint32; + u_int64_t as_uint64; +#if ROUTE_PLUGIN_USE_FLOAT + float as_float; +#endif +} sum_t; + + +static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames) +{ + if (dst_channel->wanted) + snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format); + dst_channel->enabled = 0; +} + +static void route_to_channel_from_one(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + route_t *data = (route_t *)plugin->extra_data; + void *conv; + const snd_pcm_plugin_channel_t *src_channel = 0; + unsigned int srcidx; + char *src, *dst; + int src_step, dst_step; + for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { + src_channel = &src_channels[ttable->srcs[srcidx].channel]; + if (src_channel->area.addr != NULL) + break; + } + if (srcidx == ttable->nsrcs) { + route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); + return; + } + + dst_channel->enabled = 1; + conv = conv_labels[data->conv]; + src = src_channel->area.addr + src_channel->area.first / 8; + src_step = src_channel->area.step / 8; + dst = dst_channel->area.addr + dst_channel->area.first / 8; + dst_step = dst_channel->area.step / 8; + while (frames-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } +} + +static void route_to_channel(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames) +{ +#define GET_U_LABELS +#define PUT_U32_LABELS +#include "plugin_ops.h" +#undef GET_U_LABELS +#undef PUT_U32_LABELS + static void *zero_labels[3] = { &&zero_int32, &&zero_int64, +#if ROUTE_PLUGIN_USE_FLOAT + &&zero_float +#endif + }; + /* sum_type att */ + static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att, + &&add_int64_noatt, &&add_int64_att, +#if ROUTE_PLUGIN_USE_FLOAT + &&add_float_noatt, &&add_float_att +#endif + }; + /* sum_type att shift */ + static void *norm_labels[3 * 2 * 4] = { 0, + &&norm_int32_8_noatt, + &&norm_int32_16_noatt, + &&norm_int32_24_noatt, + 0, + &&norm_int32_8_att, + &&norm_int32_16_att, + &&norm_int32_24_att, + &&norm_int64_0_noatt, + &&norm_int64_8_noatt, + &&norm_int64_16_noatt, + &&norm_int64_24_noatt, + &&norm_int64_0_att, + &&norm_int64_8_att, + &&norm_int64_16_att, + &&norm_int64_24_att, +#if ROUTE_PLUGIN_USE_FLOAT + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, +#endif + }; + route_t *data = (route_t *)plugin->extra_data; + void *zero, *get, *add, *norm, *put_u32; + int nsrcs = ttable->nsrcs; + char *dst; + int dst_step; + char *srcs[nsrcs]; + int src_steps[nsrcs]; + ttable_src_t src_tt[nsrcs]; + u_int32_t sample = 0; + int srcidx, srcidx1 = 0; + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel]; + if (!src_channel->enabled) + continue; + srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8; + src_steps[srcidx1] = src_channel->area.step / 8; + src_tt[srcidx1] = ttable->srcs[srcidx]; + srcidx1++; + } + nsrcs = srcidx1; + if (nsrcs == 0) { + route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); + return; + } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { + route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames); + return; + } + + dst_channel->enabled = 1; + zero = zero_labels[data->sum_type]; + get = get_u_labels[data->get]; + add = add_labels[data->sum_type * 2 + ttable->att]; + norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; + put_u32 = put_u32_labels[data->put]; + dst = dst_channel->area.addr + dst_channel->area.first / 8; + dst_step = dst_channel->area.step / 8; + + while (frames-- > 0) { + ttable_src_t *ttp = src_tt; + sum_t sum; + + /* Zero sum */ + goto *zero; + zero_int32: + sum.as_uint32 = 0; + goto zero_end; + zero_int64: + sum.as_uint64 = 0; + goto zero_end; +#if ROUTE_PLUGIN_USE_FLOAT + zero_float: + sum.as_float = 0.0; + goto zero_end; +#endif + zero_end: + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + char *src = srcs[srcidx]; + + /* Get sample */ + goto *get; +#define GET_U_END after_get +#include "plugin_ops.h" +#undef GET_U_END + after_get: + + /* Sum */ + goto *add; + add_int32_att: + sum.as_uint32 += sample * ttp->as_int; + goto after_sum; + add_int32_noatt: + if (ttp->as_int) + sum.as_uint32 += sample; + goto after_sum; + add_int64_att: + sum.as_uint64 += (u_int64_t) sample * ttp->as_int; + goto after_sum; + add_int64_noatt: + if (ttp->as_int) + sum.as_uint64 += sample; + goto after_sum; +#if ROUTE_PLUGIN_USE_FLOAT + add_float_att: + sum.as_float += sample * ttp->as_float; + goto after_sum; + add_float_noatt: + if (ttp->as_int) + sum.as_float += sample; + goto after_sum; +#endif + after_sum: + srcs[srcidx] += src_steps[srcidx]; + ttp++; + } + + /* Normalization */ + goto *norm; + norm_int32_8_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_att: + sum.as_uint64 <<= 8; + norm_int64_0_att: + div(sum.as_uint64); + goto norm_int; + + norm_int32_16_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_att: + sum.as_uint64 <<= 16; + div(sum.as_uint64); + goto norm_int; + + norm_int32_24_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_att: + sum.as_uint64 <<= 24; + div(sum.as_uint64); + goto norm_int; + + norm_int32_8_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_noatt: + sum.as_uint64 <<= 8; + goto norm_int; + + norm_int32_16_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_noatt: + sum.as_uint64 <<= 16; + goto norm_int; + + norm_int32_24_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_noatt: + sum.as_uint64 <<= 24; + goto norm_int; + + norm_int64_0_noatt: + norm_int: + if (sum.as_uint64 > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_uint64; + goto after_norm; + +#if ROUTE_PLUGIN_USE_FLOAT + norm_float_8: + sum.as_float *= 1 << 8; + goto norm_float; + norm_float_16: + sum.as_float *= 1 << 16; + goto norm_float; + norm_float_24: + sum.as_float *= 1 << 24; + goto norm_float; + norm_float_0: + norm_float: + sum.as_float = floor(sum.as_float + 0.5); + if (sum.as_float > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_float; + goto after_norm; +#endif + after_norm: + + /* Put sample */ + goto *put_u32; +#define PUT_U32_END after_put_u32 +#include "plugin_ops.h" +#undef PUT_U32_END + after_put_u32: + + dst += dst_step; + } +} + +int route_src_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int schannels = plugin->src_format.channels; + int dchannels = plugin->dst_format.channels; + bitset_t *vmask = plugin->src_vmask; + int channel; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, schannels); + for (channel = 0; channel < dchannels; channel++, dp++) { + unsigned int src; + ttable_src_t *sp; + if (!bitset_get(dst_vmask, channel)) + continue; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) + bitset_set(vmask, sp->channel); + } + *src_vmask = vmask; + return 0; +} + +int route_dst_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int dchannels = plugin->dst_format.channels; + bitset_t *vmask = plugin->dst_vmask; + int channel; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, dchannels); + for (channel = 0; channel < dchannels; channel++, dp++) { + unsigned int src; + ttable_src_t *sp; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) { + if (bitset_get(src_vmask, sp->channel)) { + bitset_set(vmask, channel); + break; + } + } + } + *dst_vmask = vmask; + return 0; +} + +static void route_free(snd_pcm_plugin_t *plugin) +{ + route_t *data = (route_t *)plugin->extra_data; + unsigned int dst_channel; + for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { + if (data->ttable[dst_channel].srcs != NULL) + kfree(data->ttable[dst_channel].srcs); + } +} + +static int route_load_ttable(snd_pcm_plugin_t *plugin, + const route_ttable_entry_t* src_ttable) +{ + route_t *data; + unsigned int src_channel, dst_channel; + const route_ttable_entry_t *sptr; + ttable_dst_t *dptr; + if (src_ttable == NULL) + return 0; + data = (route_t *)plugin->extra_data; + dptr = data->ttable; + sptr = src_ttable; + plugin->private_free = route_free; + for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { + route_ttable_entry_t t = 0; + int att = 0; + int nsrcs = 0; + ttable_src_t srcs[plugin->src_format.channels]; + for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) { + snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO); + if (*sptr != 0) { + srcs[nsrcs].channel = src_channel; +#if ROUTE_PLUGIN_USE_FLOAT + /* Also in user space for non attenuated */ + srcs[nsrcs].as_int = (*sptr == FULL ? ROUTE_PLUGIN_RESOLUTION : 0); + srcs[nsrcs].as_float = *sptr; +#else + srcs[nsrcs].as_int = *sptr; +#endif + if (*sptr != FULL) + att = 1; + t += *sptr; + nsrcs++; + } + sptr++; + } + dptr->att = att; + dptr->nsrcs = nsrcs; + if (nsrcs == 0) + dptr->func = route_to_channel_from_zero; + else if (nsrcs == 1 && !att) + dptr->func = route_to_channel_from_one; + else + dptr->func = route_to_channel; + if (nsrcs > 0) { + int srcidx; + dptr->srcs = snd_kcalloc(nsrcs * sizeof(*srcs), GFP_KERNEL); + for(srcidx = 0; srcidx < nsrcs; srcidx++) + dptr->srcs[srcidx] = srcs[srcidx]; + } else + dptr->srcs = 0; + dptr++; + } + return 0; +} + +static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + route_t *data; + int src_nchannels, dst_nchannels; + int dst_channel; + ttable_dst_t *ttp; + snd_pcm_plugin_channel_t *dvp; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (route_t *)plugin->extra_data; + + src_nchannels = plugin->src_format.channels; + dst_nchannels = plugin->dst_format.channels; + +#ifdef CONFIG_SND_DEBUG + { + int src_channel; + for (src_channel = 0; src_channel < src_nchannels; ++src_channel) { + snd_assert(src_channels[src_channel].area.first % 8 == 0 || + src_channels[src_channel].area.step % 8 == 0, + return -ENXIO); + } + for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { + snd_assert(dst_channels[dst_channel].area.first % 8 == 0 || + dst_channels[dst_channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + + ttp = data->ttable; + dvp = dst_channels; + for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { + ttp->func(plugin, src_channels, dvp, ttp, frames); + dvp++; + ttp++; + } + return frames; +} + +int getput_index(int format) +{ + int sign, width, endian; + sign = !snd_pcm_format_signed(format); + width = snd_pcm_format_width(format) / 8 - 1; +#ifdef SNDRV_LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(format); +#else + endian = snd_pcm_format_little_endian(format); +#endif + if (endian < 0) + endian = 0; + return width * 4 + endian * 2 + sign; +} + +int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + route_ttable_entry_t *ttable, + snd_pcm_plugin_t **r_plugin) +{ + route_t *data; + snd_pcm_plugin_t *plugin; + int err; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) != 0 && + snd_pcm_format_linear(dst_format->format) != 0, + return -ENXIO); + + err = snd_pcm_plugin_build(plug, "attenuated route conversion", + src_format, dst_format, + sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels, + &plugin); + if (err < 0) + return err; + + data = (route_t *) plugin->extra_data; + + data->get = getput_index(src_format->format); + data->put = getput_index(dst_format->format); + data->conv = conv_index(src_format->format, dst_format->format); + +#if ROUTE_PLUGIN_USE_FLOAT + data->sum_type = R_FLOAT; +#else + if (snd_pcm_format_width(src_format->format) == 32) + data->sum_type = R_UINT64; + else + data->sum_type = R_UINT32; +#endif + data->src_sample_size = snd_pcm_format_width(src_format->format) / 8; + + if ((err = route_load_ttable(plugin, ttable)) < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + plugin->transfer = route_transfer; + plugin->src_channels_mask = route_src_channels_mask; + plugin->dst_channels_mask = route_dst_channels_mask; + *r_plugin = plugin; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/pcm.c linux/sound/core/pcm.c --- linux-2.4.21-rc1.orig/sound/core/pcm.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/pcm.c 2003-02-28 09:59:53.000000000 -0700 @@ -0,0 +1,1049 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); +MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); +MODULE_LICENSE("GPL"); + +snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES]; +static LIST_HEAD(snd_pcm_notify_list); +static DECLARE_MUTEX(register_mutex); + +static int snd_pcm_free(snd_pcm_t *pcm); +static int snd_pcm_dev_free(snd_device_t *device); +static int snd_pcm_dev_register(snd_device_t *device); +static int snd_pcm_dev_disconnect(snd_device_t *device); +static int snd_pcm_dev_unregister(snd_device_t *device); + +void snd_pcm_lock(int xup) +{ + if (!xup) { + down(®ister_mutex); + } else { + up(®ister_mutex); + } +} + +static int snd_pcm_control_ioctl(snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_PCM_DEVICES; + switch (cmd) { + case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_PCM_DEVICES) { + if (snd_pcm_devices[tmp + device]) + break; + device++; + } + if (device == SNDRV_PCM_DEVICES) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_PCM_INFO: + { + snd_pcm_info_t *info = (snd_pcm_info_t *)arg; + unsigned int device, subdevice; + snd_pcm_stream_t stream; + snd_pcm_t *pcm; + snd_pcm_str_t *pstr; + snd_pcm_substream_t *substream; + if (get_user(device, &info->device)) + return -EFAULT; + if (device >= SNDRV_PCM_DEVICES) + return -ENXIO; + pcm = snd_pcm_devices[tmp + device]; + if (pcm == NULL) + return -ENXIO; + if (get_user(stream, &info->stream)) + return -EFAULT; + if (stream < 0 || stream > 1) + return -EINVAL; + pstr = &pcm->streams[stream]; + if (pstr->substream_count == 0) + return -ENOENT; + if (get_user(subdevice, &info->subdevice)) + return -EFAULT; + if (subdevice >= pstr->substream_count) + return -ENXIO; + for (substream = pstr->substream; substream; substream = substream->next) + if (substream->number == (int)subdevice) + break; + if (substream == NULL) + return -ENXIO; + return snd_pcm_info_user(substream, info); + } + case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: + { + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + control->prefer_pcm_subdevice = val; + return 0; + } + } + return -ENOIOCTLCMD; +} +#define STATE(v) [SNDRV_PCM_STATE_##v] = #v +#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v +#define READY(v) [SNDRV_PCM_READY_##v] = #v +#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v +#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v +#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v +#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v +#define START(v) [SNDRV_PCM_START_##v] = #v +#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v +#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v + +char *snd_pcm_stream_names[] = { + STREAM(PLAYBACK), + STREAM(CAPTURE), +}; + +char *snd_pcm_state_names[] = { + STATE(OPEN), + STATE(SETUP), + STATE(PREPARED), + STATE(RUNNING), + STATE(XRUN), + STATE(DRAINING), + STATE(PAUSED), + STATE(SUSPENDED), +}; + +char *snd_pcm_access_names[] = { + ACCESS(MMAP_INTERLEAVED), + ACCESS(MMAP_NONINTERLEAVED), + ACCESS(MMAP_COMPLEX), + ACCESS(RW_INTERLEAVED), + ACCESS(RW_NONINTERLEAVED), +}; + +char *snd_pcm_format_names[] = { + FORMAT(S8), + FORMAT(U8), + FORMAT(S16_LE), + FORMAT(S16_BE), + FORMAT(U16_LE), + FORMAT(U16_BE), + FORMAT(S24_LE), + FORMAT(S24_BE), + FORMAT(U24_LE), + FORMAT(U24_BE), + FORMAT(S32_LE), + FORMAT(S32_BE), + FORMAT(U32_LE), + FORMAT(U32_BE), + FORMAT(FLOAT_LE), + FORMAT(FLOAT_BE), + FORMAT(FLOAT64_LE), + FORMAT(FLOAT64_BE), + FORMAT(IEC958_SUBFRAME_LE), + FORMAT(IEC958_SUBFRAME_BE), + FORMAT(MU_LAW), + FORMAT(A_LAW), + FORMAT(IMA_ADPCM), + FORMAT(MPEG), + FORMAT(GSM), + FORMAT(SPECIAL), + FORMAT(S24_3LE), + FORMAT(S24_3BE), + FORMAT(U24_3LE), + FORMAT(U24_3BE), + FORMAT(S20_3LE), + FORMAT(S20_3BE), + FORMAT(U20_3LE), + FORMAT(U20_3BE), + FORMAT(S18_3LE), + FORMAT(S18_3BE), + FORMAT(U18_3LE), + FORMAT(U18_3BE), +}; + +char *snd_pcm_subformat_names[] = { + SUBFORMAT(STD), +}; + +char *snd_pcm_tstamp_mode_names[] = { + TSTAMP(NONE), + TSTAMP(MMAP), +}; + +const char *snd_pcm_stream_name(snd_pcm_stream_t stream) +{ + snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return 0); + return snd_pcm_stream_names[stream]; +} + +const char *snd_pcm_access_name(snd_pcm_access_t access) +{ + snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return 0); + return snd_pcm_access_names[access]; +} + +const char *snd_pcm_format_name(snd_pcm_format_t format) +{ + snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return 0); + return snd_pcm_format_names[format]; +} + +const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) +{ + snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return 0); + return snd_pcm_subformat_names[subformat]; +} + +const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode) +{ + snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return 0); + return snd_pcm_tstamp_mode_names[mode]; +} + +const char *snd_pcm_state_name(snd_pcm_state_t state) +{ + snd_assert(state <= SNDRV_PCM_STATE_LAST, return 0); + return snd_pcm_state_names[state]; +} + +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#include +const char *snd_pcm_oss_format_name(int format) +{ + switch (format) { + case AFMT_MU_LAW: + return "MU_LAW"; + case AFMT_A_LAW: + return "A_LAW"; + case AFMT_IMA_ADPCM: + return "IMA_ADPCM"; + case AFMT_U8: + return "U8"; + case AFMT_S16_LE: + return "S16_LE"; + case AFMT_S16_BE: + return "S16_BE"; + case AFMT_S8: + return "S8"; + case AFMT_U16_LE: + return "U16_LE"; + case AFMT_U16_BE: + return "U16_BE"; + case AFMT_MPEG: + return "MPEG"; + default: + return "unknown"; + } +} +#endif + + +static void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer) +{ + snd_pcm_info_t info; + int err; + snd_runtime_check(substream, return); + err = snd_pcm_info(substream, &info); + if (err < 0) { + snd_iprintf(buffer, "error %d\n", err); + return; + } + snd_iprintf(buffer, "card: %d\n", info.card); + snd_iprintf(buffer, "device: %d\n", info.device); + snd_iprintf(buffer, "subdevice: %d\n", info.subdevice); + snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info.stream)); + snd_iprintf(buffer, "id: %s\n", info.id); + snd_iprintf(buffer, "name: %s\n", info.name); + snd_iprintf(buffer, "subname: %s\n", info.subname); + snd_iprintf(buffer, "class: %d\n", info.dev_class); + snd_iprintf(buffer, "subclass: %d\n", info.dev_subclass); + snd_iprintf(buffer, "subdevices_count: %d\n", info.subdevices_count); + snd_iprintf(buffer, "subdevices_avail: %d\n", info.subdevices_avail); +} + +static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer); +} + +static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer); +} + +static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_iprintf(buffer, "no setup\n"); + spin_unlock_irq(&runtime->lock); + return; + } + snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); + snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); + snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); + snd_iprintf(buffer, "channels: %u\n", runtime->channels); + snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); + snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); + snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); + snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + if (substream->oss.oss) { + snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); + snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); + snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); + snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); + snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); + } +#endif + spin_unlock_irq(&runtime->lock); +} + +static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_iprintf(buffer, "no setup\n"); + spin_unlock_irq(&runtime->lock); + return; + } + snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); + snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); + snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); + snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); + snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align); + snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); + snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); + snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); + snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); + snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); + spin_unlock_irq(&runtime->lock); +} + +static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_status_t status; + int err; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + err = snd_pcm_status(substream, &status); + if (err < 0) { + snd_iprintf(buffer, "error %d\n", err); + return; + } + snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); + snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", + status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); + snd_iprintf(buffer, "tstamp : %ld.%09ld\n", + status.tstamp.tv_sec, status.tstamp.tv_nsec); + snd_iprintf(buffer, "delay : %ld\n", status.delay); + snd_iprintf(buffer, "avail : %ld\n", status.avail); + snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); + snd_iprintf(buffer, "-----\n"); + snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); + snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); +} + +static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr) +{ + snd_pcm_t *pcm = pstr->pcm; + snd_info_entry_t *entry; + char name[16]; + + sprintf(name, "pcm%i%c", pcm->device, + pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); + if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + pstr->proc_root = entry; + + if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_stream_proc_info_read; + entry->private_data = pstr; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + pstr->proc_info_entry = entry; + + return 0; +} + +static int snd_pcm_stream_proc_done(snd_pcm_str_t *pstr) +{ + if (pstr->proc_info_entry) { + snd_info_unregister(pstr->proc_info_entry); + pstr->proc_info_entry = NULL; + } + if (pstr->proc_root) { + snd_info_unregister(pstr->proc_root); + pstr->proc_root = NULL; + } + return 0; +} + +static int snd_pcm_substream_proc_init(snd_pcm_substream_t *substream) +{ + snd_info_entry_t *entry; + snd_card_t *card; + char name[16]; + + card = substream->pcm->card; + + sprintf(name, "sub%i", substream->number); + if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + substream->proc_root = entry; + + if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_info_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_hw_params_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_hw_params_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_sw_params_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_sw_params_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_status_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_status_entry = entry; + + return 0; +} + +static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream) +{ + if (substream->proc_info_entry) { + snd_info_unregister(substream->proc_info_entry); + substream->proc_info_entry = 0; + } + if (substream->proc_hw_params_entry) { + snd_info_unregister(substream->proc_hw_params_entry); + substream->proc_hw_params_entry = 0; + } + if (substream->proc_sw_params_entry) { + snd_info_unregister(substream->proc_sw_params_entry); + substream->proc_sw_params_entry = 0; + } + if (substream->proc_status_entry) { + snd_info_unregister(substream->proc_status_entry); + substream->proc_status_entry = 0; + } + if (substream->proc_root) { + snd_info_unregister(substream->proc_root); + substream->proc_root = 0; + } + return 0; +} + +/** + * snd_pcm_new_stream - create a new PCM stream + * @pcm: the pcm instance + * @stream: the stream direction, SNDRV_PCM_STREAM_XXX + * @substream_count: the number of substreams + * + * Creates a new stream for the pcm. + * The corresponding stream on the pcm must have been empty before + * calling this, i.e. zero must be given to the argument of + * snd_pcm_new(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) +{ + int idx, err; + snd_pcm_str_t *pstr = &pcm->streams[stream]; + snd_pcm_substream_t *substream, *prev; + +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + init_MUTEX(&pstr->oss.setup_mutex); +#endif + pstr->stream = stream; + pstr->pcm = pcm; + pstr->substream_count = substream_count; + pstr->reg = &snd_pcm_reg[stream]; + if (substream_count > 0) { + err = snd_pcm_stream_proc_init(pstr); + if (err < 0) + return err; + } + prev = NULL; + for (idx = 0, prev = NULL; idx < substream_count; idx++) { + substream = snd_magic_kcalloc(snd_pcm_substream_t, 0, GFP_KERNEL); + if (substream == NULL) + return -ENOMEM; + substream->pcm = pcm; + substream->pstr = pstr; + substream->number = idx; + substream->stream = stream; + sprintf(substream->name, "subdevice #%i", idx); + substream->buffer_bytes_max = UINT_MAX; + if (prev == NULL) + pstr->substream = substream; + else + prev->next = substream; + substream->link_next = substream; + substream->link_prev = substream; + err = snd_pcm_substream_proc_init(substream); + if (err < 0) { + snd_magic_kfree(substream); + return err; + } + spin_lock_init(&substream->timer_lock); + prev = substream; + } + return 0; +} + +/** + * snd_pcm_new - create a new PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new PCM instance. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_new(snd_card_t * card, char *id, int device, + int playback_count, int capture_count, + snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_pcm_dev_free, + .dev_register = snd_pcm_dev_register, + .dev_disconnect = snd_pcm_dev_disconnect, + .dev_unregister = snd_pcm_dev_unregister + }; + + snd_assert(rpcm != NULL, return -EINVAL); + *rpcm = NULL; + snd_assert(card != NULL, return -ENXIO); + pcm = snd_magic_kcalloc(snd_pcm_t, 0, GFP_KERNEL); + if (pcm == NULL) + return -ENOMEM; + pcm->card = card; + pcm->device = device; + if (id) { + strncpy(pcm->id, id, sizeof(pcm->id) - 1); + } + if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { + snd_pcm_free(pcm); + return err; + } + if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { + snd_pcm_free(pcm); + return err; + } + init_MUTEX(&pcm->open_mutex); + init_waitqueue_head(&pcm->open_wait); + if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { + snd_pcm_free(pcm); + return err; + } + *rpcm = pcm; + return 0; +} + +static void snd_pcm_free_stream(snd_pcm_str_t * pstr) +{ + snd_pcm_substream_t *substream, *substream_next; +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + snd_pcm_oss_setup_t *setup, *setupn; +#endif + substream = pstr->substream; + while (substream) { + substream_next = substream->next; + snd_pcm_substream_proc_done(substream); + snd_magic_kfree(substream); + substream = substream_next; + } + snd_pcm_stream_proc_done(pstr); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + for (setup = pstr->oss.setup_list; setup; setup = setupn) { + setupn = setup->next; + kfree(setup->task_name); + kfree(setup); + } +#endif +} + +static int snd_pcm_free(snd_pcm_t *pcm) +{ + snd_assert(pcm != NULL, return -ENXIO); + if (pcm->private_free) + pcm->private_free(pcm); + snd_pcm_lib_preallocate_free_for_all(pcm); + snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); + snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); + snd_magic_kfree(pcm); + return 0; +} + +static int snd_pcm_dev_free(snd_device_t *device) +{ + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + return snd_pcm_free(pcm); +} + +static void snd_pcm_tick_timer_func(unsigned long data) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t*) data; + snd_pcm_tick_elapsed(substream); +} + +int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, + snd_pcm_substream_t **rsubstream) +{ + snd_pcm_str_t * pstr; + snd_pcm_substream_t * substream; + snd_pcm_runtime_t * runtime; + snd_ctl_file_t *kctl; + snd_card_t *card; + struct list_head *list; + int prefer_subdevice = -1; + size_t size; + + snd_assert(rsubstream != NULL, return -EINVAL); + *rsubstream = NULL; + snd_assert(pcm != NULL, return -ENXIO); + pstr = &pcm->streams[stream]; + if (pstr->substream == NULL) + return -ENODEV; + + card = pcm->card; + down_read(&card->controls_rwsem); + list_for_each(list, &card->ctl_files) { + kctl = snd_ctl_file(list); + if (kctl->pid == current->pid) { + prefer_subdevice = kctl->prefer_pcm_subdevice; + break; + } + } + up_read(&card->controls_rwsem); + + if (pstr->substream_count == 0) + return -ENODEV; + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; + } + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; + } + } + break; + default: + return -EINVAL; + } + + if (prefer_subdevice >= 0) { + for (substream = pstr->substream; substream; substream = substream->next) + if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) + goto __ok; + } + for (substream = pstr->substream; substream; substream = substream->next) + if (!SUBSTREAM_BUSY(substream)) + break; + __ok: + if (substream == NULL) + return -EAGAIN; + + runtime = snd_kcalloc(sizeof(snd_pcm_runtime_t), GFP_KERNEL); + if (runtime == NULL) + return -ENOMEM; + + size = PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)); + runtime->status = snd_malloc_pages(size, GFP_KERNEL); + if (runtime->status == NULL) { + kfree(runtime); + return -ENOMEM; + } + memset((void*)runtime->status, 0, size); + + size = PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)); + runtime->control = snd_malloc_pages(size, GFP_KERNEL); + if (runtime->control == NULL) { + snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))); + kfree(runtime); + return -ENOMEM; + } + memset((void*)runtime->control, 0, size); + + init_waitqueue_head(&runtime->sleep); + spin_lock_init(&runtime->lock); + atomic_set(&runtime->mmap_count, 0); + init_timer(&runtime->tick_timer); + runtime->tick_timer.function = snd_pcm_tick_timer_func; + runtime->tick_timer.data = (unsigned long) substream; + + runtime->status->state = SNDRV_PCM_STATE_OPEN; + + substream->runtime = runtime; + substream->private_data = pcm->private_data; + pstr->substream_opened++; + *rsubstream = substream; + return 0; +} + +void snd_pcm_release_substream(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t * runtime; + substream->file = NULL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + if (runtime->private_free != NULL) + runtime->private_free(runtime); + snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))); + snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))); + kfree(runtime->hw_constraints.rules); + kfree(runtime); + substream->runtime = NULL; + substream->pstr->substream_opened--; +} + +static int snd_pcm_dev_register(snd_device_t *device) +{ + int idx, cidx, err; + unsigned short minor; + snd_pcm_substream_t *substream; + struct list_head *list; + char str[16]; + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + + snd_assert(pcm != NULL && device != NULL, return -ENXIO); + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + if (snd_pcm_devices[idx]) { + snd_pcm_lock(1); + return -EBUSY; + } + snd_pcm_devices[idx] = pcm; + for (cidx = 0; cidx < 2; cidx++) { + int devtype = -1; + if (pcm->streams[cidx].substream == NULL) + continue; + switch (cidx) { + case SNDRV_PCM_STREAM_PLAYBACK: + sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); + minor = SNDRV_MINOR_PCM_PLAYBACK + idx; + devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; + break; + case SNDRV_PCM_STREAM_CAPTURE: + sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); + minor = SNDRV_MINOR_PCM_CAPTURE + idx; + devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; + break; + } + if ((err = snd_register_device(devtype, pcm->card, pcm->device, pcm->streams[cidx].reg, str)) < 0) { + snd_pcm_devices[idx] = NULL; + snd_pcm_lock(1); + return err; + } + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + snd_pcm_timer_init(substream); + } + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + notify->n_register(pcm); + } + snd_pcm_lock(1); + return 0; +} + +static int snd_pcm_dev_disconnect(snd_device_t *device) +{ + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + struct list_head *list; + int idx; + + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + snd_pcm_devices[idx] = NULL; + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + notify->n_disconnect(pcm); + } + snd_pcm_lock(1); + return 0; +} + +static int snd_pcm_dev_unregister(snd_device_t *device) +{ + int idx, cidx, devtype; + snd_pcm_substream_t *substream; + struct list_head *list; + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + + snd_assert(pcm != NULL, return -ENXIO); + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + snd_pcm_devices[idx] = NULL; + for (cidx = 0; cidx < 2; cidx++) { + devtype = -1; + switch (cidx) { + case SNDRV_PCM_STREAM_PLAYBACK: + devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; + break; + case SNDRV_PCM_STREAM_CAPTURE: + devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; + break; + } + snd_unregister_device(devtype, pcm->card, pcm->device); + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + snd_pcm_timer_done(substream); + } + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + notify->n_unregister(pcm); + } + snd_pcm_lock(1); + return snd_pcm_free(pcm); +} + +int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree) +{ + int idx; + + snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); + snd_pcm_lock(0); + if (nfree) { + list_del(¬ify->list); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + if (snd_pcm_devices[idx] == NULL) + continue; + notify->n_unregister(snd_pcm_devices[idx]); + } + } else { + list_add_tail(¬ify->list, &snd_pcm_notify_list); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + if (snd_pcm_devices[idx] == NULL) + continue; + notify->n_register(snd_pcm_devices[idx]); + } + } + snd_pcm_lock(1); + return 0; +} + +/* + * Info interface + */ + +static void snd_pcm_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + snd_pcm_t *pcm; + + down(®ister_mutex); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + pcm = snd_pcm_devices[idx]; + if (pcm == NULL) + continue; + snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES, + idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) + snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) + snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); + snd_iprintf(buffer, "\n"); + } + up(®ister_mutex); +} + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_pcm_proc_entry = NULL; + +static int __init alsa_pcm_init(void) +{ + snd_info_entry_t *entry; + + snd_ctl_register_ioctl(snd_pcm_control_ioctl); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = SNDRV_CARDS * SNDRV_PCM_DEVICES * 128; + entry->c.text.read = snd_pcm_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_pcm_proc_entry = entry; + return 0; +} + +static void __exit alsa_pcm_exit(void) +{ + snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); + if (snd_pcm_proc_entry) { + snd_info_unregister(snd_pcm_proc_entry); + snd_pcm_proc_entry = NULL; + } +} + +module_init(alsa_pcm_init) +module_exit(alsa_pcm_exit) + +EXPORT_SYMBOL(snd_pcm_lock); +EXPORT_SYMBOL(snd_pcm_devices); +EXPORT_SYMBOL(snd_pcm_new); +EXPORT_SYMBOL(snd_pcm_new_stream); +EXPORT_SYMBOL(snd_pcm_notify); +EXPORT_SYMBOL(snd_pcm_open_substream); +EXPORT_SYMBOL(snd_pcm_release_substream); +EXPORT_SYMBOL(snd_pcm_format_name); +EXPORT_SYMBOL(snd_pcm_subformat_name); + /* pcm_native.c */ +EXPORT_SYMBOL(snd_pcm_start); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_pcm_suspend); +EXPORT_SYMBOL(snd_pcm_suspend_all); +#endif +EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl); +EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl); +EXPORT_SYMBOL(snd_pcm_kernel_ioctl); +EXPORT_SYMBOL(snd_pcm_open); +EXPORT_SYMBOL(snd_pcm_release); +EXPORT_SYMBOL(snd_pcm_playback_poll); +EXPORT_SYMBOL(snd_pcm_capture_poll); +EXPORT_SYMBOL(snd_pcm_mmap_data); + /* pcm_misc.c */ +EXPORT_SYMBOL(snd_pcm_format_signed); +EXPORT_SYMBOL(snd_pcm_format_unsigned); +EXPORT_SYMBOL(snd_pcm_format_linear); +EXPORT_SYMBOL(snd_pcm_format_little_endian); +EXPORT_SYMBOL(snd_pcm_format_big_endian); +EXPORT_SYMBOL(snd_pcm_format_width); +EXPORT_SYMBOL(snd_pcm_format_physical_width); +EXPORT_SYMBOL(snd_pcm_format_size); +EXPORT_SYMBOL(snd_pcm_format_silence_64); +EXPORT_SYMBOL(snd_pcm_format_set_silence); +EXPORT_SYMBOL(snd_pcm_build_linear_format); diff -urN linux-2.4.21-rc1.orig/sound/core/pcm_lib.c linux/sound/core/pcm_lib.c --- linux-2.4.21-rc1.orig/sound/core/pcm_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/pcm_lib.c 2003-02-28 09:59:53.000000000 -0700 @@ -0,0 +1,2668 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * fill ring buffer with silence + * runtime->silenced_start: starting pointer to silence area + * runtime->silenced_size: size filled with silence + * runtime->silence_threshold: threshold from application + * runtime->silence_size: maximal size from application + * + * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately + */ +void snd_pcm_playback_silence(snd_pcm_substream_t *substream, snd_pcm_uframes_t new_hw_ptr) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frames, ofs, transfer; + + if (runtime->silence_size < runtime->boundary) { + snd_pcm_sframes_t noise_dist, n; + if (runtime->silenced_start != runtime->control->appl_ptr) { + n = runtime->control->appl_ptr - runtime->silenced_start; + if (n < 0) + n += runtime->boundary; + if ((snd_pcm_uframes_t)n < runtime->silenced_size) + runtime->silenced_size -= n; + else + runtime->silenced_size = 0; + runtime->silenced_start = runtime->control->appl_ptr; + } + if (runtime->silenced_size == runtime->buffer_size) + return; + snd_assert(runtime->silenced_size <= runtime->buffer_size, return); + noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size; + if (noise_dist > (snd_pcm_sframes_t) runtime->silence_threshold) + return; + frames = runtime->silence_threshold - noise_dist; + if (frames > runtime->silence_size) + frames = runtime->silence_size; + } else { + if (new_hw_ptr == ULONG_MAX) { /* initialization */ + runtime->silenced_size = 0; + runtime->silenced_start = runtime->control->appl_ptr; + } else { + ofs = runtime->status->hw_ptr; + frames = new_hw_ptr - ofs; + if ((snd_pcm_sframes_t)frames < 0) + frames += runtime->boundary; + runtime->silenced_size -= frames; + if ((snd_pcm_sframes_t)runtime->silenced_size < 0) { + runtime->silenced_size = 0; + runtime->silenced_start = (ofs + frames) - runtime->buffer_size; + } else { + runtime->silenced_start = ofs - runtime->silenced_size; + } + if ((snd_pcm_sframes_t)runtime->silenced_start < 0) + runtime->silenced_start += runtime->boundary; + } + frames = runtime->buffer_size; + } + snd_assert(frames >= runtime->silenced_size, return); + frames -= runtime->silenced_size; + if (frames == 0) + return; + ofs = (runtime->silenced_start + runtime->silenced_size) % runtime->buffer_size; + while (frames > 0) { + transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + if (substream->ops->silence) { + int err; + err = substream->ops->silence(substream, -1, ofs, transfer); + snd_assert(err >= 0, ); + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); + snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); + } + } else { + unsigned int c; + unsigned int channels = runtime->channels; + if (substream->ops->silence) { + for (c = 0; c < channels; ++c) { + int err; + err = substream->ops->silence(substream, c, ofs, transfer); + snd_assert(err >= 0, ); + } + } else { + size_t dma_csize = runtime->dma_bytes / channels; + for (c = 0; c < channels; ++c) { + char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); + snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); + } + } + } + runtime->silenced_size += transfer; + frames -= transfer; + ofs = 0; + } +} + +int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt; + snd_pcm_uframes_t avail; + snd_pcm_sframes_t delta; + + old_hw_ptr = runtime->status->hw_ptr; + pos = substream->ops->pointer(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec); +#ifdef CONFIG_SND_DEBUG + if (pos >= runtime->buffer_size) { + snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + } else +#endif + snd_runtime_check(pos < runtime->buffer_size, return 0); + + pos -= pos % runtime->min_align; + new_hw_ptr = runtime->hw_ptr_base + pos; + + hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; + + delta = hw_ptr_interrupt - new_hw_ptr; + if (delta > 0) { + if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { + snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); + return 0; + } + runtime->hw_ptr_base += runtime->buffer_size; + if (runtime->hw_ptr_base == runtime->boundary) + runtime->hw_ptr_base = 0; + new_hw_ptr = runtime->hw_ptr_base + pos; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream, new_hw_ptr); + + runtime->status->hw_ptr = new_hw_ptr; + runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail > runtime->avail_max) + runtime->avail_max = avail; + if (avail >= runtime->stop_threshold) { + snd_pcm_stop(substream, + runtime->status->state == SNDRV_PCM_STATE_DRAINING ? + SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); + return -EPIPE; + } + if (avail >= runtime->control->avail_min) + wake_up(&runtime->sleep); + return 0; +} + +/* CAUTION: call it with irq disabled */ +int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; + snd_pcm_uframes_t avail; + snd_pcm_sframes_t delta; + + old_hw_ptr = runtime->status->hw_ptr; + pos = substream->ops->pointer(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec); +#ifdef CONFIG_SND_DEBUG + if (pos >= runtime->buffer_size) { + snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + } else +#endif + snd_runtime_check(pos < runtime->buffer_size, return 0); + + pos -= pos % runtime->min_align; + new_hw_ptr = runtime->hw_ptr_base + pos; + + delta = old_hw_ptr - new_hw_ptr; + if (delta > 0) { + if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { + snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); + return 0; + } + runtime->hw_ptr_base += runtime->buffer_size; + if (runtime->hw_ptr_base == runtime->boundary) + runtime->hw_ptr_base = 0; + new_hw_ptr = runtime->hw_ptr_base + pos; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream, new_hw_ptr); + runtime->status->hw_ptr = new_hw_ptr; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail > runtime->avail_max) + runtime->avail_max = avail; + if (avail >= runtime->stop_threshold) { + snd_pcm_stop(substream, + runtime->status->state == SNDRV_PCM_STATE_DRAINING ? + SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); + return -EPIPE; + } + if (avail >= runtime->control->avail_min) + wake_up(&runtime->sleep); + return 0; +} + +/** + * snd_pcm_set_ops - set the PCM operators + * @pcm: the pcm instance + * @direction: stream direction, SNDRV_PCM_STREAM_XXX + * @ops: the operator table + * + * Sets the given PCM operators to the pcm instance. + */ +void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) +{ + snd_pcm_str_t *stream = &pcm->streams[direction]; + snd_pcm_substream_t *substream; + + for (substream = stream->substream; substream != NULL; substream = substream->next) + substream->ops = ops; +} + + +/** + * snd_pcm_sync - set the PCM sync id + * @substream: the pcm substream + * + * Sets the PCM sync identifier for the card. + */ +void snd_pcm_set_sync(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->sync.id32[0] = substream->pcm->card->number; + runtime->sync.id32[1] = -1; + runtime->sync.id32[2] = -1; + runtime->sync.id32[3] = -1; +} + +/* + * Standard ioctl routine + */ + +/* Code taken from alsa-lib */ +#define assert(a) snd_assert((a), return -EINVAL) + +static inline unsigned int div32(unsigned int a, unsigned int b, + unsigned int *r) +{ + if (b == 0) { + *r = 0; + return UINT_MAX; + } + *r = a % b; + return a / b; +} + +static inline unsigned int div_down(unsigned int a, unsigned int b) +{ + if (b == 0) + return UINT_MAX; + return a / b; +} + +static inline unsigned int div_up(unsigned int a, unsigned int b) +{ + unsigned int r; + unsigned int q; + if (b == 0) + return UINT_MAX; + q = div32(a, b, &r); + if (r) + ++q; + return q; +} + +static inline unsigned int mul(unsigned int a, unsigned int b) +{ + if (a == 0) + return 0; + if (div_down(UINT_MAX, a) < b) + return UINT_MAX; + return a * b; +} + +static inline unsigned int muldiv32(unsigned int a, unsigned int b, + unsigned int c, unsigned int *r) +{ + u_int64_t n = (u_int64_t) a * b; + if (c == 0) { + snd_assert(n > 0, ); + *r = 0; + return UINT_MAX; + } + div64_32(&n, c, r); + if (n >= UINT_MAX) { + *r = 0; + return UINT_MAX; + } + return n; +} + +int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->min < min) { + i->min = min; + i->openmin = openmin; + changed = 1; + } else if (i->min == min && !i->openmin && openmin) { + i->openmin = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->max > max) { + i->max = max; + i->openmax = openmax; + changed = 1; + } else if (i->max == max && !i->openmax && openmax) { + i->openmax = 1; + changed = 1; + } + if (i->integer) { + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +/** + * snd_interval_refine - refine the interval value of configurator + * @i: the interval value to refine + * @v: the interval value to refer to + * + * Refines the interval value with the reference value. + * The interval is changed to the range satisfying both intervals. + * The interval status (min, max, integer, etc.) are evaluated. + * + * Returns non-zero if the value is changed, zero if not changed. + */ +int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->min < v->min) { + i->min = v->min; + i->openmin = v->openmin; + changed = 1; + } else if (i->min == v->min && !i->openmin && v->openmin) { + i->openmin = 1; + changed = 1; + } + if (i->max > v->max) { + i->max = v->max; + i->openmax = v->openmax; + changed = 1; + } else if (i->max == v->max && !i->openmax && v->openmax) { + i->openmax = 1; + changed = 1; + } + if (!i->integer && v->integer) { + i->integer = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } else if (!i->openmin && !i->openmax && i->min == i->max) + i->integer = 1; + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +int snd_interval_refine_first(snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + if (snd_interval_single(i)) + return 0; + i->max = i->min; + i->openmax = i->openmin; + if (i->openmax) + i->max++; + return 1; +} + +int snd_interval_refine_last(snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + if (snd_interval_single(i)) + return 0; + i->min = i->max; + i->openmin = i->openmax; + if (i->openmin) + i->min--; + return 1; +} + +int snd_interval_refine_set(snd_interval_t *i, unsigned int val) +{ + snd_interval_t t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) +{ + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = mul(a->min, b->min); + c->openmin = (a->openmin || b->openmin); + c->max = mul(a->max, b->max); + c->openmax = (a->openmax || b->openmax); + c->integer = (a->integer && b->integer); +} + +/** + * snd_interval_div - refine the interval value with division + * + * c = a / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ +void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = div32(a->min, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = div32(a->max, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +/** + * snd_interval_muldivk - refine the interval value + * + * c = a * b / k + * + * Returns non-zero if the value is changed, zero if not changed. + */ +void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, + unsigned int k, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, b->min, k, &r); + c->openmin = (r || a->openmin || b->openmin); + c->max = muldiv32(a->max, b->max, k, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmax); + c->integer = 0; +} + +/** + * snd_interval_mulkdiv - refine the interval value + * + * c = a * k / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ +void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, + const snd_interval_t *b, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, k, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = muldiv32(a->max, k, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +#undef assert +/* ---- */ + + +/** + * snd_interval_ratnum - refine the interval value + * + * Returns non-zero if the value is changed, zero if not changed. + */ +int snd_interval_ratnum(snd_interval_t *i, + unsigned int rats_count, ratnum_t *rats, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + snd_interval_t t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->min; + int diff; + if (q == 0) + q = 1; + den = div_down(num, q); + if (den < rats[k].den_min) + continue; + if (den > rats[k].den_max) + den = rats[k].den_max; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den -= r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->max; + int diff; + if (q == 0) { + i->empty = 1; + return -EINVAL; + } + den = div_up(num, q); + if (den > rats[k].den_max) + continue; + if (den < rats[k].den_min) + den = rats[k].den_min; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den += rats[k].den_step - r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +/** + * snd_interval_ratden - refine the interval value + * + * Returns non-zero if the value is changed, zero if not changed. + */ +int snd_interval_ratden(snd_interval_t *i, + unsigned int rats_count, ratden_t *rats, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + snd_interval_t t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->min; + int diff; + num = mul(q, den); + if (num > rats[k].num_max) + continue; + if (num < rats[k].num_min) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num += rats[k].num_step - r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->max; + int diff; + num = mul(q, den); + if (num < rats[k].num_min) + continue; + if (num > rats[k].num_max) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num -= r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +/** + * snd_interval_list - refine the interval value from the list + * @i: the interval value to refine + * @count: the number of elements in the list + * @list: the value list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Returns non-zero if the value is changed, zero if not changed. + */ +int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) +{ + unsigned int k; + int changed = 0; + for (k = 0; k < count; k++) { + if (mask && !(mask & (1 << k))) + continue; + if (i->min == list[k] && !i->openmin) + goto _l1; + if (i->min < list[k]) { + i->min = list[k]; + i->openmin = 0; + changed = 1; + goto _l1; + } + } + i->empty = 1; + return -EINVAL; + _l1: + for (k = count; k-- > 0;) { + if (mask && !(mask & (1 << k))) + continue; + if (i->max == list[k] && !i->openmax) + goto _l2; + if (i->max > list[k]) { + i->max = list[k]; + i->openmax = 0; + changed = 1; + goto _l2; + } + } + i->empty = 1; + return -EINVAL; + _l2: + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) +{ + unsigned int n; + int changed = 0; + n = (i->min - min) % step; + if (n != 0 || i->openmin) { + i->min += step - n; + changed = 1; + } + n = (i->max - min) % step; + if (n != 0 || i->openmax) { + i->max -= n; + changed = 1; + } + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +/* Info constraints helpers */ + +/** + * snd_pcm_hw_rule_add - add the hw-constraint rule + * @runtime: the pcm runtime instance + * @cond: condition bits + * @var: the variable to evaluate + * @func: the evaluation function + * @private: the private data pointer passed to function + * @dep: the dependent variables + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_pcm_hw_rule_t *c; + unsigned int k; + va_list args; + va_start(args, dep); + if (constrs->rules_num >= constrs->rules_all) { + snd_pcm_hw_rule_t *old = constrs->rules; + if (constrs->rules_all == 0) + constrs->rules_all = 32; + else { + old = constrs->rules; + constrs->rules_all += 10; + } + constrs->rules = snd_kcalloc(constrs->rules_all * sizeof(*c), + GFP_KERNEL); + if (!constrs->rules) + return -ENOMEM; + if (old) { + memcpy(constrs->rules, old, + constrs->rules_num * sizeof(*c)); + kfree(old); + } + } + c = &constrs->rules[constrs->rules_num]; + c->cond = cond; + c->func = func; + c->var = var; + c->private = private; + k = 0; + while (1) { + snd_assert(k < sizeof(c->deps) / sizeof(c->deps[0]), return -EINVAL); + c->deps[k++] = dep; + if (dep < 0) + break; + dep = va_arg(args, int); + } + constrs->rules_num++; + va_end(args); + return 0; +} + +/** + * snd_pcm_hw_constraint_mask + */ +int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + u_int32_t mask) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_mask_t *maskp = constrs_mask(constrs, var); + *maskp->bits &= mask; + memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ + if (*maskp->bits == 0) + return -EINVAL; + return 0; +} + +/** + * snd_pcm_hw_constraint_mask64 + */ +int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + u_int64_t mask) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_mask_t *maskp = constrs_mask(constrs, var); + maskp->bits[0] &= (u_int32_t)mask; + maskp->bits[1] &= (u_int32_t)(mask >> 32); + memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ + if (! maskp->bits[0] && ! maskp->bits[1]) + return -EINVAL; + return 0; +} + +/** + * snd_pcm_hw_constraint_integer + */ +int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + return snd_interval_setinteger(constrs_interval(constrs, var)); +} + +/** + * snd_pcm_hw_constraint_minmax + */ +int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_interval_t t; + t.min = min; + t.max = max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(constrs_interval(constrs, var), &t); +} + +static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_list_t *list = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); +} + + +/** + * snd_pcm_hw_constraint_list + */ +int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_list_t *l) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_list, l, + var, -1); +} + +static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_ratnums_t *r = rule->private; + unsigned int num = 0, den = 0; + int err; + err = snd_interval_ratnum(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +/** + * snd_pcm_hw_constraint_ratnums + */ +int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratnums_t *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratnums, r, + var, -1); +} + +static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_ratdens_t *r = rule->private; + unsigned int num = 0, den = 0; + int err = snd_interval_ratden(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +/** + * snd_pcm_hw_constraint_ratdens + */ +int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratdens_t *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratdens, r, + var, -1); +} + +static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int l = (unsigned long) rule->private; + int width = l & 0xffff; + unsigned int msbits = l >> 16; + snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i) && snd_interval_value(i) == width) + params->msbits = msbits; + return 0; +} + +/** + * snd_pcm_hw_constraint_msbits + */ +int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits) +{ + unsigned long l = (msbits << 16) | width; + return snd_pcm_hw_rule_add(runtime, cond, -1, + snd_pcm_hw_rule_msbits, + (void*) l, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); +} + +static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned long step = (unsigned long) rule->private; + return snd_interval_step(hw_param_interval(params, rule->var), 0, step); +} + +/** + * snd_pcm_hw_constraint_step + */ +int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_step, (void *) step, + var, -1); +} + +static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) +{ + static int pow2_sizes[] = { + 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, + 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, + 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, + 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 + }; + return snd_interval_list(hw_param_interval(params, rule->var), + sizeof(pow2_sizes)/sizeof(int), pow2_sizes, 0); +} + +/** + * snd_pcm_hw_constraint_pow2 + */ +int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_pow2, NULL, + var, -1); +} + +/* To use the same code we have in alsa-lib */ +#define snd_pcm_t snd_pcm_substream_t +#define assert(i) snd_assert((i), return -EINVAL) +#ifndef INT_MIN +#define INT_MIN ((int)((unsigned int)INT_MAX+1)) +#endif + +void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) +{ + if (hw_is_mask(var)) { + snd_mask_any(hw_param_mask(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + return; + } + if (hw_is_interval(var)) { + snd_interval_any(hw_param_interval(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + return; + } + snd_BUG(); +} + +/** + * snd_pcm_hw_param_any + */ +int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + _snd_pcm_hw_param_any(params, var); + return snd_pcm_hw_refine(pcm, params); +} + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) +{ + unsigned int k; + memset(params, 0, sizeof(*params)); + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) + _snd_pcm_hw_param_any(params, k); + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + _snd_pcm_hw_param_any(params, k); + params->info = ~0U; +} + +/** + * snd_pcm_hw_params_any + * + * Fill PARAMS with full configuration space boundaries + */ +int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + _snd_pcm_hw_params_any(params); + return snd_pcm_hw_refine(pcm, params); +} + +/** + * snd_pcm_hw_param_value + * + * Return the value for field PAR if it's fixed in configuration space + * defined by PARAMS. Return -EINVAL otherwise + */ +int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + const snd_mask_t *mask = hw_param_mask_c(params, var); + if (!snd_mask_single(mask)) + return -EINVAL; + if (dir) + *dir = 0; + return snd_mask_value(mask); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (!snd_interval_single(i)) + return -EINVAL; + if (dir) + *dir = i->openmin; + return snd_interval_value(i); + } + assert(0); + return -EINVAL; +} + +/** + * snd_pcm_hw_param_value_min + * + * Return the minimum value for field PAR. + */ +unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_min(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (dir) + *dir = i->openmin; + return snd_interval_min(i); + } + assert(0); + return -EINVAL; +} + +/** + * snd_pcm_hw_param_value_max + * + * Return the maximum value for field PAR. + */ +unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_max(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (dir) + *dir = - (int) i->openmax; + return snd_interval_max(i); + } + assert(0); + return -EINVAL; +} + +void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + if (hw_is_mask(var)) { + snd_mask_none(hw_param_mask(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } else if (hw_is_interval(var)) { + snd_interval_none(hw_param_interval(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } else { + snd_BUG(); + } +} + +int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + assert(hw_is_interval(var)); + changed = snd_interval_setinteger(hw_param_interval(params, var)); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_setinteger + * + * Inside configuration space defined by PARAMS remove from PAR all + * non integer values. Reduce configuration space accordingly. + * Return -EINVAL if the configuration space is empty + */ +int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed = _snd_pcm_hw_param_setinteger(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + if (hw_is_mask(var)) + changed = snd_mask_refine_first(hw_param_mask(params, var)); + else if (hw_is_interval(var)) + changed = snd_interval_refine_first(hw_param_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + + +/** + * snd_pcm_hw_param_first + * + * Inside configuration space defined by PARAMS remove from PAR all + * values > minimum. Reduce configuration space accordingly. + * Return the minimum. + */ +int snd_pcm_hw_param_first(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + int changed = _snd_pcm_hw_param_first(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_param_value(params, var, dir); +} + +int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + if (hw_is_mask(var)) + changed = snd_mask_refine_last(hw_param_mask(params, var)); + else if (hw_is_interval(var)) + changed = snd_interval_refine_last(hw_param_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + + +/** + * snd_pcm_hw_param_last + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < maximum. Reduce configuration space accordingly. + * Return the maximum. + */ +int snd_pcm_hw_param_last(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + int changed = _snd_pcm_hw_param_last(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_param_value(params, var, dir); +} + +int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir > 0) { + open = 1; + } else if (dir < 0) { + if (val > 0) { + open = 1; + val--; + } + } + } + if (hw_is_mask(var)) + changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open); + else if (hw_is_interval(var)) + changed = snd_interval_refine_min(hw_param_interval(params, var), val, open); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_min + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < VAL. Reduce configuration space accordingly. + * Return new minimum or -EINVAL if the configuration space is empty + */ +int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int *dir) +{ + int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_min(params, var, dir); +} + +int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir < 0) { + open = 1; + } else if (dir > 0) { + open = 1; + val++; + } + } + if (hw_is_mask(var)) { + if (val == 0 && open) { + snd_mask_none(hw_param_mask(params, var)); + changed = -EINVAL; + } else + changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open); + } else if (hw_is_interval(var)) + changed = snd_interval_refine_max(hw_param_interval(params, var), val, open); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_max + * + * Inside configuration space defined by PARAMS remove from PAR all + * values >= VAL + 1. Reduce configuration space accordingly. + * Return new maximum or -EINVAL if the configuration space is empty + */ +int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int *dir) +{ + int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_max(params, var, dir); +} + +int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + if (hw_is_mask(var)) { + snd_mask_t *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set(hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + snd_interval_t *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + snd_interval_t t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_set + * + * Inside configuration space defined by PARAMS remove from PAR all + * values != VAL. Reduce configuration space accordingly. + * Return VAL or -EINVAL if the configuration space is empty + */ +int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed = _snd_pcm_hw_param_set(params, var, val, dir); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value(params, var, 0); +} + +int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) +{ + int changed; + assert(hw_is_mask(var)); + changed = snd_mask_refine(hw_param_mask(params, var), val); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_mask + * + * Inside configuration space defined by PARAMS remove from PAR all values + * not contained in MASK. Reduce configuration space accordingly. + * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return 0 on success or -EINVAL + * if the configuration space is empty + */ +int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) +{ + int changed = _snd_pcm_hw_param_mask(params, var, val); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +static int boundary_sub(int a, int adir, + int b, int bdir, + int *c, int *cdir) +{ + adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); + bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); + *c = a - b; + *cdir = adir - bdir; + if (*cdir == -2) { + assert(*c > INT_MIN); + (*c)--; + } else if (*cdir == 2) { + assert(*c < INT_MAX); + (*c)++; + } + return 0; +} + +static int boundary_lt(unsigned int a, int adir, + unsigned int b, int bdir) +{ + assert(a > 0 || adir >= 0); + assert(b > 0 || bdir >= 0); + if (adir < 0) { + a--; + adir = 1; + } else if (adir > 0) + adir = 1; + if (bdir < 0) { + b--; + bdir = 1; + } else if (bdir > 0) + bdir = 1; + return a < b || (a == b && adir < bdir); +} + +/* Return 1 if min is nearer to best than max */ +static int boundary_nearer(int min, int mindir, + int best, int bestdir, + int max, int maxdir) +{ + int dmin, dmindir; + int dmax, dmaxdir; + boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); + boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); + return boundary_lt(dmin, dmindir, dmax, dmaxdir); +} + +/** + * snd_pcm_hw_param_near + * + * Inside configuration space defined by PARAMS set PAR to the available value + * nearest to VAL. Reduce configuration space accordingly. + * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return the value found. + */ +int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int best, int *dir) +{ + snd_pcm_hw_params_t *save = NULL; + int v; + unsigned int saved_min; + int last = 0; + int min, max; + int mindir, maxdir; + int valdir = dir ? *dir : 0; + /* FIXME */ + if (best > INT_MAX) + best = INT_MAX; + min = max = best; + mindir = maxdir = valdir; + if (maxdir > 0) + maxdir = 0; + else if (maxdir == 0) + maxdir = -1; + else { + maxdir = 1; + max--; + } + save = kmalloc(sizeof(*save), GFP_KERNEL); + if (save == NULL) + return -ENOMEM; + *save = *params; + saved_min = min; + min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); + if (min >= 0) { + snd_pcm_hw_params_t *params1; + if (max < 0) + goto _end; + if ((unsigned int)min == saved_min && mindir == valdir) + goto _end; + params1 = kmalloc(sizeof(*params1), GFP_KERNEL); + if (params1 == NULL) { + kfree(save); + return -ENOMEM; + } + *params1 = *save; + max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); + if (max < 0) { + kfree(params1); + goto _end; + } + if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { + *params = *params1; + last = 1; + } + kfree(params1); + } else { + *params = *save; + max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); + assert(max >= 0); + last = 1; + } + _end: + kfree(save); + if (last) + v = snd_pcm_hw_param_last(pcm, params, var, dir); + else + v = snd_pcm_hw_param_first(pcm, params, var, dir); + assert(v >= 0); + return v; +} + +/** + * snd_pcm_hw_param_choose + * + * Choose one configuration from configuration space defined by PARAMS + * The configuration chosen is that obtained fixing in this order: + * first access, first format, first subformat, min channels, + * min rate, min period time, max buffer size, min tick time + */ +int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, 0); + assert(err >= 0); + + return 0; +} + +#undef snd_pcm_t +#undef assert + +static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream, + void *arg) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (snd_pcm_running(substream) && + snd_pcm_update_hw_ptr(substream) >= 0) { + runtime->status->hw_ptr %= runtime->buffer_size; + return 0; + } + runtime->status->hw_ptr = 0; + return 0; +} + +static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, + void *arg) +{ + snd_pcm_channel_info_t *info = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + int width; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { + info->offset = -1; + return 0; + } + width = snd_pcm_format_physical_width(runtime->format); + if (width < 0) + return width; + info->offset = 0; + switch (runtime->access) { + case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: + case SNDRV_PCM_ACCESS_RW_INTERLEAVED: + info->first = info->channel * width; + info->step = runtime->channels * width; + break; + case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: + case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: + { + size_t size = runtime->dma_bytes / runtime->channels; + info->first = info->channel * size * 8; + info->step = width; + break; + } + default: + snd_BUG(); + break; + } + return 0; +} + +/** + * snd_pcm_lib_ioctl - a generic PCM ioctl callback + * @substream: the pcm substream instance + * @cmd: ioctl command + * @arg: ioctl argument + * + * Processes the generic ioctl commands for PCM. + * Can be passed as the ioctl callback for PCM ops. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_INFO: + return 0; + case SNDRV_PCM_IOCTL1_RESET: + return snd_pcm_lib_ioctl_reset(substream, arg); + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + return snd_pcm_lib_ioctl_channel_info(substream, arg); + } + return -ENXIO; +} + +/* + * Conditions + */ + +/** + * snd_pcm_playback_ready - check whether the playback buffer is available + * @substream: the pcm substream instance + * + * Checks whether enough free space is available on the playback buffer. + * + * Returns non-zero if available, or zero if not. + */ +int snd_pcm_playback_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min; +} + +/** + * snd_pcm_capture_ready - check whether the capture buffer is available + * @substream: the pcm substream instance + * + * Checks whether enough capture data is available on the capture buffer. + * + * Returns non-zero if available, or zero if not. + */ +int snd_pcm_capture_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min; +} + +/** + * snd_pcm_playback_data - check whether any data exists on the playback buffer + * @substream: the pcm substream instance + * + * Checks whether any data exists on the playback buffer. If stop_threshold + * is bigger or equal to boundary, then this function returns always non-zero. + * + * Returns non-zero if exists, or zero if not. + */ +int snd_pcm_playback_data(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + if (runtime->stop_threshold >= runtime->boundary) + return 1; + return snd_pcm_playback_avail(runtime) < runtime->buffer_size; +} + +/** + * snd_pcm_playback_empty - check whether the playback buffer is empty + * @substream: the pcm substream instance + * + * Checks whether the playback buffer is empty. + * + * Returns non-zero if empty, or zero if not. + */ +int snd_pcm_playback_empty(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) >= runtime->buffer_size; +} + +/** + * snd_pcm_capture_empty - check whether the capture buffer is empty + * @substream: the pcm substream instance + * + * Checks whether the capture buffer is empty. + * + * Returns non-zero if empty, or zero if not. + */ +int snd_pcm_capture_empty(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_capture_avail(runtime) == 0; +} + +static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream, + unsigned long ticks) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (ticks == 0) + del_timer(&runtime->tick_timer); + else { + ticks /= (1000000 / HZ); + if (ticks % (1000000 / HZ)) + ticks++; + mod_timer(&runtime->tick_timer, jiffies + ticks); + } +} + +/* Temporary alias */ +void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks) +{ + snd_pcm_system_tick_set(substream, ticks); +} + +void snd_pcm_tick_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frames = ULONG_MAX; + snd_pcm_uframes_t avail, dist; + unsigned int ticks; + u_int64_t n; + u_int32_t r; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (runtime->silence_size >= runtime->boundary) { + frames = 1; + } else if (runtime->silence_size > 0 && + runtime->silenced_size < runtime->buffer_size) { + snd_pcm_sframes_t noise_dist; + noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size; + snd_assert(noise_dist <= (snd_pcm_sframes_t)runtime->silence_threshold, ); + frames = noise_dist - runtime->silence_threshold; + } + avail = snd_pcm_playback_avail(runtime); + } else { + avail = snd_pcm_capture_avail(runtime); + } + if (avail < runtime->control->avail_min) { + snd_pcm_sframes_t n = runtime->control->avail_min - avail; + if (n > 0 && frames > (snd_pcm_uframes_t)n) + frames = n; + } + if (avail < runtime->buffer_size) { + snd_pcm_sframes_t n = runtime->buffer_size - avail; + if (n > 0 && frames > (snd_pcm_uframes_t)n) + frames = n; + } + if (frames == ULONG_MAX) { + snd_pcm_tick_set(substream, 0); + return; + } + dist = runtime->status->hw_ptr - runtime->hw_ptr_base; + /* Distance to next interrupt */ + dist = runtime->period_size - dist % runtime->period_size; + if (dist <= frames) { + snd_pcm_tick_set(substream, 0); + return; + } + /* the base time is us */ + n = frames; + n *= 1000000; + div64_32(&n, runtime->tick_time * runtime->rate, &r); + ticks = n + (r > 0 ? 1 : 0); + if (ticks < runtime->sleep_min) + ticks = runtime->sleep_min; + snd_pcm_tick_set(substream, (unsigned long) ticks); +} + +void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return); + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + + spin_lock_irq(&runtime->lock); + if (!snd_pcm_running(substream) || + snd_pcm_update_hw_ptr(substream) < 0) + goto _end; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + _end: + spin_unlock_irq(&runtime->lock); +} + +/** + * snd_pcm_period_elapsed - update the pcm status for the next period + * @substream: the pcm substream instance + * + * This function is called from the interrupt handler when the + * PCM has processed the period size. It will update the current + * pointer, set up the tick, wake up sleepers, etc. + * + * Even if more than one periods have elapsed since the last call, you + * have to call this only once. + */ +void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return); + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + + if (runtime->transfer_ack_begin) + runtime->transfer_ack_begin(substream); + + spin_lock(&runtime->lock); + if (!snd_pcm_running(substream) || + snd_pcm_update_hw_ptr_interrupt(substream) < 0) + goto _end; + + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + _end: + spin_unlock(&runtime->lock); + if (runtime->transfer_ack_end) + runtime->transfer_ack_end(substream); + kill_fasync(&runtime->fasync, SIGIO, POLL_IN); +} + +static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + char *buf = (char *) data + frames_to_bytes(runtime, off); + if (substream->ops->copy) { + if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) + return err; + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + snd_assert(runtime->dma_area, return -EFAULT); + if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) + return -EFAULT; + } + return 0; +} + +typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff, + void *data, unsigned int off, snd_pcm_uframes_t size); + +static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, + const void *data, snd_pcm_uframes_t size, + int nonblock, + transfer_f transfer) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + int err = 0; + + if (size == 0) + return 0; + if (size > runtime->xfer_align) + size -= size % runtime->xfer_align; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + err = -EBADFD; + goto _end_unlock; + } + + while (size > 0) { + snd_pcm_uframes_t frames, appl_ptr, appl_ofs; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t cont; + if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_playback_avail(runtime); + if (((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align))) { + wait_queue_t wait; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + + if (nonblock) { + err = -EAGAIN; + goto _end_unlock; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && + runtime->status->state != SNDRV_PCM_STATE_PAUSED) { + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + } + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + state = ERROR; + goto _end_loop; + case SNDRV_PCM_STATE_SUSPENDED: + state = SUSPENDED; + goto _end_loop; + default: + break; + } + avail = snd_pcm_playback_avail(runtime); + if (avail >= runtime->control->avail_min) { + state = READY; + break; + } + } + _end_loop: + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case ERROR: + err = -EPIPE; + goto _end_unlock; + case SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + case SIGNALED: + err = -ERESTARTSYS; + goto _end_unlock; + case EXPIRED: + snd_printd("playback write error (DMA or IRQ trouble?)\n"); + err = -EIO; + goto _end_unlock; + default: + break; + } + } + if (avail > runtime->xfer_align) + avail -= avail % runtime->xfer_align; + frames = size > avail ? avail : size; + cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + if (frames > cont) + frames = cont; + snd_assert(frames != 0, + spin_unlock_irq(&runtime->lock); + return -EINVAL); + appl_ptr = runtime->control->appl_ptr; + appl_ofs = appl_ptr % runtime->buffer_size; + spin_unlock_irq(&runtime->lock); + if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0) + goto _end; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + break; + } + appl_ptr += frames; + if (appl_ptr >= runtime->boundary) { + runtime->control->appl_ptr = 0; + } else { + runtime->control->appl_ptr = appl_ptr; + } + if (substream->ops->ack) + substream->ops->ack(substream); + + offset += frames; + size -= frames; + xfer += frames; + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && + snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } + if (runtime->sleep_min && + runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_tick_prepare(substream); + } + _end_unlock: + spin_unlock_irq(&runtime->lock); + _end: + return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; +} + +snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void *buf, snd_pcm_uframes_t size) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + return snd_pcm_lib_write1(substream, buf, size, nonblock, + snd_pcm_lib_write_transfer); +} + +static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + void **bufs = data; + int channels = runtime->channels; + int c; + if (substream->ops->copy) { + snd_assert(substream->ops->silence != NULL, return -EINVAL); + for (c = 0; c < channels; ++c, ++bufs) { + if (*bufs == NULL) { + if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) + return err; + } else { + char *buf = *bufs + samples_to_bytes(runtime, off); + if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) + return err; + } + } + } else { + /* default transfer behaviour */ + size_t dma_csize = runtime->dma_bytes / channels; + snd_assert(runtime->dma_area, return -EFAULT); + for (c = 0; c < channels; ++c, ++bufs) { + char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); + if (*bufs == NULL) { + snd_pcm_format_set_silence(runtime->format, hwbuf, frames); + } else { + char *buf = *bufs + samples_to_bytes(runtime, off); + if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, void **bufs, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + return snd_pcm_lib_write1(substream, bufs, frames, nonblock, + snd_pcm_lib_writev_transfer); +} + +static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + char *buf = (char *) data + frames_to_bytes(runtime, off); + if (substream->ops->copy) { + if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) + return err; + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + snd_assert(runtime->dma_area, return -EFAULT); + if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) + return -EFAULT; + } + return 0; +} + +static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void *data, snd_pcm_uframes_t size, int nonblock, + transfer_f transfer) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + int err = 0; + + if (size == 0) + return 0; + if (size > runtime->xfer_align) + size -= size % runtime->xfer_align; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + if (size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + err = -EBADFD; + goto _end_unlock; + } + + while (size > 0) { + snd_pcm_uframes_t frames, appl_ptr, appl_ofs; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t cont; + if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_capture_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + if (avail < runtime->xfer_align) { + err = -EPIPE; + goto _end_unlock; + } + } else if ((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align)) { + wait_queue_t wait; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + + if (nonblock) { + err = -EAGAIN; + goto _end_unlock; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && + runtime->status->state != SNDRV_PCM_STATE_PAUSED) { + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + } + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + state = ERROR; + goto _end_loop; + case SNDRV_PCM_STATE_SUSPENDED: + state = SUSPENDED; + goto _end_loop; + default: + break; + } + avail = snd_pcm_capture_avail(runtime); + if (avail >= runtime->control->avail_min) { + state = READY; + break; + } + } + _end_loop: + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case ERROR: + err = -EPIPE; + goto _end_unlock; + case SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + case SIGNALED: + err = -ERESTARTSYS; + goto _end_unlock; + case EXPIRED: + snd_printd("capture read error (DMA or IRQ trouble?)\n"); + err = -EIO; + goto _end_unlock; + default: + break; + } + } + if (avail > runtime->xfer_align) + avail -= avail % runtime->xfer_align; + frames = size > avail ? avail : size; + cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + if (frames > cont) + frames = cont; + snd_assert(frames != 0, + spin_unlock_irq(&runtime->lock); + return -EINVAL); + appl_ptr = runtime->control->appl_ptr; + appl_ofs = appl_ptr % runtime->buffer_size; + spin_unlock_irq(&runtime->lock); + if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0) + goto _end; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + break; + } + appl_ptr += frames; + if (appl_ptr >= runtime->boundary) { + runtime->control->appl_ptr = 0; + } else { + runtime->control->appl_ptr = appl_ptr; + } + if (substream->ops->ack) + substream->ops->ack(substream); + + offset += frames; + size -= frames; + xfer += frames; + if (runtime->sleep_min && + runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_tick_prepare(substream); + } + _end_unlock: + spin_unlock_irq(&runtime->lock); + _end: + return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; +} + +snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void *buf, snd_pcm_uframes_t size) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) + return -EINVAL; + return snd_pcm_lib_read1(substream, buf, size, nonblock, snd_pcm_lib_read_transfer); +} + +static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + void **bufs = data; + int channels = runtime->channels; + int c; + if (substream->ops->copy) { + for (c = 0; c < channels; ++c, ++bufs) { + char *buf; + if (*bufs == NULL) + continue; + buf = *bufs + samples_to_bytes(runtime, off); + if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) + return err; + } + } else { + snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; + snd_assert(runtime->dma_area, return -EFAULT); + for (c = 0; c < channels; ++c, ++bufs) { + char *hwbuf, *buf; + if (*bufs == NULL) + continue; + + hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); + buf = *bufs + samples_to_bytes(runtime, off); + if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + return snd_pcm_lib_read1(substream, bufs, frames, nonblock, snd_pcm_lib_readv_transfer); +} + +/* + * Exported symbols + */ + +EXPORT_SYMBOL(snd_interval_refine); +EXPORT_SYMBOL(snd_interval_list); +EXPORT_SYMBOL(snd_interval_ratnum); +EXPORT_SYMBOL(snd_interval_ratden); +EXPORT_SYMBOL(snd_interval_muldivk); +EXPORT_SYMBOL(snd_interval_mulkdiv); +EXPORT_SYMBOL(snd_interval_div); +EXPORT_SYMBOL(_snd_pcm_hw_params_any); +EXPORT_SYMBOL(_snd_pcm_hw_param_min); +EXPORT_SYMBOL(_snd_pcm_hw_param_set); +EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); +EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger); +EXPORT_SYMBOL(snd_pcm_hw_param_value_min); +EXPORT_SYMBOL(snd_pcm_hw_param_value_max); +EXPORT_SYMBOL(snd_pcm_hw_param_mask); +EXPORT_SYMBOL(snd_pcm_hw_param_first); +EXPORT_SYMBOL(snd_pcm_hw_param_last); +EXPORT_SYMBOL(snd_pcm_hw_param_near); +EXPORT_SYMBOL(snd_pcm_hw_param_set); +EXPORT_SYMBOL(snd_pcm_hw_refine); +EXPORT_SYMBOL(snd_pcm_hw_constraints_init); +EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); +EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +EXPORT_SYMBOL(snd_pcm_hw_constraint_step); +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); +EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); +EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); +EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); +EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); +EXPORT_SYMBOL(snd_pcm_hw_rule_add); +EXPORT_SYMBOL(snd_pcm_set_ops); +EXPORT_SYMBOL(snd_pcm_set_sync); +EXPORT_SYMBOL(snd_pcm_lib_ioctl); +EXPORT_SYMBOL(snd_pcm_playback_ready); +EXPORT_SYMBOL(snd_pcm_capture_ready); +EXPORT_SYMBOL(snd_pcm_playback_data); +EXPORT_SYMBOL(snd_pcm_capture_empty); +EXPORT_SYMBOL(snd_pcm_stop); +EXPORT_SYMBOL(snd_pcm_period_elapsed); +EXPORT_SYMBOL(snd_pcm_lib_write); +EXPORT_SYMBOL(snd_pcm_lib_read); +EXPORT_SYMBOL(snd_pcm_lib_writev); +EXPORT_SYMBOL(snd_pcm_lib_readv); +EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes); +EXPORT_SYMBOL(snd_pcm_lib_period_bytes); +/* pcm_memory.c */ +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); +EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); +EXPORT_SYMBOL(snd_pcm_lib_free_pages); +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all); +EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); +#endif +#ifdef CONFIG_SBUS +EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages_for_all); +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/pcm_memory.c linux/sound/core/pcm_memory.c --- linux-2.4.21-rc1.orig/sound/core/pcm_memory.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/pcm_memory.c 2003-03-05 07:54:00.000000000 -0700 @@ -0,0 +1,570 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static int preallocate_dma = 1; +MODULE_PARM(preallocate_dma, "i"); +MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized."); +MODULE_PARM_SYNTAX(preallocate_dma, SNDRV_BOOLEAN_TRUE_DESC); + +static int maximum_substreams = 4; +MODULE_PARM(maximum_substreams, "i"); +MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory."); +MODULE_PARM_SYNTAX(maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC); + +const static size_t snd_minimum_buffer = 16384; + + +/* + * try to allocate as the large pages as possible. + * stores the resultant memory size in *res_size. + * + * the minimum size is snd_minimum_buffer. it should be power of 2. + */ +static int preallocate_pcm_pages(snd_pcm_substream_t *substream, size_t size) +{ + struct snd_dma_buffer *dmab = &substream->dma_buffer; + int err; + + snd_assert(size > 0, return -EINVAL); + + /* already reserved? */ + if (snd_dma_get_reserved(&substream->dma_device, dmab) > 0) { + if (dmab->bytes >= size) + return 0; /* yes */ + /* no, reset the reserved block */ + snd_dma_free_reserved(&substream->dma_device); + dmab->bytes = 0; + } + + do { + if ((err = snd_dma_alloc_pages(&substream->dma_device, size, dmab)) < 0) + return err; + if (dmab->area) { + /* remember this one */ + snd_dma_set_reserved(&substream->dma_device, dmab); + return 0; + } + size >>= 1; + } while (size >= snd_minimum_buffer); + dmab->bytes = 0; /* tell error */ + return 0; +} + +/* + * release the preallocated buffer if not yet done. + */ +static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +{ + if (substream->dma_buffer.area == NULL) + return; + snd_dma_free_reserved(&substream->dma_device); + substream->dma_buffer.area = NULL; +} + +/** + * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream. + * @substream: the pcm substream instance + * + * Releases the pre-allocated buffer of the given substream. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_preallocate_dma_free(substream); + if (substream->proc_prealloc_entry) { + snd_info_unregister(substream->proc_prealloc_entry); + substream->proc_prealloc_entry = NULL; + } + substream->dma_device.type = SNDRV_DMA_TYPE_UNKNOWN; + return 0; +} + +/** + * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm + * @pcm: the pcm instance + * + * Releases all the pre-allocated buffers on the given pcm. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + int stream; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_free(substream); + return 0; +} + +/* + * read callback for prealloc proc file + * + * prints the current allocated size in kB. + */ +static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024); +} + +/* + * write callback for prealloc proc file + * + * accepts the preallocation size in kB. + */ +static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + char line[64], str[64]; + size_t size; + struct snd_dma_buffer new_dmab; + + if (substream->runtime) { + buffer->error = -EBUSY; + return; + } + if (!snd_info_get_line(buffer, line, sizeof(line))) { + snd_info_get_str(str, line, sizeof(str)); + size = simple_strtoul(str, NULL, 10) * 1024; + if ((size != 0 && size < 8192) || size > substream->dma_max) { + buffer->error = -EINVAL; + return; + } + if (substream->dma_buffer.bytes == size) + return; + memset(&new_dmab, 0, sizeof(new_dmab)); + if (size > 0) { + + if (snd_dma_alloc_pages(&substream->dma_device, size, &new_dmab) < 0 || + new_dmab.area == NULL) { + buffer->error = -ENOMEM; + return; + } + substream->buffer_bytes_max = size; + } else { + substream->buffer_bytes_max = UINT_MAX; + } + snd_dma_set_reserved(&substream->dma_device, &new_dmab); + substream->dma_buffer = new_dmab; + } else { + buffer->error = -EINVAL; + } +} + +/* + * pre-allocate the buffer and create a proc file for the substream + */ +static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + snd_info_entry_t *entry; + + memset(&substream->dma_buffer, 0, sizeof(substream->dma_buffer)); + if (size > 0 && preallocate_dma && substream->number < maximum_substreams) + preallocate_pcm_pages(substream, size); + + if (substream->dma_buffer.bytes > 0) + substream->buffer_bytes_max = substream->dma_buffer.bytes; + substream->dma_max = max; + if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { + entry->c.text.read_size = 64; + entry->c.text.read = snd_pcm_lib_preallocate_proc_read; + entry->c.text.write_size = 64; + entry->c.text.write = snd_pcm_lib_preallocate_proc_write; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_prealloc_entry = entry; + return 0; +} + + +/* + * set up the unique pcm id + */ +static inline void setup_pcm_id(snd_pcm_substream_t *subs) +{ + subs->dma_device.id = subs->pcm->device << 16 | + subs->stream << 8 | subs->number; +} + +/** + * snd_pcm_lib_preallocate_pages - pre-allocation for the continuous memory type + * @substream: the pcm substream instance + * @size: the requested pre-allocation size in bytes + * @max: the max. allowed pre-allocation size + * @flags: allocation condition, GFP_XXX + * + * Do pre-allocation for the continuous memory type. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, + size_t size, size_t max, + unsigned int flags) +{ + substream->dma_device.type = SNDRV_DMA_TYPE_CONTINUOUS; + substream->dma_device.dev.flags = flags; + setup_pcm_id(substream); + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +/** + * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * @flags: allocation condition, GFP_XXX + * + * Do pre-allocation to all substreams of the given pcm for the + * continuous memory type. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max, + unsigned int flags) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_pages(substream, size, max, flags)) < 0) + return err; + return 0; +} + +#ifdef CONFIG_ISA +/** + * snd_pcm_lib_preallocate_isa_pages - pre-allocation for the ISA bus + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the ISA bus. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_device.type = SNDRV_DMA_TYPE_ISA; + substream->dma_device.dev.flags = 0; /* FIXME: any good identifier? */ + setup_pcm_id(substream); + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_isa_pages_for_all - pre-allocation for the ISA bus (all substreams) + * @pcm: pcm to assign the buffer + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * ISA bus. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_isa_pages(substream, size, max)) < 0) + return err; + return 0; +} +#endif /* CONFIG_ISA */ + +/** + * snd_pcm_lib_malloc_pages - allocate the DMA buffer + * @substream: the substream to allocate the DMA buffer to + * @size: the requested buffer size in bytes + * + * Allocates the DMA buffer on the BUS type given by + * snd_pcm_lib_preallocate_xxx_pages(). + * + * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * code on failure. + */ +int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) +{ + snd_pcm_runtime_t *runtime; + struct snd_dma_buffer dmab; + + snd_assert(substream->dma_device.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL); + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + + if (runtime->dma_area != NULL) { + /* perphaps, we might free the large DMA memory region + to save some space here, but the actual solution + costs us less time */ + if (runtime->dma_bytes >= size) + return 0; /* ok, do not change */ + snd_pcm_lib_free_pages(substream); + } + if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) { + dmab = substream->dma_buffer; + } else { + memset(&dmab, 0, sizeof(dmab)); + snd_dma_alloc_pages(&substream->dma_device, size, &dmab); + } + if (! dmab.area) + return -ENOMEM; + runtime->dma_area = dmab.area; + runtime->dma_addr = dmab.addr; + runtime->dma_private = dmab.private_data; + runtime->dma_bytes = size; + return 1; /* area was changed */ +} + +/** + * snd_pcm_lib_free_pages - release the allocated DMA buffer. + * @substream: the substream to release the DMA buffer + * + * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + if (runtime->dma_area == NULL) + return 0; + if (runtime->dma_area != substream->dma_buffer.area) { + struct snd_dma_buffer dmab; + memset(&dmab, 0, sizeof(dmab)); + dmab.area = runtime->dma_area; + dmab.addr = runtime->dma_addr; + dmab.bytes = runtime->dma_bytes; + dmab.private_data = runtime->dma_private; + snd_dma_free_pages(&substream->dma_device, &dmab); + } + runtime->dma_area = NULL; + runtime->dma_addr = 0UL; + runtime->dma_bytes = 0; + runtime->dma_private = NULL; + return 0; +} + +#ifdef CONFIG_PCI +/** + * snd_pcm_lib_preallocate_pci_pages - pre-allocation for the PCI bus + * + * @pci: pci device + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_device.type = SNDRV_DMA_TYPE_PCI; + substream->dma_device.dev.pci = pci; + setup_pcm_id(substream); + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_pci_pages_for_all - pre-allocation for the PCI bus (all substreams) + * @pci: pci device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_pci_pages(pci, substream, size, max)) < 0) + return err; + return 0; +} + +#endif /* CONFIG_PCI */ + +#ifdef CONFIG_SBUS +/** + * snd_pcm_lib_preallocate_sbus_pages - pre-allocation for the SBUS bus + * + * @sbus: SBUS device + * @substream: substream to assign the buffer + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the SBUS. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, + snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_device.type = SNDRV_DMA_TYPE_SBUS; + substream->dma_device.dev.sbus = sdev; + setup_pcm_id(substream); + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_sbus_pages_for_all - pre-allocation for the SBUS bus (all substreams) + * @sbus: SBUS device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * SUBS. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, + snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_sbus_pages(sdev, substream, size, max)) < 0) + return err; + return 0; +} + +#endif /* CONFIG_SBUS */ + + +#ifdef CONFIG_PCI +/** + * snd_pcm_lib_preallocate_sg_pages - initialize SG-buffer for the PCI bus + * + * @pci: pci device + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Initializes SG-buffer for the PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_device.type = SNDRV_DMA_TYPE_PCI_SG; + substream->dma_device.dev.pci = pci; + setup_pcm_id(substream); + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_sg_pages_for_all - initialize SG-buffer for the PCI bus (all substreams) + * @pci: pci device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Initialize the SG-buffer to all substreams of the given pcm for the + * PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_sg_pages(pci, substream, size, max)) < 0) + return err; + return 0; +} + +/** + * snd_pcm_sgbuf_ops_page - get the page struct at the given offset + * @substream: the pcm substream instance + * @offset: the buffer offset + * + * Returns the page struct at the given buffer offset. + * Used as the page callback of PCM ops. + */ +struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) +{ + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); + + unsigned int idx = offset >> PAGE_SHIFT; + if (idx >= (unsigned int)sgbuf->pages) + return NULL; + return sgbuf->page_table[idx]; +} + +#endif /* CONFIG_PCI */ diff -urN linux-2.4.21-rc1.orig/sound/core/pcm_misc.c linux/sound/core/pcm_misc.c --- linux-2.4.21-rc1.orig/sound/core/pcm_misc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/pcm_misc.c 2003-02-16 13:41:32.000000000 -0700 @@ -0,0 +1,652 @@ +/* + * PCM Interface - misc routines + * Copyright (c) 1998 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#define bswap_16 swab16 +#define bswap_32 swab32 +#define bswap_64 swab64 +#define SND_PCM_FORMAT_UNKNOWN (-1) +#define snd_enum_to_int(v) (v) +#define snd_int_to_enum(v) (v) + +/** + * snd_pcm_format_signed - Check the PCM format is signed linear + * @format: the format to check + * + * Returns 1 if the given PCM format is signed linear, 0 if unsigned + * linear, and a negative error code for non-linear formats. + */ +int snd_pcm_format_signed(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + return 1; + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + return 0; + default: + return -EINVAL; + } +} + +/** + * snd_pcm_format_unsigned - Check the PCM format is unsigned linear + * @format: the format to check + * + * Returns 1 if the given PCM format is unsigned linear, 0 if signed + * linear, and a negative error code for non-linear formats. + */ +int snd_pcm_format_unsigned(snd_pcm_format_t format) +{ + int val; + + val = snd_pcm_format_signed(format); + if (val < 0) + return val; + return !val; +} + +/** + * snd_pcm_format_linear - Check the PCM format is linear + * @format: the format to check + * + * Returns 1 if the given PCM format is linear, 0 if not. + */ +int snd_pcm_format_linear(snd_pcm_format_t format) +{ + return snd_pcm_format_signed(format) >= 0; +} + +/** + * snd_pcm_format_little_endian - Check the PCM format is little-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is little-endian, 0 if + * big-endian, or a negative error code if endian not specified. + */ +int snd_pcm_format_little_endian(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U18_3LE: + return 1; + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_BE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U24_3BE: + case SNDRV_PCM_FORMAT_U20_3BE: + case SNDRV_PCM_FORMAT_U18_3BE: + return 0; + default: + return -EINVAL; + } +} + +/** + * snd_pcm_format_big_endian - Check the PCM format is big-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is big-endian, 0 if + * little-endian, or a negative error code if endian not specified. + */ +int snd_pcm_format_big_endian(snd_pcm_format_t format) +{ + int val; + + val = snd_pcm_format_little_endian(format); + if (val < 0) + return val; + return !val; +} + +/** + * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is CPU-endian, 0 if + * opposite, or a negative error code if endian not specified. + */ +int snd_pcm_format_cpu_endian(snd_pcm_format_t format) +{ +#ifdef SNDRV_LITTLE_ENDIAN + return snd_pcm_format_little_endian(format); +#else + return snd_pcm_format_big_endian(format); +#endif +} + +/** + * snd_pcm_format_width - return the bit-width of the format + * @format: the format to check + * + * Returns the bit-width of the format, or a negative error code + * if unknown format. + */ +int snd_pcm_format_width(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + return 18; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + return 20; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + return 24; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 24; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +/** + * snd_pcm_format_physical_width - return the physical bit-width of the format + * @format: the format to check + * + * Returns the physical bit-width of the format, or a negative error code + * if unknown format. + */ +int snd_pcm_format_physical_width(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + return 24; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +/** + * snd_pcm_format_size - return the byte size of samples on the given format + * @format: the format to check + * + * Returns the byte size of the given samples for the format, or a + * negative error code if unknown format. + */ +ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return samples; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return samples * 2; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + return samples * 3; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return samples * 8; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return samples; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + if (samples & 1) + return -EINVAL; + return samples / 2; + default: + return -EINVAL; + } +} + +/** + * snd_pcm_format_silence_64 - return the silent data in 64bit integer + * @format: the format to check + * + * Returns the silent data in 64bit integer for the given format. + */ +u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + return 0; + case SNDRV_PCM_FORMAT_U8: + return 0x8080808080808080ULL; +#ifdef SNDRV_LITTLE_ENDIAN + case SNDRV_PCM_FORMAT_U16_LE: + return 0x8000800080008000ULL; + case SNDRV_PCM_FORMAT_U24_LE: + return 0x0080000000800000ULL; + case SNDRV_PCM_FORMAT_U32_LE: + return 0x8000000080000000ULL; + case SNDRV_PCM_FORMAT_U16_BE: + return 0x0080008000800080ULL; + case SNDRV_PCM_FORMAT_U24_BE: + return 0x0000800000008000ULL; + case SNDRV_PCM_FORMAT_U32_BE: + return 0x0000008000000080ULL; + case SNDRV_PCM_FORMAT_U24_3LE: + return 0x0000800000800000ULL; + case SNDRV_PCM_FORMAT_U24_3BE: + return 0x0080000080000080ULL; + case SNDRV_PCM_FORMAT_U20_3LE: + return 0x0000080000080000ULL; + case SNDRV_PCM_FORMAT_U20_3BE: + return 0x0008000008000008ULL; + case SNDRV_PCM_FORMAT_U18_3LE: + return 0x0000020000020000ULL; + case SNDRV_PCM_FORMAT_U18_3BE: + return 0x0002000002000002ULL; +#else + case SNDRV_PCM_FORMAT_U16_LE: + return 0x0080008000800080ULL; + case SNDRV_PCM_FORMAT_U24_LE: + return 0x0000800000008000ULL; + case SNDRV_PCM_FORMAT_U32_LE: + return 0x0000008000000080ULL; + case SNDRV_PCM_FORMAT_U16_BE: + return 0x8000800080008000ULL; + case SNDRV_PCM_FORMAT_U24_BE: + return 0x0080000000800000ULL; + case SNDRV_PCM_FORMAT_U32_BE: + return 0x8000000080000000ULL; + case SNDRV_PCM_FORMAT_U24_3LE: + return 0x0080000080000080ULL; + case SNDRV_PCM_FORMAT_U24_3BE: + return 0x0000800000800000ULL; + case SNDRV_PCM_FORMAT_U20_3LE: + return 0x0008000008000008ULL; + case SNDRV_PCM_FORMAT_U20_3BE: + return 0x0000080000080000ULL; + case SNDRV_PCM_FORMAT_U18_3LE: + return 0x0002000002000002ULL; + case SNDRV_PCM_FORMAT_U18_3BE: + return 0x0000020000020000ULL; +#endif + case SNDRV_PCM_FORMAT_FLOAT_LE: + { + union { + float f; + u_int32_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return u.i; +#else + return bswap_32(u.i); +#endif + } + case SNDRV_PCM_FORMAT_FLOAT64_LE: + { + union { + double f; + u_int64_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return u.i; +#else + return bswap_64(u.i); +#endif + } + case SNDRV_PCM_FORMAT_FLOAT_BE: + { + union { + float f; + u_int32_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return bswap_32(u.i); +#else + return u.i; +#endif + } + case SNDRV_PCM_FORMAT_FLOAT64_BE: + { + union { + double f; + u_int64_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return bswap_64(u.i); +#else + return u.i; +#endif + } + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 0; + case SNDRV_PCM_FORMAT_MU_LAW: + return 0x7f7f7f7f7f7f7f7fULL; + case SNDRV_PCM_FORMAT_A_LAW: + return 0x5555555555555555ULL; + case SNDRV_PCM_FORMAT_IMA_ADPCM: /* special case */ + case SNDRV_PCM_FORMAT_MPEG: + case SNDRV_PCM_FORMAT_GSM: + case SNDRV_PCM_FORMAT_SPECIAL: + return 0; + default: + return -EINVAL; + } + return 0; +} + +u_int32_t snd_pcm_format_silence_32(snd_pcm_format_t format) +{ + return (u_int32_t)snd_pcm_format_silence_64(format); +} + +u_int16_t snd_pcm_format_silence_16(snd_pcm_format_t format) +{ + return (u_int16_t)snd_pcm_format_silence_64(format); +} + +u_int8_t snd_pcm_format_silence(snd_pcm_format_t format) +{ + return (u_int8_t)snd_pcm_format_silence_64(format); +} + +/** + * snd_pcm_format_set_silence - set the silence data on the buffer + * @format: the PCM format + * @data: the buffer pointer + * @samples: the number of samples to set silence + * + * Sets the silence data on the buffer for the given samples. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) +{ + if (samples == 0) + return 0; + switch (snd_pcm_format_width(format)) { + case 4: { + u_int8_t silence = snd_pcm_format_silence_64(format); + unsigned int samples1; + if (samples % 2 != 0) + return -EINVAL; + samples1 = samples / 2; + memset(data, silence, samples1); + break; + } + case 8: { + u_int8_t silence = snd_pcm_format_silence_64(format); + memset(data, silence, samples); + break; + } + case 16: { + u_int16_t silence = snd_pcm_format_silence_64(format); + if (! silence) + memset(data, 0, samples * 2); + else { + while (samples-- > 0) + *((u_int16_t *)data)++ = silence; + } + break; + } + case 24: { + u_int32_t silence = snd_pcm_format_silence_64(format); + if (! silence) + memset(data, 0, samples * 3); + else { + while (samples-- > 0) { +#ifdef SNDRV_LITTLE_ENDIAN + *((u_int8_t *)data)++ = silence >> 0; + *((u_int8_t *)data)++ = silence >> 8; + *((u_int8_t *)data)++ = silence >> 16; +#else + *((u_int8_t *)data)++ = silence >> 16; + *((u_int8_t *)data)++ = silence >> 8; + *((u_int8_t *)data)++ = silence >> 0; +#endif + } + } + break; + } + case 32: { + u_int32_t silence = snd_pcm_format_silence_64(format); + if (! silence) + memset(data, 0, samples * 4); + else { + while (samples-- > 0) + *((u_int32_t *)data)++ = silence; + } + break; + } + case 64: { + u_int64_t silence = snd_pcm_format_silence_64(format); + if (! silence) + memset(data, 0, samples * 8); + else { + while (samples-- > 0) + *((u_int64_t *)data)++ = silence; + } + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int linear_formats[4*2*2] = { + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE +}; + +/** + * snd_pcm_build_linear_format - return the suitable linear format for the given condition + * @width: the bit-width + * @unsignd: 1 if unsigned, 0 if signed. + * @big_endian: 1 if big-endian, 0 if little-endian + * + * Returns the suitable linear format for the given condition. + */ +snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) +{ + switch (width) { + case 8: + width = 0; + break; + case 16: + width = 1; + break; + case 24: + width = 2; + break; + case 32: + width = 3; + break; + default: + return SND_PCM_FORMAT_UNKNOWN; + } + return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]); +} diff -urN linux-2.4.21-rc1.orig/sound/core/pcm_native.c linux/sound/core/pcm_native.c --- linux-2.4.21-rc1.orig/sound/core/pcm_native.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/pcm_native.c 2003-03-12 05:14:03.000000000 -0700 @@ -0,0 +1,3078 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Compatibility + */ + +struct sndrv_pcm_hw_params_old { + unsigned int flags; + unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - + SNDRV_PCM_HW_PARAM_ACCESS + 1]; + struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME - + SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1]; + unsigned int rmask; + unsigned int cmask; + unsigned int info; + unsigned int msbits; + unsigned int rate_num; + unsigned int rate_den; + sndrv_pcm_uframes_t fifo_size; + unsigned char reserved[64]; +}; + +#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old) +#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old) + +static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old * _oparams); +static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old * _oparams); + +/* + * + */ + +static rwlock_t pcm_link_lock = RW_LOCK_UNLOCKED; + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + + + +int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info) +{ + snd_pcm_runtime_t * runtime; + snd_pcm_t *pcm = substream->pcm; + snd_pcm_str_t *pstr = substream->pstr; + + snd_assert(substream != NULL, return -ENXIO); + memset(info, 0, sizeof(*info)); + info->card = pcm->card->number; + info->device = pcm->device; + info->stream = substream->stream; + info->subdevice = substream->number; + strncpy(info->id, pcm->id, sizeof(info->id)-1); + strncpy(info->name, pcm->name, sizeof(info->name)-1); + info->dev_class = pcm->dev_class; + info->dev_subclass = pcm->dev_subclass; + info->subdevices_count = pstr->substream_count; + info->subdevices_avail = pstr->substream_count - pstr->substream_opened; + strncpy(info->subname, substream->name, sizeof(info->subname)-1); + runtime = substream->runtime; + /* AB: FIXME!!! This is definitely nonsense */ + if (runtime) { + info->sync = runtime->sync; + substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); + } + return 0; +} + +int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t * _info) +{ + snd_pcm_info_t info; + int err = snd_pcm_info(substream, &info); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return err; +} + +#undef RULES_DEBUG + +#ifdef RULES_DEBUG +#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v +char *snd_pcm_hw_param_names[] = { + HW_PARAM(ACCESS), + HW_PARAM(FORMAT), + HW_PARAM(SUBFORMAT), + HW_PARAM(SAMPLE_BITS), + HW_PARAM(FRAME_BITS), + HW_PARAM(CHANNELS), + HW_PARAM(RATE), + HW_PARAM(PERIOD_TIME), + HW_PARAM(PERIOD_SIZE), + HW_PARAM(PERIOD_BYTES), + HW_PARAM(PERIODS), + HW_PARAM(BUFFER_TIME), + HW_PARAM(BUFFER_SIZE), + HW_PARAM(BUFFER_BYTES), + HW_PARAM(TICK_TIME), +}; +#endif + +int snd_pcm_hw_refine(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned int k; + snd_pcm_hardware_t *hw; + snd_interval_t *i = NULL; + snd_mask_t *m = NULL; + snd_pcm_hw_constraints_t *constrs = &substream->runtime->hw_constraints; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; + unsigned int stamp = 2; + int changed, again; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + m = hw_param_mask(params, k); + if (snd_mask_empty(m)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + printk("%s = ", snd_pcm_hw_param_names[k]); + printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); +#endif + changed = snd_mask_refine(m, constrs_mask(constrs, k)); +#ifdef RULES_DEBUG + printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + i = hw_param_interval(params, k); + if (snd_interval_empty(i)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + printk("%s = ", snd_pcm_hw_param_names[k]); + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + printk(" -> "); +#endif + changed = snd_interval_refine(i, constrs_interval(constrs, k)); +#ifdef RULES_DEBUG + if (i->empty) + printk("empty\n"); + else + printk("%c%u %u%c\n", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = 0; k < constrs->rules_num; k++) + rstamps[k] = 0; + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; + do { + again = 0; + for (k = 0; k < constrs->rules_num; k++) { + snd_pcm_hw_rule_t *r = &constrs->rules[k]; + unsigned int d; + int doit = 0; + if (r->cond && !(r->cond & params->flags)) + continue; + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) { + doit = 1; + break; + } + } + if (!doit) + continue; +#ifdef RULES_DEBUG + printk("Rule %d [%p]: ", k, r->func); + if (r->var >= 0) { + printk("%s = ", snd_pcm_hw_param_names[r->var]); + if (hw_is_mask(r->var)) { + m = hw_param_mask(params, r->var); + printk("%x", *m->bits); + } else { + i = hw_param_interval(params, r->var); + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } +#endif + changed = r->func(params, r); +#ifdef RULES_DEBUG + if (r->var >= 0) { + printk(" -> "); + if (hw_is_mask(r->var)) + printk("%x", *m->bits); + else { + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } + printk("\n"); +#endif + rstamps[k] = stamp; + if (changed && r->var >= 0) { + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = 1; + } + if (changed < 0) + return changed; + stamp++; + } + } while (again); + if (!params->msbits) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i)) + params->msbits = snd_interval_value(i); + } + + if (!params->rate_den) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (snd_interval_single(i)) { + params->rate_num = snd_interval_value(i); + params->rate_den = 1; + } + } + + hw = &substream->runtime->hw; + if (!params->info) + params->info = hw->info; + if (!params->fifo_size) + params->fifo_size = hw->fifo_size; + params->rmask = 0; + return 0; +} + +static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params) +{ + snd_pcm_hw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_hw_refine(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + snd_pcm_runtime_t *runtime; + int err; + unsigned int bits; + snd_pcm_uframes_t frames; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + break; + default: + return -EBADFD; + } +#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) + if (!substream->oss.oss) +#endif + if (atomic_read(&runtime->mmap_count)) + return -EBADFD; + + params->rmask = ~0U; + err = snd_pcm_hw_refine(substream, params); + if (err < 0) + goto _error; + + err = snd_pcm_hw_params_choose(substream, params); + if (err < 0) + goto _error; + + if (substream->ops->hw_params != NULL) { + err = substream->ops->hw_params(substream, params); + if (err < 0) + goto _error; + } + + runtime->access = params_access(params); + runtime->format = params_format(params); + runtime->subformat = params_subformat(params); + runtime->channels = params_channels(params); + runtime->rate = params_rate(params); + runtime->period_size = params_period_size(params); + runtime->periods = params_periods(params); + runtime->buffer_size = params_buffer_size(params); + runtime->tick_time = params_tick_time(params); + runtime->info = params->info; + runtime->rate_num = params->rate_num; + runtime->rate_den = params->rate_den; + + bits = snd_pcm_format_physical_width(runtime->format); + runtime->sample_bits = bits; + bits *= runtime->channels; + runtime->frame_bits = bits; + frames = 1; + while (bits % 8 != 0) { + bits *= 2; + frames *= 2; + } + runtime->byte_align = bits / 8; + runtime->min_align = frames; + + /* Default sw params */ + runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + runtime->period_step = 1; + runtime->sleep_min = 0; + runtime->control->avail_min = runtime->period_size; + runtime->xfer_align = runtime->period_size; + runtime->start_threshold = 1; + runtime->stop_threshold = runtime->buffer_size; + runtime->silence_threshold = 0; + runtime->silence_size = 0; + runtime->boundary = runtime->buffer_size; + while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) + runtime->boundary *= 2; + + snd_pcm_timer_resolution_change(substream); + runtime->status->state = SNDRV_PCM_STATE_SETUP; + return 0; + _error: + /* hardware might be unuseable from this time, + so we force application to retry to set + the correct hardware parameter settings */ + runtime->status->state = SNDRV_PCM_STATE_OPEN; + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + return err; +} + +static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params) +{ + snd_pcm_hw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_hw_params(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime; + int result; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + break; + default: + return -EBADFD; + } + if (atomic_read(&runtime->mmap_count)) + return -EBADFD; + if (substream->ops->hw_free == NULL) { + runtime->status->state = SNDRV_PCM_STATE_OPEN; + return 0; + } + result = substream->ops->hw_free(substream); + runtime->status->state = SNDRV_PCM_STATE_OPEN; + return result; +} + +static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) + return -EINVAL; + if (params->avail_min == 0) + return -EINVAL; + if (params->xfer_align == 0 || + params->xfer_align % runtime->min_align != 0) + return -EINVAL; + if ((params->silence_threshold != 0 || params->silence_size < runtime->boundary) && + (params->silence_threshold + params->silence_size > runtime->buffer_size)) + return -EINVAL; + spin_lock_irq(&runtime->lock); + runtime->tstamp_mode = params->tstamp_mode; + runtime->sleep_min = params->sleep_min; + runtime->period_step = params->period_step; + runtime->control->avail_min = params->avail_min; + runtime->start_threshold = params->start_threshold; + runtime->stop_threshold = params->stop_threshold; + runtime->silence_threshold = params->silence_threshold; + runtime->silence_size = params->silence_size; + runtime->xfer_align = params->xfer_align; + params->boundary = runtime->boundary; + if (snd_pcm_running(substream)) { + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + else + snd_pcm_tick_set(substream, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream, ULONG_MAX); + wake_up(&runtime->sleep); + } + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t * _params) +{ + snd_pcm_sw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_sw_params(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +int snd_pcm_status(snd_pcm_substream_t *substream, + snd_pcm_status_t *status) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&runtime->lock); + status->state = runtime->status->state; + status->suspended_state = runtime->status->suspended_state; + if (status->state == SNDRV_PCM_STATE_OPEN) + goto _end; + status->trigger_tstamp = runtime->trigger_tstamp; + if (snd_pcm_running(substream)) { + snd_pcm_update_hw_ptr(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + status->tstamp = runtime->status->tstamp; + else + snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec); + } else + snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec); + status->appl_ptr = runtime->control->appl_ptr; + status->hw_ptr = runtime->status->hw_ptr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + status->avail = snd_pcm_playback_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || + runtime->status->state == SNDRV_PCM_STATE_DRAINING) + status->delay = runtime->buffer_size - status->avail; + else + status->delay = 0; + } else { + status->avail = snd_pcm_capture_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) + status->delay = status->avail; + else + status->delay = 0; + } + status->avail_max = runtime->avail_max; + status->overrange = runtime->overrange; + runtime->avail_max = 0; + runtime->overrange = 0; + _end: + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t * _status) +{ + snd_pcm_status_t status; + snd_pcm_runtime_t *runtime; + int res; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + memset(&status, 0, sizeof(status)); + res = snd_pcm_status(substream, &status); + if (res < 0) + return res; + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t * _info) +{ + snd_pcm_channel_info_t info; + snd_pcm_runtime_t *runtime; + int res; + unsigned int channel; + + snd_assert(substream != NULL, return -ENXIO); + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + channel = info.channel; + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (channel >= runtime->channels) + return -EINVAL; + memset(&info, 0, sizeof(info)); + info.channel = channel; + res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info); + if (res < 0) + return res; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static void snd_pcm_trigger_tstamp(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->trigger_master == NULL) + return; + if (runtime->trigger_master == substream) { + snd_timestamp_now(&runtime->trigger_tstamp, runtime->tstamp_timespec); + } else { + snd_pcm_trigger_tstamp(runtime->trigger_master); + runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; + } + runtime->trigger_master = NULL; +} + +#define _SND_PCM_ACTION(aname, substream, state, res, check_master) { \ + snd_pcm_substream_t *s; \ + res = 0; \ + read_lock(&pcm_link_lock); \ + s = substream; \ + do { \ + if (s != substream) \ + spin_lock(&s->runtime->lock); \ + res = snd_pcm_pre_##aname(s, state); \ + if (res < 0) \ + break; \ + s = s->link_next; \ + } while (s != substream); \ + if (res < 0) { \ + /* Clean all spin_lock */ \ + while (s != substream) { \ + spin_unlock(&s->runtime->lock); \ + s = s->link_prev; \ + } \ + goto _end; \ + } \ + s = substream; \ + do { \ + snd_pcm_runtime_t *runtime = s->runtime; \ + int err; \ + if (check_master && runtime->trigger_master != s) \ + goto _done; \ + err = snd_pcm_do_##aname(s, state); \ + if (err < 0) { \ + if (res == 0) \ + res = err; \ + } else { \ + _done: \ + snd_pcm_post_##aname(s, state); \ + } \ + if (s != substream) \ + spin_unlock(&runtime->lock); \ + s = s->link_next; \ + } while (s != substream); \ + _end: \ + read_unlock(&pcm_link_lock); \ +} + +#define SND_PCM_ACTION(aname, substream, state) { \ + int res; \ + _SND_PCM_ACTION(aname, substream, state, res, 1); \ + return res; \ +} + +static inline int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) + return -EBADFD; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !snd_pcm_playback_data(substream)) + return -EPIPE; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state) +{ + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); +} + +static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_tstamp(substream); + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream, ULONG_MAX); + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + if (substream->timer) + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp); +} + +/** + * snd_pcm_sart + */ +int snd_pcm_start(snd_pcm_substream_t *substream) +{ + SND_PCM_ACTION(start, substream, 0); +} + +static inline int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING && + substream->runtime->status->state != SNDRV_PCM_STATE_DRAINING) + return -EBADFD; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state) +{ + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); +} + +static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_tstamp(substream); + if (substream->timer) + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp); + runtime->status->state = state; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); +} + +/** + * snd_pcm_stop + */ +int snd_pcm_stop(snd_pcm_substream_t *substream, int state) +{ + SND_PCM_ACTION(stop, substream, state); +} + +static inline int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_PAUSE)) + return -ENOSYS; + if (push) { + if (runtime->status->state != SNDRV_PCM_STATE_RUNNING) + return -EBADFD; + } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED) + return -EBADFD; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push) +{ + return substream->ops->trigger(substream, + push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : + SNDRV_PCM_TRIGGER_PAUSE_RELEASE); +} + +static inline void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_tstamp(substream); + if (push) { + runtime->status->state = SNDRV_PCM_STATE_PAUSED; + if (substream->timer) + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); + } else { + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + if (substream->timer) + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); + } +} + +static int snd_pcm_pause(snd_pcm_substream_t *substream, int push) +{ + SND_PCM_ACTION(pause, substream, push); +} + +#ifdef CONFIG_PM +/* suspend */ + +static inline int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + return -EBUSY; + runtime->status->suspended_state = runtime->status->state; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING) + return 0; + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); +} + +static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_tstamp(substream); + if (substream->timer) + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); + runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); +} + +/** + * snd_pcm_suspend + */ +int snd_pcm_suspend(snd_pcm_substream_t *substream) +{ + SND_PCM_ACTION(suspend, substream, 0); +} + +/** + * snd_pcm_suspend_all + */ +int snd_pcm_suspend_all(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + int stream, err; + + for (stream = 0; stream < 2; stream++) { + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) { + /* FIXME: the open/close code should lock this as well */ + if ((runtime = substream->runtime) == NULL) + continue; + spin_lock(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + spin_unlock(&runtime->lock); + continue; + } + if ((err = snd_pcm_suspend(substream)) < 0) { + spin_unlock(&runtime->lock); + return err; + } + spin_unlock(&runtime->lock); + } + } + return 0; +} + +/* resume */ + +static inline int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) + return -ENOSYS; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING) + return 0; + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME); +} + +static inline void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_tstamp(substream); + if (substream->timer) + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); + runtime->status->state = runtime->status->suspended_state; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); +} + +static int snd_pcm_resume(snd_pcm_substream_t *substream) +{ + snd_card_t *card = substream->pcm->card; + int res; + + snd_power_lock(card); + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _power_unlock; + } + snd_power_wait(card); + } + + _SND_PCM_ACTION(resume, substream, 0, res, 1); + + _power_unlock: + snd_power_unlock(card); + return res; +} + +#else + +static int snd_pcm_resume(snd_pcm_substream_t *substream) +{ + return -ENOSYS; +} + +#endif /* CONFIG_PM */ + +static int snd_pcm_xrun(snd_pcm_substream_t *substream) +{ + snd_card_t *card = substream->pcm->card; + snd_pcm_runtime_t *runtime = substream->runtime; + int result; + + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + _xrun_recovery: + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + result = 0; /* already there */ + break; + case SNDRV_PCM_STATE_RUNNING: + result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + default: + result = -EBADFD; + } + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return result; +} + +static inline int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + case SNDRV_PCM_STATE_SUSPENDED: + return 0; + default: + return -EBADFD; + } +} + +static inline int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, 0); + if (err < 0) + return err; + // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); + runtime->hw_ptr_base = 0; + runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; + runtime->silenced_start = runtime->status->hw_ptr; + runtime->silenced_size = 0; + return 0; +} + +static inline void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + runtime->control->appl_ptr = runtime->status->hw_ptr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream, ULONG_MAX); +} + +static int snd_pcm_reset(snd_pcm_substream_t *substream) +{ + int res; + + spin_lock_irq(&substream->runtime->lock); + _SND_PCM_ACTION(reset, substream, 0, res, 0); + spin_unlock_irq(&substream->runtime->lock); + return res; +} + +static inline int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + return -EBADFD; + case SNDRV_PCM_STATE_RUNNING: + return -EBUSY; + default: + return 0; + } +} + +static inline int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state) +{ + int err; + err = substream->ops->prepare(substream); + if (err < 0) + return err; + return snd_pcm_do_reset(substream, 0); +} + +static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + runtime->control->appl_ptr = runtime->status->hw_ptr; + runtime->status->state = SNDRV_PCM_STATE_PREPARED; +} + +/** + * snd_pcm_prepare + */ +int snd_pcm_prepare(snd_pcm_substream_t *substream) +{ + int res; + snd_card_t *card = substream->pcm->card; + + snd_power_lock(card); + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _power_unlock; + } + snd_power_wait(card); + } + + spin_lock_irq(&substream->runtime->lock); + _SND_PCM_ACTION(prepare, substream, 0, res, 0); + spin_unlock_irq(&substream->runtime->lock); + + _power_unlock: + snd_power_unlock(card); + return res; +} + +static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_substream_t *s; + read_lock(&pcm_link_lock); + s = substream->link_next; + while (s != substream) { + spin_lock(&s->runtime->lock); + s = s->link_next; + } + s = substream; + do { + snd_pcm_runtime_t *runtime = s->runtime; + runtime->status->state = state; + if (s != substream) + spin_unlock(&runtime->lock); + s = s->link_next; + } while (s != substream); + read_unlock(&pcm_link_lock); +} + +static int snd_pcm_playback_drop(snd_pcm_substream_t *substream); + +static int snd_pcm_playback_drain(snd_pcm_substream_t * substream) +{ + snd_card_t *card; + snd_pcm_runtime_t *runtime; + int err, result = 0; + wait_queue_t wait; + enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY; + snd_pcm_uframes_t stop_threshold; + + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + runtime = substream->runtime; + card = substream->pcm->card; + + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + + /* stop_threshold fixup to avoid endless loop when */ + /* stop_threshold > buffer_size */ + stop_threshold = runtime->stop_threshold; + if (runtime->stop_threshold > runtime->buffer_size) + runtime->stop_threshold = runtime->buffer_size; + + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + case SNDRV_PCM_STATE_OPEN: + result = -EBADFD; + goto _end; + case SNDRV_PCM_STATE_PREPARED: + if (!snd_pcm_playback_empty(substream)) { + err = snd_pcm_start(substream); + if (err < 0) { + result = err; + goto _end; + } + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + /* Fall through */ + case SNDRV_PCM_STATE_SETUP: + goto _end; + default: + break; + } + + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { + if (snd_pcm_playback_empty(substream)) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + goto _end; + } + snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING); + } + + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) { + state = READY; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case SIGNALED: + result = -ERESTARTSYS; + goto _end; + case SUSPENDED: + result = -ESTRPIPE; + goto _end; + case EXPIRED: + snd_printd("playback drain error (DMA or IRQ trouble?)\n"); + result = -EIO; + goto _end; + default: + break; + } + + _end: + runtime->stop_threshold = stop_threshold; + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + if (state == EXPIRED) + snd_pcm_playback_drop(substream); + + return result; +} + +static int snd_pcm_playback_drop(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + default: + break; + } + runtime->control->appl_ptr = runtime->status->hw_ptr; + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +static int snd_pcm_capture_drain(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_PREPARED: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_stop(substream, + snd_pcm_capture_avail(runtime) > 0 ? + SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP); + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, + snd_pcm_capture_avail(runtime) > 0 ? + SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + default: + break; + } + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +static int snd_pcm_capture_drop(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_XRUN: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + default: + break; + } + runtime->control->appl_ptr = runtime->status->hw_ptr; + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +/* WARNING: Don't forget to fput back the file */ +static struct file *snd_pcm_file_fd(int fd) +{ + struct file *file; + struct inode *inode; + unsigned short minor; + file = fget(fd); + if (!file) + return 0; + inode = file->f_dentry->d_inode; + if (!S_ISCHR(inode->i_mode) || + major(inode->i_rdev) != CONFIG_SND_MAJOR) { + fput(file); + return 0; + } + minor = minor(inode->i_rdev); + if (minor >= 256 || + minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) { + fput(file); + return 0; + } + return file; +} + +static int snd_pcm_link(snd_pcm_substream_t *substream, int fd) +{ + int res = 0; + struct file *file; + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *s, *substream1; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + file = snd_pcm_file_fd(fd); + if (!file) + return -EBADFD; + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream1 = pcm_file->substream; + write_lock_irq(&pcm_link_lock); + if (substream->runtime->status->state != substream1->runtime->status->state) { + res = -EBADFD; + goto _end; + } + s = substream; + do { + if (s == substream1) { + res = -EALREADY; + goto _end; + } + s = s->link_next; + } while (s != substream); + substream1->link_prev->link_next = substream->link_next; + substream->link_next->link_prev = substream1->link_prev; + substream->link_next = substream1; + substream1->link_prev = substream; + _end: + write_unlock_irq(&pcm_link_lock); + fput(file); + return res; +} + +static int snd_pcm_unlink(snd_pcm_substream_t *substream) +{ + write_lock_irq(&pcm_link_lock); + substream->link_prev->link_next = substream->link_next; + substream->link_next->link_prev = substream->link_prev; + substream->link_prev = substream; + substream->link_next = substream; + write_unlock_irq(&pcm_link_lock); + return 0; +} + + +static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_div(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), + (unsigned long) rule->private, &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), + (unsigned long) rule->private, + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int k; + snd_interval_t *i = hw_param_interval(params, rule->deps[0]); + snd_mask_t m; + snd_mask_t *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_any(&m); + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (! snd_mask_test(mask, k)) + continue; + bits = snd_pcm_format_physical_width(k); + snd_assert(bits > 0, continue); + if ((unsigned)bits < i->min || (unsigned)bits > i->max) + snd_mask_reset(&m, k); + } + return snd_mask_refine(mask, &m); +} + +static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + unsigned int k; + t.min = UINT_MAX; + t.max = 0; + t.openmin = 0; + t.openmax = 0; + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) + continue; + bits = snd_pcm_format_physical_width(k); + snd_assert(bits > 0, continue); + if (t.min > (unsigned)bits) + t.min = bits; + if (t.max < (unsigned)bits) + t.max = bits; + } + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 +#error "Change this table" +#endif + +static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 }; + +#define RATES (sizeof(rates) / sizeof(rates[0])) + +static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hardware_t *hw = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), RATES, rates, hw->rates); +} + +static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_pcm_substream_t *substream = rule->private; + t.min = 0; + t.max = substream->buffer_bytes_max; + t.openmin = 0; + t.openmax = 0; + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + int k, err; + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + snd_mask_any(constrs_mask(constrs, k)); + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + snd_interval_any(constrs_interval(constrs, k)); + } + + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_hw_rule_format, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_sample_bits, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mul, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mul, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + return 0; +} + +int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hardware_t *hw = &runtime->hw; + int err; + unsigned int mask = 0; + + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_MMAP) { + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_COMPLEX) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; + } + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + hw->channels_min, hw->channels_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, + hw->rate_min, hw->rate_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + hw->period_bytes_min, hw->period_bytes_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, + hw->periods_min, hw->periods_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + hw->period_bytes_min, hw->buffer_bytes_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_buffer_bytes_max, substream, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); + if (err < 0) + return err; + + /* FIXME: remove */ + if (runtime->dma_bytes) { + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); + snd_assert(err >= 0, return -EINVAL); + } + + if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_rate, hw, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + } + + /* FIXME: this belong to lowlevel */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, + 1000000 / HZ, 1000000 / HZ); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + + return 0; +} + +static void snd_pcm_add_file(snd_pcm_str_t *str, + snd_pcm_file_t *pcm_file) +{ + pcm_file->next = str->files; + str->files = pcm_file; +} + +static void snd_pcm_remove_file(snd_pcm_str_t *str, + snd_pcm_file_t *pcm_file) +{ + snd_pcm_file_t * pcm_file1; + if (str->files == pcm_file) { + str->files = pcm_file->next; + } else { + pcm_file1 = str->files; + while (pcm_file1 && pcm_file1->next != pcm_file) + pcm_file1 = pcm_file1->next; + if (pcm_file1 != NULL) + pcm_file1->next = pcm_file->next; + } +} + +static int snd_pcm_release_file(snd_pcm_file_t * pcm_file) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_str_t * str; + + snd_assert(pcm_file != NULL, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + str = substream->pstr; + snd_pcm_unlink(substream); + if (substream->open_flag) { + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + substream->ops->close(substream); + substream->open_flag = 0; + } + substream->ffile = NULL; + snd_pcm_remove_file(str, pcm_file); + snd_pcm_release_substream(substream); + snd_magic_kfree(pcm_file); + return 0; +} + +static int snd_pcm_open_file(struct file *file, + snd_pcm_t *pcm, + int stream, + snd_pcm_file_t **rpcm_file) +{ + int err = 0; + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_str_t *str; + + snd_assert(rpcm_file != NULL, return -EINVAL); + *rpcm_file = NULL; + + pcm_file = snd_magic_kcalloc(snd_pcm_file_t, 0, GFP_KERNEL); + if (pcm_file == NULL) { + return -ENOMEM; + } + + if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) { + snd_magic_kfree(pcm_file); + return err; + } + + str = substream->pstr; + substream->file = pcm_file; + + pcm_file->substream = substream; + + snd_pcm_add_file(str, pcm_file); + + err = snd_pcm_hw_constraints_init(substream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraints_init failed\n"); + snd_pcm_release_file(pcm_file); + return err; + } + + if ((err = substream->ops->open(substream)) < 0) { + snd_pcm_release_file(pcm_file); + return err; + } + substream->open_flag = 1; + + err = snd_pcm_hw_constraints_complete(substream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraints_complete failed\n"); + substream->ops->close(substream); + snd_pcm_release_file(pcm_file); + return err; + } + + substream->ffile = file; + + file->private_data = pcm_file; + *rpcm_file = pcm_file; + return 0; +} + +int snd_pcm_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + int device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)); + int err; + snd_pcm_t *pcm; + snd_pcm_file_t *pcm_file; + wait_queue_t wait; + + snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO); + pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)]; + if (pcm == NULL) { + err = -ENODEV; + goto __error1; + } + err = snd_card_file_add(pcm->card, file); + if (err < 0) + goto __error1; + if (!try_module_get(pcm->card->module)) { + err = -EFAULT; + goto __error2; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&pcm->open_wait, &wait); + while (1) { + down(&pcm->open_mutex); + err = snd_pcm_open_file(file, pcm, device >= SNDRV_MINOR_PCM_CAPTURE ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, &pcm_file); + if (err >= 0) + break; + up(&pcm->open_mutex); + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pcm->open_wait, &wait); + if (err < 0) + goto __error; + up(&pcm->open_mutex); + return err; + + __error: + module_put(pcm->card->module); + __error2: + snd_card_file_remove(pcm->card, file); + __error1: + return err; +} + +int snd_pcm_release(struct inode *inode, struct file *file) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + snd_assert(!atomic_read(&substream->runtime->mmap_count), ); + pcm = substream->pcm; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_playback_drop(substream); + else + snd_pcm_capture_drop(substream); + fasync_helper(-1, file, 0, &substream->runtime->fasync); + down(&pcm->open_mutex); + snd_pcm_release_file(pcm_file); + up(&pcm->open_mutex); + wake_up(&pcm->open_wait); + module_put(pcm->card->module); + snd_card_file_remove(pcm->card, file); + return 0; +} + +snd_pcm_sframes_t snd_pcm_playback_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t hw_avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + hw_avail = snd_pcm_playback_hw_avail(runtime); + if (hw_avail <= 0) { + ret = 0; + goto __end; + } + if (frames > (snd_pcm_uframes_t)hw_avail) + frames = hw_avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr - frames; + if (appl_ptr < 0) + appl_ptr += runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t hw_avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + hw_avail = snd_pcm_capture_hw_avail(runtime); + if (hw_avail <= 0) { + ret = 0; + goto __end; + } + if (frames > (snd_pcm_uframes_t)hw_avail) + frames = hw_avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr - frames; + if (appl_ptr < 0) + appl_ptr += runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +snd_pcm_sframes_t snd_pcm_playback_forward(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + avail = snd_pcm_playback_avail(runtime); + if (avail <= 0) { + ret = 0; + goto __end; + } + if (frames > (snd_pcm_uframes_t)avail) + frames = avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr + frames; + if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) + appl_ptr -= runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +snd_pcm_sframes_t snd_pcm_capture_forward(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + avail = snd_pcm_capture_avail(runtime); + if (avail <= 0) { + ret = 0; + goto __end; + } + if (frames > (snd_pcm_uframes_t)avail) + frames = avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr + frames; + if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) + appl_ptr -= runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +static int snd_pcm_hwsync(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_DRAINING: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + goto __badfd; + case SNDRV_PCM_STATE_RUNNING: + if ((err = snd_pcm_update_hw_ptr(substream)) < 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_SUSPENDED: + err = 0; + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + break; + default: + __badfd: + err = -EBADFD; + break; + } + spin_unlock_irq(&runtime->lock); + return err; +} + +static int snd_pcm_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + snd_pcm_sframes_t n = 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_DRAINING: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + goto __badfd; + case SNDRV_PCM_STATE_RUNNING: + if ((err = snd_pcm_update_hw_ptr(substream)) < 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_SUSPENDED: + err = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + n = snd_pcm_playback_hw_avail(runtime); + else + n = snd_pcm_capture_avail(runtime); + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + break; + default: + __badfd: + err = -EBADFD; + break; + } + spin_unlock_irq(&runtime->lock); + if (!err) + if (put_user(n, res)) + err = -EFAULT; + return err; +} + +static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); +static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); + +static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + + switch (cmd) { + case SNDRV_PCM_IOCTL_PVERSION: + return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_PCM_IOCTL_INFO: + return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg); + case SNDRV_PCM_IOCTL_TSTAMP: + { + int xarg; + if (get_user(xarg, (int *) arg)) + return -EFAULT; + substream->runtime->tstamp_timespec = xarg ? 1 : 0; + return 0; + } + case SNDRV_PCM_IOCTL_HW_REFINE: + return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg); + case SNDRV_PCM_IOCTL_HW_PARAMS: + return snd_pcm_hw_params_user(substream, (snd_pcm_hw_params_t *) arg); + case SNDRV_PCM_IOCTL_HW_FREE: + return snd_pcm_hw_free(substream); + case SNDRV_PCM_IOCTL_SW_PARAMS: + return snd_pcm_sw_params_user(substream, (snd_pcm_sw_params_t *) arg); + case SNDRV_PCM_IOCTL_STATUS: + return snd_pcm_status_user(substream, (snd_pcm_status_t *) arg); + case SNDRV_PCM_IOCTL_CHANNEL_INFO: + return snd_pcm_channel_info(substream, (snd_pcm_channel_info_t *) arg); + case SNDRV_PCM_IOCTL_PREPARE: + return snd_pcm_prepare(substream); + case SNDRV_PCM_IOCTL_RESET: + return snd_pcm_reset(substream); + case SNDRV_PCM_IOCTL_START: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_start(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_LINK: + return snd_pcm_link(substream, (long) arg); + case SNDRV_PCM_IOCTL_UNLINK: + return snd_pcm_unlink(substream); + case SNDRV_PCM_IOCTL_RESUME: + return snd_pcm_resume(substream); + case SNDRV_PCM_IOCTL_XRUN: + return snd_pcm_xrun(substream); + case SNDRV_PCM_IOCTL_HWSYNC: + return snd_pcm_hwsync(substream); + case SNDRV_PCM_IOCTL_DELAY: + return snd_pcm_delay(substream, (snd_pcm_sframes_t *) arg); + case SNDRV_PCM_IOCTL_HW_REFINE_OLD: + return snd_pcm_hw_refine_old_user(substream, (struct sndrv_pcm_hw_params_old *) arg); + case SNDRV_PCM_IOCTL_HW_PARAMS_OLD: + return snd_pcm_hw_params_old_user(substream, (struct sndrv_pcm_hw_params_old *) arg); + } + snd_printd("unknown ioctl = 0x%x\n", cmd); + return -ENOTTY; +} + +static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEI_FRAMES: + { + snd_xferi_t xferi, *_xferi = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (put_user(0, &_xferi->result)) + return -EFAULT; + if (copy_from_user(&xferi, _xferi, sizeof(xferi))) + return -EFAULT; + result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); + __put_user(result, &_xferi->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + { + snd_xfern_t xfern, *_xfern = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + void *bufs; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (runtime->channels > 128) + return -EINVAL; + if (put_user(0, &_xfern->result)) + return -EFAULT; + if (copy_from_user(&xfern, _xfern, sizeof(xfern))) + return -EFAULT; + bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); + if (bufs == NULL) + return -ENOMEM; + if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { + kfree(bufs); + return -EFAULT; + } + result = snd_pcm_lib_writev(substream, bufs, xfern.frames); + kfree(bufs); + __put_user(result, &_xfern->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_REWIND: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_playback_rewind(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_FORWARD: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_playback_forward(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_PAUSE: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_pause(substream, (long) arg); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_playback_drain(substream); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_playback_drop(substream); + } + return snd_pcm_common_ioctl1(substream, cmd, arg); +} + +static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); + switch (cmd) { + case SNDRV_PCM_IOCTL_READI_FRAMES: + { + snd_xferi_t xferi, *_xferi = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (put_user(0, &_xferi->result)) + return -EFAULT; + if (copy_from_user(&xferi, _xferi, sizeof(xferi))) + return -EFAULT; + result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); + __put_user(result, &_xferi->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_READN_FRAMES: + { + snd_xfern_t xfern, *_xfern = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + void *bufs; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (runtime->channels > 128) + return -EINVAL; + if (put_user(0, &_xfern->result)) + return -EFAULT; + if (copy_from_user(&xfern, _xfern, sizeof(xfern))) + return -EFAULT; + bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); + if (bufs == NULL) + return -ENOMEM; + if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { + kfree(bufs); + return -EFAULT; + } + result = snd_pcm_lib_readv(substream, bufs, xfern.frames); + kfree(bufs); + __put_user(result, &_xfern->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_REWIND: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_capture_rewind(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_FORWARD: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_capture_forward(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_capture_drain(substream); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_capture_drop(substream); + } + return snd_pcm_common_ioctl1(substream, cmd, arg); +} + +static int snd_pcm_playback_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + + if (((cmd >> 8) & 0xff) != 'A') + return -ENOTTY; + + return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void *) arg); +} + +static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + + if (((cmd >> 8) & 0xff) != 'A') + return -ENOTTY; + + return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void *) arg); +} + +int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + mm_segment_t fs; + int result; + + fs = snd_enter_user(); + result = snd_pcm_playback_ioctl1(substream, cmd, arg); + snd_leave_user(fs); + return result; +} + +int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + mm_segment_t fs; + int result; + + fs = snd_enter_user(); + result = snd_pcm_capture_ioctl1(substream, cmd, arg); + snd_leave_user(fs); + return result; +} + +int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + return snd_pcm_kernel_playback_ioctl(substream, cmd, arg); + case SNDRV_PCM_STREAM_CAPTURE: + return snd_pcm_kernel_capture_ioctl(substream, cmd, arg); + default: + return -EINVAL; + } +} + +static ssize_t snd_pcm_read(struct file *file, char *buf, size_t count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!frame_aligned(runtime, count)) + return -EINVAL; + count = bytes_to_frames(runtime, count); + result = snd_pcm_lib_read(substream, buf, count); + if (result > 0) + result = frames_to_bytes(runtime, result); + return result; +} + +static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) + up(&file->f_dentry->d_inode->i_sem); +#endif + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + substream = pcm_file->substream; + snd_assert(substream != NULL, result = -ENXIO; goto end); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + result = -EBADFD; + goto end; + } + if (!frame_aligned(runtime, count)) { + result = -EINVAL; + goto end; + } + count = bytes_to_frames(runtime, count); + result = snd_pcm_lib_write(substream, buf, count); + if (result > 0) + result = frames_to_bytes(runtime, result); + end: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) + down(&file->f_dentry->d_inode->i_sem); +#endif + return result; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) +static ssize_t snd_pcm_readv(struct file *file, const struct iovec *_vector, + unsigned long count, loff_t * offset) + +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + unsigned long i; + void **bufs; + snd_pcm_uframes_t frames; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (count > 1024 || count != runtime->channels) + return -EINVAL; + if (!frame_aligned(runtime, _vector->iov_len)) + return -EINVAL; + frames = bytes_to_samples(runtime, _vector->iov_len); + bufs = kmalloc(sizeof(void *) * count, GFP_KERNEL); + if (bufs == NULL) + return -ENOMEM; + for (i = 0; i < count; ++i) + bufs[i] = _vector[i].iov_base; + result = snd_pcm_lib_readv(substream, bufs, frames); + if (result > 0) + result = frames_to_bytes(runtime, result); + kfree(bufs); + return result; +} + +static ssize_t snd_pcm_writev(struct file *file, const struct iovec *_vector, + unsigned long count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + unsigned long i; + void **bufs; + snd_pcm_uframes_t frames; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) + up(&file->f_dentry->d_inode->i_sem); +#endif + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + substream = pcm_file->substream; + snd_assert(substream != NULL, result = -ENXIO; goto end); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + result = -EBADFD; + goto end; + } + if (count > 128 || count != runtime->channels || + !frame_aligned(runtime, _vector->iov_len)) { + result = -EINVAL; + goto end; + } + frames = bytes_to_samples(runtime, _vector->iov_len); + bufs = kmalloc(sizeof(void *) * count, GFP_KERNEL); + if (bufs == NULL) + return -ENOMEM; + for (i = 0; i < count; ++i) + bufs[i] = _vector[i].iov_base; + result = snd_pcm_lib_writev(substream, bufs, frames); + if (result > 0) + result = frames_to_bytes(runtime, result); + kfree(bufs); + end: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) + down(&file->f_dentry->d_inode->i_sem); +#endif + return result; +} +#endif + +unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + unsigned int mask; + snd_pcm_uframes_t avail; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + poll_wait(file, &runtime->sleep, wait); + + spin_lock_irq(&runtime->lock); + avail = snd_pcm_playback_avail(runtime); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + if (avail >= runtime->control->avail_min) { + mask = POLLOUT | POLLWRNORM; + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_DRAINING: + mask = 0; + break; + default: + mask = POLLOUT | POLLWRNORM | POLLERR; + break; + } + spin_unlock_irq(&runtime->lock); + return mask; +} + +unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + unsigned int mask; + snd_pcm_uframes_t avail; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + poll_wait(file, &runtime->sleep, wait); + + spin_lock_irq(&runtime->lock); + avail = snd_pcm_capture_avail(runtime); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + if (avail >= runtime->control->avail_min) { + mask = POLLIN | POLLRDNORM; + break; + } + mask = 0; + break; + case SNDRV_PCM_STATE_DRAINING: + if (avail > 0) { + mask = POLLIN | POLLRDNORM; + break; + } + /* Fall through */ + default: + mask = POLLIN | POLLRDNORM | POLLERR; + break; + } + spin_unlock_irq(&runtime->lock); + return mask; +} + +#ifndef VM_RESERVED +#ifndef LINUX_2_2 +static int snd_pcm_mmap_swapout(struct page * page, struct file * file) +#else +static int snd_pcm_mmap_swapout(struct vm_area_struct * area, struct page * page) +#endif +{ + return 0; +} +#endif + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + struct page * page; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; + page = virt_to_page(runtime->status); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_status = +{ + .nopage = snd_pcm_mmap_status_nopage, +#ifndef VM_RESERVED + .swapout = snd_pcm_mmap_swapout, +#endif +}; + +int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + size = area->vm_end - area->vm_start; + if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))) + return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_status; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + return 0; +} + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + struct page * page; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; + page = virt_to_page(runtime->control); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_control = +{ + .nopage = snd_pcm_mmap_control_nopage, +#ifndef VM_RESERVED + .swapout = snd_pcm_mmap_swapout, +#endif +}; + +static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + size = area->vm_end - area->vm_start; + if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))) + return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_control; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + return 0; +} + +static void snd_pcm_mmap_data_open(struct vm_area_struct *area) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_inc(&substream->runtime->mmap_count); +} + +static void snd_pcm_mmap_data_close(struct vm_area_struct *area) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_dec(&substream->runtime->mmap_count); +} + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + unsigned long offset; + struct page * page; + void *vaddr; + size_t dma_bytes; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + offset += address - area->vm_start; + snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM); + dma_bytes = PAGE_ALIGN(runtime->dma_bytes); + if (offset > dma_bytes - PAGE_SIZE) + return NOPAGE_SIGBUS; + if (substream->ops->page) { + page = substream->ops->page(substream, offset); + if (! page) + return NOPAGE_OOM; + } else { + vaddr = runtime->dma_area + offset; + page = virt_to_page(vaddr); + } + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_data = +{ + .open = snd_pcm_mmap_data_open, + .close = snd_pcm_mmap_data_close, + .nopage = snd_pcm_mmap_data_nopage, +#ifndef VM_RESERVED + .swapout = snd_pcm_mmap_swapout, +#endif +}; + +int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + unsigned long offset; + size_t dma_bytes; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!(area->vm_flags & (VM_WRITE|VM_READ))) + return -EINVAL; + } else { + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + } + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) + return -ENXIO; + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + size = area->vm_end - area->vm_start; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + dma_bytes = PAGE_ALIGN(runtime->dma_bytes); + if ((size_t)size > dma_bytes) + return -EINVAL; + if (offset > dma_bytes - size) + return -EINVAL; + + area->vm_ops = &snd_pcm_vm_ops_data; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + atomic_inc(&runtime->mmap_count); + return 0; +} + +static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) +{ + snd_pcm_file_t * pcm_file; + snd_pcm_substream_t *substream; + unsigned long offset; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + switch (offset) { + case SNDRV_PCM_MMAP_OFFSET_STATUS: + return snd_pcm_mmap_status(substream, file, area); + case SNDRV_PCM_MMAP_OFFSET_CONTROL: + return snd_pcm_mmap_control(substream, file, area); + default: + return snd_pcm_mmap_data(substream, file, area); + } + return 0; +} + +static int snd_pcm_fasync(int fd, struct file * file, int on) +{ + snd_pcm_file_t * pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + int err; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + err = fasync_helper(fd, file, on, &runtime->fasync); + if (err < 0) + return err; + return 0; +} + +/* + * To be removed helpers to keep binary compatibility + */ + +#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5)) +#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5)) + +static void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params, struct sndrv_pcm_hw_params_old *oparams) +{ + unsigned int i; + + memset(params, 0, sizeof(*params)); + params->flags = oparams->flags; + for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + params->masks[i].bits[0] = oparams->masks[i]; + memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals)); + params->rmask = __OLD_TO_NEW_MASK(oparams->rmask); + params->cmask = __OLD_TO_NEW_MASK(oparams->cmask); + params->info = oparams->info; + params->msbits = oparams->msbits; + params->rate_num = oparams->rate_num; + params->rate_den = oparams->rate_den; + params->fifo_size = oparams->fifo_size; +} + +static void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old *oparams, snd_pcm_hw_params_t *params) +{ + unsigned int i; + + memset(oparams, 0, sizeof(*oparams)); + oparams->flags = params->flags; + for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + oparams->masks[i] = params->masks[i].bits[0]; + memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals)); + oparams->rmask = __NEW_TO_OLD_MASK(params->rmask); + oparams->cmask = __NEW_TO_OLD_MASK(params->cmask); + oparams->info = params->info; + oparams->msbits = params->msbits; + oparams->rate_num = params->rate_num; + oparams->rate_den = params->rate_den; + oparams->fifo_size = params->fifo_size; +} + +static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old * _oparams) +{ + snd_pcm_hw_params_t params; + struct sndrv_pcm_hw_params_old oparams; + int err; + if (copy_from_user(&oparams, _oparams, sizeof(oparams))) + return -EFAULT; + snd_pcm_hw_convert_from_old_params(¶ms, &oparams); + err = snd_pcm_hw_refine(substream, ¶ms); + snd_pcm_hw_convert_to_old_params(&oparams, ¶ms); + if (copy_to_user(_oparams, &oparams, sizeof(oparams))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old * _oparams) +{ + snd_pcm_hw_params_t params; + struct sndrv_pcm_hw_params_old oparams; + int err; + if (copy_from_user(&oparams, _oparams, sizeof(oparams))) + return -EFAULT; + snd_pcm_hw_convert_from_old_params(¶ms, &oparams); + err = snd_pcm_hw_params(substream, ¶ms); + snd_pcm_hw_convert_to_old_params(&oparams, ¶ms); + if (copy_to_user(_oparams, &oparams, sizeof(oparams))) + return -EFAULT; + return err; +} + +/* + * Register section + */ + +static struct file_operations snd_pcm_f_ops_playback = { +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .write = snd_pcm_write, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) + .writev = snd_pcm_writev, +#endif + .open = snd_pcm_open, + .release = snd_pcm_release, + .poll = snd_pcm_playback_poll, + .ioctl = snd_pcm_playback_ioctl, + .mmap = snd_pcm_mmap, + .fasync = snd_pcm_fasync, +}; + +static struct file_operations snd_pcm_f_ops_capture = { +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = snd_pcm_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) + .readv = snd_pcm_readv, +#endif + .open = snd_pcm_open, + .release = snd_pcm_release, + .poll = snd_pcm_capture_poll, + .ioctl = snd_pcm_capture_ioctl, + .mmap = snd_pcm_mmap, + .fasync = snd_pcm_fasync, +}; + +snd_minor_t snd_pcm_reg[2] = +{ + { + .comment = "digital audio playback", + .f_ops = &snd_pcm_f_ops_playback, + }, + { + .comment = "digital audio capture", + .f_ops = &snd_pcm_f_ops_capture, + } +}; diff -urN linux-2.4.21-rc1.orig/sound/core/pcm_timer.c linux/sound/core/pcm_timer.c --- linux-2.4.21-rc1.orig/sound/core/pcm_timer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/pcm_timer.c 2002-08-13 10:13:34.000000000 -0600 @@ -0,0 +1,158 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#define chip_t snd_pcm_substream_t + +/* + * Timer functions + */ + +/* Greatest common divisor */ +static int gcd(int a, int b) +{ + int r; + if (a < b) { + r = a; + a = b; + b = r; + } + while ((r = a % b) != 0) { + a = b; + b = r; + } + return b; +} + +void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream) +{ + unsigned int rate, mult, fsize, l; + snd_pcm_runtime_t *runtime = substream->runtime; + + mult = 1000000000; + rate = runtime->rate; + snd_assert(rate != 0, return); + l = gcd(mult, rate); + mult /= l; + rate /= l; + fsize = runtime->period_size; + snd_assert(fsize != 0, return); + l = gcd(rate, fsize); + rate /= l; + fsize /= l; + while ((mult * fsize) / fsize != mult) { + mult /= 2; + rate /= 2; + } + snd_assert(rate != 0, return); + runtime->timer_resolution = mult * fsize / rate; +} + +static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer) +{ + snd_pcm_substream_t * substream; + + substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return -ENXIO); + return substream->runtime ? substream->runtime->timer_resolution : 0; +} + +static int snd_pcm_timer_start(snd_timer_t * timer) +{ + unsigned long flags; + snd_pcm_substream_t * substream; + + substream = snd_timer_chip(timer); + spin_lock_irqsave(&substream->timer_lock, flags); + substream->timer_running = 1; + spin_unlock_irqrestore(&substream->timer_lock, flags); + return 0; +} + +static int snd_pcm_timer_stop(snd_timer_t * timer) +{ + unsigned long flags; + snd_pcm_substream_t * substream; + + substream = snd_timer_chip(timer); + spin_lock_irqsave(&substream->timer_lock, flags); + substream->timer_running = 0; + spin_unlock_irqrestore(&substream->timer_lock, flags); + return 0; +} + +static struct _snd_timer_hardware snd_pcm_timer = +{ + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE, + .resolution = 0, + .ticks = 1, + .c_resolution = snd_pcm_timer_resolution, + .start = snd_pcm_timer_start, + .stop = snd_pcm_timer_stop, +}; + +/* + * Init functions + */ + +static void snd_pcm_timer_free(snd_timer_t *timer) +{ + snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return); + substream->timer = NULL; +} + +void snd_pcm_timer_init(snd_pcm_substream_t *substream) +{ + snd_timer_id_t tid; + snd_timer_t *timer; + + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.dev_class = SNDRV_TIMER_CLASS_PCM; + tid.card = substream->pcm->card->number; + tid.device = substream->pcm->device; + tid.subdevice = (substream->number << 1) | (substream->stream & 1); + if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) + return; + sprintf(timer->name, "PCM %s %i-%i-%i", + substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + "capture" : "playback", + tid.card, tid.device, tid.subdevice); + timer->hw = snd_pcm_timer; + if (snd_device_register(timer->card, timer) < 0) { + snd_device_free(timer->card, timer); + return; + } + timer->private_data = substream; + timer->private_free = snd_pcm_timer_free; + substream->timer = timer; +} + +void snd_pcm_timer_done(snd_pcm_substream_t *substream) +{ + if (substream->timer) { + snd_device_free(substream->pcm->card, substream->timer); + substream->timer = NULL; + } +} diff -urN linux-2.4.21-rc1.orig/sound/core/rawmidi.c linux/sound/core/rawmidi.c --- linux-2.4.21-rc1.orig/sound/core/rawmidi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/rawmidi.c 2003-03-10 03:08:58.000000000 -0700 @@ -0,0 +1,1652 @@ +/* + * Abstract layer for MIDI v1.0 stream + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA."); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_SND_OSSEMUL +static int midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +MODULE_PARM(midi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device."); +MODULE_PARM_SYNTAX(midi_map, "default:0,skill:advanced"); +MODULE_PARM(amidi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); +MODULE_PARM_SYNTAX(amidi_map, "default:1,skill:advanced"); +#endif /* CONFIG_SND_OSSEMUL */ + +static int snd_rawmidi_free(snd_rawmidi_t *rawmidi); +static int snd_rawmidi_dev_free(snd_device_t *device); +static int snd_rawmidi_dev_register(snd_device_t *device); +static int snd_rawmidi_dev_disconnect(snd_device_t *device); +static int snd_rawmidi_dev_unregister(snd_device_t *device); + +snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES]; + +static DECLARE_MUTEX(register_mutex); + +static inline unsigned short snd_rawmidi_file_flags(struct file *file) +{ + switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_WRITE: + return SNDRV_RAWMIDI_LFLG_OUTPUT; + case FMODE_READ: + return SNDRV_RAWMIDI_LFLG_INPUT; + default: + return SNDRV_RAWMIDI_LFLG_OPEN; + } +} + +static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + return runtime->avail >= runtime->avail_min; +} + +static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + return runtime->avail >= runtime->avail_min && + (!substream->append || runtime->avail >= count); +} + +static int snd_rawmidi_init(snd_rawmidi_substream_t *substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + spin_lock_init(&runtime->lock); + init_waitqueue_head(&runtime->sleep); + runtime->event = NULL; + runtime->buffer_size = PAGE_SIZE; + runtime->avail_min = 1; + if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) + runtime->avail = 0; + else + runtime->avail = runtime->buffer_size; + if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + runtime->appl_ptr = runtime->hw_ptr = 0; + return 0; +} + +static int snd_rawmidi_done_buffer(snd_rawmidi_runtime_t *runtime) +{ + if (runtime->buffer) { + kfree(runtime->buffer); + runtime->buffer = NULL; + } + return 0; +} + +int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + substream->ops->trigger(substream, 0); + runtime->trigger = 0; + runtime->drain = 0; + /* interrupts are not enabled at this moment, + so spinlock is not required */ + runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->avail = runtime->buffer_size; + return 0; +} + +int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream) +{ + int err; + long timeout; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + err = 0; + runtime->drain = 1; + while (runtime->avail < runtime->buffer_size) { + timeout = interruptible_sleep_on_timeout(&runtime->sleep, 10 * HZ); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + if (runtime->avail < runtime->buffer_size && !timeout) { + snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size); + err = -EIO; + break; + } + } + runtime->drain = 0; + if (err != -ERESTARTSYS) { + /* we need wait a while to make sure that Tx FIFOs are empty */ + if (substream->ops->drain) + substream->ops->drain(substream); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + snd_rawmidi_drop_output(substream); + } + return err; +} + +int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + substream->ops->trigger(substream, 0); + runtime->trigger = 0; + runtime->drain = 0; + /* interrupts aren't enabled at this moment, so spinlock isn't needed */ + runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->avail = 0; + return 0; +} + +int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, + int mode, snd_rawmidi_file_t * rfile) +{ + snd_rawmidi_t *rmidi; + struct list_head *list1, *list2; + snd_rawmidi_substream_t *sinput, *soutput; + snd_rawmidi_runtime_t *input = NULL, *output = NULL; + int err; + + if (rfile) + rfile->input = rfile->output = NULL; + rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; + if (rmidi == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_module_get(rmidi->card->module)) { + err = -EFAULT; + goto __error1; + } + down(&rmidi->open_mutex); + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) { + err = -ENXIO; + goto __error; + } + if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { + err = -ENODEV; + goto __error; + } + if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >= + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { + err = -EAGAIN; + goto __error; + } + } + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) { + err = -ENXIO; + goto __error; + } + if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { + err = -ENODEV; + goto __error; + } + if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >= + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { + err = -EAGAIN; + goto __error; + } + } + list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next; + while (1) { + if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + sinput = NULL; + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + err = -EAGAIN; + goto __error; + } + break; + } + sinput = list_entry(list1, snd_rawmidi_substream_t, list); + if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened) + goto __nexti; + if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number)) + break; + __nexti: + list1 = list1->next; + } + list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next; + while (1) { + if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + soutput = NULL; + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + err = -EAGAIN; + goto __error; + } + break; + } + soutput = list_entry(list2, snd_rawmidi_substream_t, list); + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) { + if (soutput->opened && !soutput->append) + goto __nexto; + } else { + if (soutput->opened) + goto __nexto; + } + } + if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number)) + break; + __nexto: + list2 = list2->next; + } + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + input = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + if (input == NULL) { + err = -ENOMEM; + goto __error; + } + sinput->runtime = input; + if (snd_rawmidi_init(sinput) < 0) { + err = -ENOMEM; + goto __error; + } + if ((err = sinput->ops->open(sinput)) < 0) { + sinput->runtime = NULL; + goto __error; + } + sinput->opened = 1; + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++; + } else { + sinput = NULL; + } + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (soutput->opened) + goto __skip_output; + output = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + if (output == NULL) { + err = -ENOMEM; + goto __error; + } + soutput->runtime = output; + if (snd_rawmidi_init(soutput) < 0) { + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + sinput->ops->close(sinput); + sinput->runtime = NULL; + } + err = -ENOMEM; + goto __error; + } + if ((err = soutput->ops->open(soutput)) < 0) { + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + sinput->ops->close(sinput); + sinput->runtime = NULL; + } + soutput->runtime = NULL; + goto __error; + } + __skip_output: + soutput->opened = 1; + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) + soutput->append = 1; + if (soutput->use_count++ == 0) + soutput->active_sensing = 1; + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++; + } else { + soutput = NULL; + } + up(&rmidi->open_mutex); + if (rfile) { + rfile->rmidi = rmidi; + rfile->input = sinput; + rfile->output = soutput; + } + return 0; + + __error: + if (input != NULL) { + snd_rawmidi_done_buffer(input); + kfree(input); + } + if (output != NULL) { + snd_rawmidi_done_buffer(output); + kfree(output); + } + module_put(rmidi->card->module); + up(&rmidi->open_mutex); + __error1: + return err; +} + +static int snd_rawmidi_open(struct inode *inode, struct file *file) +{ + int maj = major(inode->i_rdev); + int cardnum; + snd_card_t *card; + int device, subdevice; + unsigned short fflags; + int err; + snd_rawmidi_t *rmidi; + snd_rawmidi_file_t *rawmidi_file; + wait_queue_t wait; + struct list_head *list; + snd_ctl_file_t *kctl; + + switch (maj) { + case CONFIG_SND_MAJOR: + cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + cardnum %= SNDRV_CARDS; + device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_RAWMIDI; + device %= SNDRV_MINOR_RAWMIDIS; + break; +#ifdef CONFIG_SND_OSSEMUL + case SOUND_MAJOR: + cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + cardnum %= SNDRV_CARDS; + device = SNDRV_MINOR_OSS_DEVICE(minor(inode->i_rdev)) == SNDRV_MINOR_OSS_MIDI ? + midi_map[cardnum] : amidi_map[cardnum]; + break; +#endif + default: + return -ENXIO; + } + + rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; + if (rmidi == NULL) + return -ENODEV; +#ifdef CONFIG_SND_OSSEMUL + if (maj == SOUND_MAJOR && !rmidi->ossreg) + return -ENXIO; +#endif + if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) + return -EINVAL; /* invalid combination */ + card = rmidi->card; + err = snd_card_file_add(card, file); + if (err < 0) + return -ENODEV; + fflags = snd_rawmidi_file_flags(file); + if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */ + fflags |= SNDRV_RAWMIDI_LFLG_APPEND; + rawmidi_file = snd_magic_kmalloc(snd_rawmidi_file_t, 0, GFP_KERNEL); + if (rawmidi_file == NULL) { + snd_card_file_remove(card, file); + return -ENOMEM; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&rmidi->open_wait, &wait); + while (1) { + subdevice = -1; + down_read(&card->controls_rwsem); + list_for_each(list, &card->ctl_files) { + kctl = snd_ctl_file(list); + if (kctl->pid == current->pid) { + subdevice = kctl->prefer_rawmidi_subdevice; + break; + } + } + up_read(&card->controls_rwsem); + err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } +#ifdef CONFIG_SND_OSSEMUL + if (rawmidi_file->input && rawmidi_file->input->runtime) + rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR); + if (rawmidi_file->output && rawmidi_file->output->runtime) + rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR); +#endif + set_current_state(TASK_RUNNING); + remove_wait_queue(&rmidi->open_wait, &wait); + if (err >= 0) { + file->private_data = rawmidi_file; + } else { + snd_card_file_remove(card, file); + snd_magic_kfree(rawmidi_file); + } + return err; +} + +int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + + snd_assert(rfile != NULL, return -ENXIO); + snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO); + rmidi = rfile->rmidi; + down(&rmidi->open_mutex); + if (rfile->input != NULL) { + substream = rfile->input; + rfile->input = NULL; + runtime = substream->runtime; + runtime->trigger = 0; + substream->ops->trigger(substream, 0); + substream->ops->close(substream); + snd_rawmidi_done_buffer(runtime); + if (runtime->private_free != NULL) + runtime->private_free(substream); + kfree(runtime); + substream->runtime = NULL; + substream->opened = 0; + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--; + } + if (rfile->output != NULL) { + substream = rfile->output; + rfile->output = NULL; + if (--substream->use_count == 0) { + runtime = substream->runtime; + if (substream->active_sensing) { + unsigned char buf = 0xfe; + /* sending single active sensing message to shut the device up */ + snd_rawmidi_kernel_write(substream, &buf, 1); + } + if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) + substream->ops->trigger(substream, 0); + substream->ops->close(substream); + snd_rawmidi_done_buffer(runtime); + if (runtime->private_free != NULL) + runtime->private_free(substream); + kfree(runtime); + substream->runtime = NULL; + substream->opened = 0; + substream->append = 0; + } + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--; + } + up(&rmidi->open_mutex); + module_put(rmidi->card->module); + return 0; +} + +static int snd_rawmidi_release(struct inode *inode, struct file *file) +{ + snd_rawmidi_file_t *rfile; + snd_rawmidi_t *rmidi; + int err; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + err = snd_rawmidi_kernel_release(rfile); + rmidi = rfile->rmidi; + wake_up(&rmidi->open_wait); + snd_magic_kfree(rfile); + snd_card_file_remove(rmidi->card, file); + return err; +} + +int snd_rawmidi_info(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t *info) +{ + snd_rawmidi_t *rmidi; + + if (substream == NULL) + return -ENODEV; + rmidi = substream->rmidi; + memset(info, 0, sizeof(*info)); + info->card = rmidi->card->number; + info->device = rmidi->device; + info->subdevice = substream->number; + info->stream = substream->stream; + info->flags = rmidi->info_flags; + strcpy(info->id, rmidi->id); + strcpy(info->name, rmidi->name); + strcpy(info->subname, substream->name); + info->subdevices_count = substream->pstr->substream_count; + info->subdevices_avail = (substream->pstr->substream_count - + substream->pstr->substream_opened); + return 0; +} + +static int snd_rawmidi_info_user(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t * _info) +{ + snd_rawmidi_info_t info; + int err; + if ((err = snd_rawmidi_info(substream, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) + return -EFAULT; + return 0; +} + +int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_str_t *pstr; + snd_rawmidi_substream_t *substream; + struct list_head *list; + if (info->device >= SNDRV_RAWMIDI_DEVICES) + return -ENXIO; + rmidi = snd_rawmidi_devices[card->number * SNDRV_RAWMIDI_DEVICES + info->device]; + if (info->stream < 0 || info->stream > 1) + return -EINVAL; + pstr = &rmidi->streams[info->stream]; + if (pstr->substream_count == 0) + return -ENOENT; + if (info->subdevice >= pstr->substream_count) + return -ENXIO; + list_for_each(list, &pstr->substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + if ((unsigned int)substream->number == info->subdevice) + return snd_rawmidi_info(substream, info); + } + return -ENXIO; +} + +static int snd_rawmidi_info_select_user(snd_card_t *card, + snd_rawmidi_info_t *_info) +{ + int err; + snd_rawmidi_info_t info; + if (get_user(info.device, &_info->device)) + return -EFAULT; + if (get_user(info.stream, &_info->stream)) + return -EFAULT; + if (get_user(info.subdevice, &_info->subdevice)) + return -EFAULT; + if ((err = snd_rawmidi_info_select(card, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) + return -EFAULT; + return 0; +} + +int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, + snd_rawmidi_params_t * params) +{ + char *newbuf; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (substream->append && substream->use_count > 1) + return -EBUSY; + snd_rawmidi_drain_output(substream); + if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->avail_min < 1 || params->avail_min > params->buffer_size) { + return -EINVAL; + } + if (params->buffer_size != runtime->buffer_size) { + if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + kfree(runtime->buffer); + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; + } + runtime->avail_min = params->avail_min; + substream->active_sensing = !params->no_active_sensing; + return 0; +} + +int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, + snd_rawmidi_params_t * params) +{ + char *newbuf; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + snd_rawmidi_drain_input(substream); + if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->avail_min < 1 || params->avail_min > params->buffer_size) { + return -EINVAL; + } + if (params->buffer_size != runtime->buffer_size) { + if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + kfree(runtime->buffer); + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; + } + runtime->avail_min = params->avail_min; + return 0; +} + +static int snd_rawmidi_output_status(snd_rawmidi_substream_t * substream, + snd_rawmidi_status_t * status) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + memset(status, 0, sizeof(*status)); + status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + spin_lock_irq(&runtime->lock); + status->avail = runtime->avail; + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_rawmidi_input_status(snd_rawmidi_substream_t * substream, + snd_rawmidi_status_t * status) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + memset(status, 0, sizeof(*status)); + status->stream = SNDRV_RAWMIDI_STREAM_INPUT; + spin_lock_irq(&runtime->lock); + status->avail = runtime->avail; + status->xruns = runtime->xruns; + runtime->xruns = 0; + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_rawmidi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_rawmidi_file_t *rfile; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + if (((cmd >> 8) & 0xff) != 'W') + return -ENOTTY; + switch (cmd) { + case SNDRV_RAWMIDI_IOCTL_PVERSION: + return put_user(SNDRV_RAWMIDI_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_RAWMIDI_IOCTL_INFO: + { + snd_rawmidi_stream_t stream; + snd_rawmidi_info_t *info = (snd_rawmidi_info_t *) arg; + if (get_user(stream, &info->stream)) + return -EFAULT; + switch (stream) { + case SNDRV_RAWMIDI_STREAM_INPUT: + return snd_rawmidi_info_user(rfile->input, info); + case SNDRV_RAWMIDI_STREAM_OUTPUT: + return snd_rawmidi_info_user(rfile->output, info); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_PARAMS: + { + snd_rawmidi_params_t params; + if (copy_from_user(¶ms, (snd_rawmidi_params_t *) arg, sizeof(snd_rawmidi_params_t))) + return -EFAULT; + switch (params.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_output_params(rfile->output, ¶ms); + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + return snd_rawmidi_input_params(rfile->input, ¶ms); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_STATUS: + { + int err = 0; + snd_rawmidi_status_t status; + if (copy_from_user(&status, (snd_rawmidi_status_t *) arg, sizeof(snd_rawmidi_status_t))) + return -EFAULT; + switch (status.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + err = snd_rawmidi_output_status(rfile->output, &status); + break; + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + err = snd_rawmidi_input_status(rfile->input, &status); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + if (copy_to_user((snd_rawmidi_status_t *) arg, &status, sizeof(snd_rawmidi_status_t))) + return -EFAULT; + return 0; + } + case SNDRV_RAWMIDI_IOCTL_DROP: + { + int val; + if (get_user(val, (long *) arg)) + return -EFAULT; + switch (val) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_drop_output(rfile->output); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_DRAIN: + { + int val; + if (get_user(val, (long *) arg)) + return -EFAULT; + switch (val) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_drain_output(rfile->output); + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + return snd_rawmidi_drain_input(rfile->input); + default: + return -EINVAL; + } + } +#ifdef CONFIG_SND_DEBUG + default: + snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd); +#endif + } + return -ENOTTY; +} + +int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_RAWMIDI_DEVICES; + switch (cmd) { + case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_RAWMIDI_DEVICES) { + if (snd_rawmidi_devices[tmp + device]) + break; + device++; + } + if (device == SNDRV_RAWMIDI_DEVICES) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE: + { + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + control->prefer_rawmidi_subdevice = val; + return 0; + } + case SNDRV_CTL_IOCTL_RAWMIDI_INFO: + return snd_rawmidi_info_select_user(card, (snd_rawmidi_info_t *)arg); + } + return -ENOIOCTLCMD; +} + +/** + * snd_rawmidi_receive - receive the input data from the device + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: the data size to read + * + * Reads the data from the internal buffer. + * + * Returns the size of read data, or a negative error code on failure. + */ +int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count) +{ + unsigned long flags; + int result = 0, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_receive: input is not active!!!\n"); + return -EINVAL; + } + spin_lock_irqsave(&runtime->lock, flags); + if (count == 1) { /* special case, faster code */ + substream->bytes++; + if (runtime->avail < runtime->buffer_size) { + runtime->buffer[runtime->hw_ptr++] = buffer[0]; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail++; + result++; + } else { + runtime->xruns++; + } + } else { + substream->bytes += count; + count1 = runtime->buffer_size - runtime->hw_ptr; + if (count1 > count) + count1 = count; + if (count1 > (int)(runtime->buffer_size - runtime->avail)) + count1 = runtime->buffer_size - runtime->avail; + memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1); + runtime->hw_ptr += count1; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail += count1; + count -= count1; + result += count1; + if (count > 0) { + buffer += count1; + count1 = count; + if (count1 > (int)(runtime->buffer_size - runtime->avail)) { + count1 = runtime->buffer_size - runtime->avail; + runtime->xruns = count - count1; + } + if (count1 > 0) { + memcpy(runtime->buffer, buffer, count1); + runtime->hw_ptr = count1; + runtime->avail += count1; + result += count1; + } + } + } + if (result > 0 && runtime->event == NULL) { + if (snd_rawmidi_ready(substream)) + wake_up(&runtime->sleep); + } + spin_unlock_irqrestore(&runtime->lock, flags); + if (result > 0 && runtime->event) + runtime->event(substream); + return result; +} + +static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream, + unsigned char *buf, long count, int kernel) +{ + unsigned long flags; + long result = 0, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + while (count > 0 && runtime->avail) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) + count1 = count; + spin_lock_irqsave(&runtime->lock, flags); + if (count1 > (int)runtime->avail) + count1 = runtime->avail; + if (kernel) { + memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1); + } else { + if (copy_to_user(buf + result, runtime->buffer + runtime->appl_ptr, count1)) { + spin_unlock_irqrestore(&runtime->lock, flags); + return result > 0 ? result : -EFAULT; + } + } + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + spin_unlock_irqrestore(&runtime->lock, flags); + result += count1; + count -= count1; + } + return result; +} + +long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count) +{ + substream->runtime->trigger = 1; + substream->ops->trigger(substream, 1); + return snd_rawmidi_kernel_read1(substream, buf, count, 1); +} + +static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + long result; + int count1; + snd_rawmidi_file_t *rfile; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + substream = rfile->input; + if (substream == NULL) + return -EIO; + runtime = substream->runtime; + runtime->trigger = 1; + substream->ops->trigger(substream, 1); + result = 0; + while (count > 0) { + spin_lock_irq(&runtime->lock); + while (!snd_rawmidi_ready(substream)) { + wait_queue_t wait; + if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EAGAIN; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + count1 = snd_rawmidi_kernel_read1(substream, buf, count, 0); + result += count1; + buf += count1; + count -= count1; + } + return result; +} + +/** + * snd_rawmidi_transmit_empty - check whether the output buffer is empty + * @substream: the rawmidi substream + * + * Returns 1 if the internal output buffer is empty, 0 if not. + */ +int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + int result; + unsigned long flags; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n"); + return 1; + } + spin_lock_irqsave(&runtime->lock, flags); + result = runtime->avail >= runtime->buffer_size; + if (result) + runtime->trigger = 1; + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} + +/** + * snd_rawmidi_transmit_peek - copy data from the internal buffer + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: data size to transfer + * + * Copies data from the internal output buffer to the given buffer. + * + * Call this in the interrupt handler when the midi output is ready, + * and call snd_rawmidi_transmit_ack() after the transmission is + * finished. + * + * Returns the size of copied data, or a negative error code on failure. + */ +int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + unsigned long flags; + int result, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n"); + return -EINVAL; + } + result = 0; + spin_lock_irqsave(&runtime->lock, flags); + if (runtime->avail >= runtime->buffer_size) { + /* warning: lowlevel layer MUST trigger down the hardware */ + runtime->trigger = 0; + goto __skip; + } + if (count == 1) { /* special case, faster code */ + *buffer = runtime->buffer[runtime->hw_ptr]; + result++; + } else { + count1 = runtime->buffer_size - runtime->hw_ptr; + if (count1 > count) + count1 = count; + if (count1 > (int)(runtime->buffer_size - runtime->avail)) + count1 = runtime->buffer_size - runtime->avail; + memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1); + count -= count1; + result += count1; + if (count > 0) + memcpy(buffer + count1, runtime->buffer, count); + } + __skip: + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} + +/** + * snd_rawmidi_transmit_ack - acknowledge the transmission + * @substream: the rawmidi substream + * @count: the tranferred count + * + * Advances the hardware pointer for the internal output buffer with + * the given size and updates the condition. + * Call after the transmission is finished. + * + * Returns the advanced size if successful, or a negative error code on failure. + */ +int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) +{ + unsigned long flags; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n"); + return -EINVAL; + } + spin_lock_irqsave(&runtime->lock, flags); + snd_assert(runtime->avail + count <= runtime->buffer_size, ); + runtime->hw_ptr += count; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail += count; + substream->bytes += count; + if (runtime->drain) + wake_up(&runtime->sleep); + else + if (count > 0 && runtime->event == NULL) + if (snd_rawmidi_ready(substream)) + wake_up(&runtime->sleep); + spin_unlock_irqrestore(&runtime->lock, flags); + if (count > 0 && runtime->event) + runtime->event(substream); + return count; +} + +/** + * snd_rawmidi_transmit - copy from the buffer to the device + * @substream: the rawmidi substream + * @buf: the buffer pointer + * @count: the data size to transfer + * + * Copies data from the buffer to the device and advances the pointer. + * + * Returns the copied size if successful, or a negative error code on failure. + */ +int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + count = snd_rawmidi_transmit_peek(substream, buffer, count); + if (count < 0) + return count; + return snd_rawmidi_transmit_ack(substream, count); +} + +static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count, int kernel) +{ + unsigned long flags; + long count1, result; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + snd_assert(buf != NULL, return -EINVAL); + snd_assert(runtime->buffer != NULL, return -EINVAL); + + result = 0; + spin_lock_irqsave(&runtime->lock, flags); + if (substream->append) { + if ((long)runtime->avail < count) { + spin_unlock_irqrestore(&runtime->lock, flags); + return -EAGAIN; + } + } + while (count > 0 && runtime->avail > 0) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) + count1 = count; + if (count1 > (long)runtime->avail) + count1 = runtime->avail; + if (kernel) { + memcpy(runtime->buffer + runtime->appl_ptr, buf, count1); + } else { + if (copy_from_user(runtime->buffer + runtime->appl_ptr, buf, count1)) { + result = result > 0 ? result : -EFAULT; + goto __end; + } + } + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + result += count1; + buf += count1; + count -= count1; + } + __end: + if (result > 0) + runtime->trigger = 1; + count1 = runtime->avail < runtime->buffer_size; + spin_unlock_irqrestore(&runtime->lock, flags); + if (count1) + substream->ops->trigger(substream, 1); + return result; +} + +long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count) +{ + return snd_rawmidi_kernel_write1(substream, buf, count, 1); +} + +static ssize_t snd_rawmidi_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + long result, timeout; + int count1; + snd_rawmidi_file_t *rfile; + snd_rawmidi_runtime_t *runtime; + snd_rawmidi_substream_t *substream; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + substream = rfile->output; + runtime = substream->runtime; + /* we cannot put an atomic message to our buffer */ + if (substream->append && count > runtime->buffer_size) + return -EIO; + result = 0; + while (count > 0) { + spin_lock_irq(&runtime->lock); + while (!snd_rawmidi_ready_append(substream, count)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EAGAIN; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(30 * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail && !timeout) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + count1 = snd_rawmidi_kernel_write1(substream, buf, count, 0); + if (count1 < 0) + continue; + result += count1; + buf += count1; + if ((size_t)count1 < count && (file->f_flags & O_NONBLOCK)) + break; + count -= count1; + } + while (file->f_flags & O_SYNC) { + spin_lock_irq(&runtime->lock); + while (runtime->avail != runtime->buffer_size) { + wait_queue_t wait; + unsigned int last_avail = runtime->avail; + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(30 * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (runtime->avail == last_avail && !timeout) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + } + return result; +} + +static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait) +{ + snd_rawmidi_file_t *rfile; + snd_rawmidi_runtime_t *runtime; + unsigned int mask; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return 0); + if (rfile->input != NULL) { + runtime = rfile->input->runtime; + runtime->trigger = 1; + rfile->input->ops->trigger(rfile->input, 1); + poll_wait(file, &runtime->sleep, wait); + } + if (rfile->output != NULL) { + runtime = rfile->output->runtime; + poll_wait(file, &runtime->sleep, wait); + } + mask = 0; + if (rfile->input != NULL) { + if (snd_rawmidi_ready(rfile->input)) + mask |= POLLIN | POLLRDNORM; + } + if (rfile->output != NULL) { + if (snd_rawmidi_ready(rfile->output)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} + +/* + + */ + +static void snd_rawmidi_proc_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + struct list_head *list; + + rmidi = snd_magic_cast(snd_rawmidi_t, entry->private_data, return); + snd_iprintf(buffer, "%s\n\n", rmidi->name); + down(&rmidi->open_mutex); + if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { + list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_iprintf(buffer, + "Output %d\n" + " Tx bytes : %lu\n", + substream->number, + (unsigned long) substream->bytes); + if (substream->opened) { + runtime = substream->runtime; + snd_iprintf(buffer, + " Mode : %s\n" + " Buffer size : %lu\n" + " Avail : %lu\n", + runtime->oss ? "OSS compatible" : "native", + (unsigned long) runtime->buffer_size, + (unsigned long) runtime->avail); + } + } + } + if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) { + list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_iprintf(buffer, + "Input %d\n" + " Rx bytes : %lu\n", + substream->number, + (unsigned long) substream->bytes); + if (substream->opened) { + runtime = substream->runtime; + snd_iprintf(buffer, + " Buffer size : %lu\n" + " Avail : %lu\n" + " Overruns : %lu\n", + (unsigned long) runtime->buffer_size, + (unsigned long) runtime->avail, + (unsigned long) runtime->xruns); + } + } + } + up(&rmidi->open_mutex); +} + +/* + * Register functions + */ + +static struct file_operations snd_rawmidi_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = snd_rawmidi_read, + .write = snd_rawmidi_write, + .open = snd_rawmidi_open, + .release = snd_rawmidi_release, + .poll = snd_rawmidi_poll, + .ioctl = snd_rawmidi_ioctl, +}; + +static snd_minor_t snd_rawmidi_reg = +{ + .comment = "raw midi", + .f_ops = &snd_rawmidi_f_ops, +}; + +static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi, + snd_rawmidi_str_t *stream, + int direction, + int count) +{ + snd_rawmidi_substream_t *substream; + int idx; + + INIT_LIST_HEAD(&stream->substreams); + for (idx = 0; idx < count; idx++) { + substream = snd_kcalloc(sizeof(snd_rawmidi_substream_t), GFP_KERNEL); + if (substream == NULL) + return -ENOMEM; + substream->stream = direction; + substream->number = idx; + substream->rmidi = rmidi; + substream->pstr = stream; + list_add_tail(&substream->list, &stream->substreams); + stream->substream_count++; + } + return 0; +} + +/** + * snd_rawmidi_new - create a rawmidi instance + * @card: the card instance + * @id: the id string + * @device: the device index + * @output_count: the number of output streams + * @input_count: the number of input streams + * @rrawmidi: the pointer to store the new rawmidi instance + * + * Creates a new rawmidi instance. + * Use snd_rawmidi_set_ops() to set the operators to the new instance. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_rawmidi_new(snd_card_t * card, char *id, int device, + int output_count, int input_count, + snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_rawmidi_dev_free, + .dev_register = snd_rawmidi_dev_register, + .dev_disconnect = snd_rawmidi_dev_disconnect, + .dev_unregister = snd_rawmidi_dev_unregister + }; + + snd_assert(rrawmidi != NULL, return -EINVAL); + *rrawmidi = NULL; + snd_assert(card != NULL, return -ENXIO); + rmidi = snd_magic_kcalloc(snd_rawmidi_t, 0, GFP_KERNEL); + if (rmidi == NULL) + return -ENOMEM; + rmidi->card = card; + rmidi->device = device; + init_MUTEX(&rmidi->open_mutex); + init_waitqueue_head(&rmidi->open_wait); + if (id != NULL) + strncpy(rmidi->id, id, sizeof(rmidi->id) - 1); + if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], SNDRV_RAWMIDI_STREAM_INPUT, input_count)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], SNDRV_RAWMIDI_STREAM_OUTPUT, output_count)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + *rrawmidi = rmidi; + return 0; +} + +static void snd_rawmidi_free_substreams(snd_rawmidi_str_t *stream) +{ + snd_rawmidi_substream_t *substream; + + while (!list_empty(&stream->substreams)) { + substream = list_entry(stream->substreams.next, snd_rawmidi_substream_t, list); + list_del(&substream->list); + kfree(substream); + } +} + +static int snd_rawmidi_free(snd_rawmidi_t *rmidi) +{ + snd_assert(rmidi != NULL, return -ENXIO); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); + if (rmidi->private_free) + rmidi->private_free(rmidi); + snd_magic_kfree(rmidi); + return 0; +} + +static int snd_rawmidi_dev_free(snd_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + return snd_rawmidi_free(rmidi); +} + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->private_data, return); + rmidi->seq_dev = NULL; +} +#endif + +static int snd_rawmidi_dev_register(snd_device_t *device) +{ + int idx, err; + snd_info_entry_t *entry; + char name[16]; + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + + if (rmidi->device >= SNDRV_RAWMIDI_DEVICES) + return -ENOMEM; + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + if (snd_rawmidi_devices[idx] != NULL) { + up(®ister_mutex); + return -EBUSY; + } + snd_rawmidi_devices[idx] = rmidi; + sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, + rmidi->card, rmidi->device, + &snd_rawmidi_reg, name)) < 0) { + snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return err; + } + if (rmidi->ops && rmidi->ops->dev_register && + (err = rmidi->ops->dev_register(rmidi)) < 0) { + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return err; + } +#ifdef CONFIG_SND_OSSEMUL + rmidi->ossreg = 0; + if ((int)rmidi->device == midi_map[rmidi->card->number]) { + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, + rmidi->card, 0, &snd_rawmidi_reg, name) < 0) { + snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); + } else { + rmidi->ossreg++; +#ifdef SNDRV_OSS_INFO_DEV_MIDI + snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name); +#endif + } + } + if ((int)rmidi->device == amidi_map[rmidi->card->number]) { + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, + rmidi->card, 1, &snd_rawmidi_reg, name) < 0) { + snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); + } else { + rmidi->ossreg++; + } + } +#endif /* CONFIG_SND_OSSEMUL */ + up(®ister_mutex); + sprintf(name, "midi%d", rmidi->device); + entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rmidi; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_rawmidi_proc_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rmidi->proc_entry = entry; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ + if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { + rmidi->seq_dev->private_data = rmidi; + rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free; + sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device); + snd_device_register(rmidi->card, rmidi->seq_dev); + } + } +#endif + return 0; +} + +static int snd_rawmidi_dev_disconnect(snd_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + int idx; + + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return 0; +} + +static int snd_rawmidi_dev_unregister(snd_device_t *device) +{ + int idx; + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + + snd_assert(rmidi != NULL, return -ENXIO); + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + snd_rawmidi_devices[idx] = NULL; + if (rmidi->proc_entry) { + snd_info_unregister(rmidi->proc_entry); + rmidi->proc_entry = NULL; + } +#ifdef CONFIG_SND_OSSEMUL + if (rmidi->ossreg) { + if ((int)rmidi->device == midi_map[rmidi->card->number]) { + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0); +#ifdef SNDRV_OSS_INFO_DEV_MIDI + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number); +#endif + } + if ((int)rmidi->device == amidi_map[rmidi->card->number]) + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1); + rmidi->ossreg = 0; + } +#endif /* CONFIG_SND_OSSEMUL */ + if (rmidi->ops && rmidi->ops->dev_unregister) + rmidi->ops->dev_unregister(rmidi); + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + up(®ister_mutex); +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (rmidi->seq_dev) { + snd_device_free(rmidi->card, rmidi->seq_dev); + rmidi->seq_dev = NULL; + } +#endif + return snd_rawmidi_free(rmidi); +} + +/** + * snd_rawmidi_set_ops - set the rawmidi operators + * @rmidi: the rawmidi instance + * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX + * @ops: the operator table + * + * Sets the rawmidi operators for the given stream direction. + */ +void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops) +{ + struct list_head *list; + snd_rawmidi_substream_t *substream; + + list_for_each(list, &rmidi->streams[stream].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + substream->ops = ops; + } +} + +/* + * ENTRY functions + */ + +static int __init alsa_rawmidi_init(void) +{ + + snd_ctl_register_ioctl(snd_rawmidi_control_ioctl); +#ifdef CONFIG_SND_OSSEMUL + { int i; + /* check device map table */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { + snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, midi_map[i]); + midi_map[i] = 0; + } + if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) { + snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, amidi_map[i]); + amidi_map[i] = 1; + } + } + } +#endif /* CONFIG_SND_OSSEMUL */ + return 0; +} + +static void __exit alsa_rawmidi_exit(void) +{ + snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl); +} + +module_init(alsa_rawmidi_init) +module_exit(alsa_rawmidi_exit) + +EXPORT_SYMBOL(snd_rawmidi_output_params); +EXPORT_SYMBOL(snd_rawmidi_input_params); +EXPORT_SYMBOL(snd_rawmidi_drop_output); +EXPORT_SYMBOL(snd_rawmidi_drain_output); +EXPORT_SYMBOL(snd_rawmidi_drain_input); +EXPORT_SYMBOL(snd_rawmidi_receive); +EXPORT_SYMBOL(snd_rawmidi_transmit_empty); +EXPORT_SYMBOL(snd_rawmidi_transmit_peek); +EXPORT_SYMBOL(snd_rawmidi_transmit_ack); +EXPORT_SYMBOL(snd_rawmidi_transmit); +EXPORT_SYMBOL(snd_rawmidi_new); +EXPORT_SYMBOL(snd_rawmidi_set_ops); +EXPORT_SYMBOL(snd_rawmidi_info); +EXPORT_SYMBOL(snd_rawmidi_info_select); +EXPORT_SYMBOL(snd_rawmidi_kernel_open); +EXPORT_SYMBOL(snd_rawmidi_kernel_release); +EXPORT_SYMBOL(snd_rawmidi_kernel_read); +EXPORT_SYMBOL(snd_rawmidi_kernel_write); diff -urN linux-2.4.21-rc1.orig/sound/core/rtctimer.c linux/sound/core/rtctimer.c --- linux-2.4.21-rc1.orig/sound/core/rtctimer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/rtctimer.c 2003-02-04 06:25:08.000000000 -0700 @@ -0,0 +1,189 @@ +/* + * RTC based high-frequency timer + * + * Copyright (C) 2000 Takashi Iwai + * based on rtctimer.c by Steve Ratcliffe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 12) /* FIXME: which 2.2.x kernel? */ +#include +#else +#include +#endif + +#define RTC_FREQ 1024 /* default frequency */ +#define NANO_SEC 1000000000L /* 10^9 in sec */ + +/* + * prototypes + */ +static int rtctimer_open(snd_timer_t *t); +static int rtctimer_close(snd_timer_t *t); +static int rtctimer_start(snd_timer_t *t); +static int rtctimer_stop(snd_timer_t *t); + + +/* + * The hardware dependent description for this timer. + */ +static struct _snd_timer_hardware rtc_hw = { + .flags = SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, + .ticks = 100000000L, /* FIXME: XXX */ + .open = rtctimer_open, + .close = rtctimer_close, + .start = rtctimer_start, + .stop = rtctimer_stop, +}; + +int rtctimer_freq = RTC_FREQ; /* frequency */ +static snd_timer_t *rtctimer; +static atomic_t rtc_inc = ATOMIC_INIT(0); +static rtc_task_t rtc_task; + + +static int +rtctimer_open(snd_timer_t *t) +{ + int err; + + err = rtc_register(&rtc_task); + if (err < 0) + return err; + t->private_data = &rtc_task; + return 0; +} + +static int +rtctimer_close(snd_timer_t *t) +{ + rtc_task_t *rtc = t->private_data; + if (rtc) { + rtc_unregister(rtc); + t->private_data = NULL; + } + return 0; +} + +static int +rtctimer_start(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + snd_assert(rtc != NULL, return -EINVAL); + rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); + rtc_control(rtc, RTC_PIE_ON, 0); + atomic_set(&rtc_inc, 0); + return 0; +} + +static int +rtctimer_stop(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + snd_assert(rtc != NULL, return -EINVAL); + rtc_control(rtc, RTC_PIE_OFF, 0); + return 0; +} + +/* + * interrupt + */ +static void rtctimer_interrupt(void *private_data) +{ + int ticks; + + atomic_inc(&rtc_inc); + ticks = atomic_read(&rtc_inc); + snd_timer_interrupt((snd_timer_t*)private_data, ticks); + atomic_sub(ticks, &rtc_inc); +} + + +/* + * ENTRY functions + */ +static int __init rtctimer_init(void) +{ + int order, err; + snd_timer_t *timer; + + if (rtctimer_freq < 2 || rtctimer_freq > 8192) { + snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); + return -EINVAL; + } + for (order = 1; rtctimer_freq > order; order <<= 1) + ; + if (rtctimer_freq != order) { + snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); + return -EINVAL; + } + + /* Create a new timer and set up the fields */ + err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); + if (err < 0) + return err; + + strcpy(timer->name, "RTC timer"); + timer->hw = rtc_hw; + timer->hw.resolution = NANO_SEC / rtctimer_freq; + + /* set up RTC callback */ + rtc_task.func = rtctimer_interrupt; + rtc_task.private_data = timer; + + err = snd_timer_global_register(timer); + if (err < 0) { + snd_timer_global_free(timer); + return err; + } + rtctimer = timer; /* remember this */ + + return 0; +} + +static void __exit rtctimer_exit(void) +{ + if (rtctimer) { + snd_timer_global_unregister(rtctimer); + rtctimer = NULL; + } +} + + +/* + * exported stuff + */ +module_init(rtctimer_init) +module_exit(rtctimer_exit) + +MODULE_PARM(rtctimer_freq, "i"); +MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); + +MODULE_LICENSE("GPL"); + +#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/core/seq/Makefile linux/sound/core/seq/Makefile --- linux-2.4.21-rc1.orig/sound/core/seq/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,115 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := sq.o + +subdir-y := instr +subdir-m := $(subdir-y) +obj-y += instr/instr.o + +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) + subdir-y += oss + obj-y += oss/oss.o +endif + +list-multi := snd-seq-device.o snd-seq.o snd-seq-midi.o snd-seq-midi-emul.o \ + snd-seq-midi-event.o snd-seq-instr.o snd-seq-dummy.o \ + snd-seq-virmidi.o + +export-objs := seq_device.o seq.o seq_ports.o seq_midi_emul.o \ + seq_midi_event.o seq_virmidi.o seq_instr.o + +snd-seq-device-objs := seq_device.o +snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ + seq_fifo.o seq_prioq.o seq_timer.o \ + seq_system.o seq_ports.o seq_info.o +snd-seq-midi-objs := seq_midi.o +snd-seq-midi-emul-objs := seq_midi_emul.o +snd-seq-midi-event-objs := seq_midi_event.o +snd-seq-instr-objs := seq_instr.o +snd-seq-dummy-objs := seq_dummy.o +snd-seq-virmidi-objs := seq_virmidi.o + +obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) +obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o +endif +obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_SERIAL_U16550) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_MTPAV) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_MPU401) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ALS100) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_AZT2320) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_DT019X) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ES18XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPL3SA2) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_AD1816A) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_CS4232) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4236) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_PC98_CS4232) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ES1688) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSMAX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_INTERWAVE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI93X) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB8) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB16) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SBAWE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o snd-seq-virmidi.o +obj-$(CONFIG_SND_ES968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ALS4000) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CMIPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4281) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ENS1370) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ENS1371) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ES1938) += snd-seq-device.o snd-seq-midi-emul.o snd-seq.o snd-seq-instr.o snd-seq-midi.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ES1968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_VIA82XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_USB_AUDIO) += snd-seq.o snd-seq-device.o snd-seq-midi-event.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-seq-device.o: $(snd-seq-device-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-device-objs) + +snd-seq.o: $(snd-seq-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-objs) + +snd-seq-midi.o: $(snd-seq-midi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-objs) + +snd-seq-midi-emul.o: $(snd-seq-midi-emul-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-emul-objs) + +snd-seq-midi-event.o: $(snd-seq-midi-event-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-event-objs) + +snd-seq-instr.o: $(snd-seq-instr-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-instr-objs) + +snd-seq-dummy.o: $(snd-seq-dummy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-dummy-objs) + +snd-seq-virmidi.o: $(snd-seq-virmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-virmidi-objs) diff -urN linux-2.4.21-rc1.orig/sound/core/seq/instr/Makefile linux/sound/core/seq/instr/Makefile --- linux-2.4.21-rc1.orig/sound/core/seq/instr/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/instr/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,64 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := instr.o + +list-multi := snd-ainstr-fm.o snd-ainstr-simple.o snd-ainstr-gf1.o \ + snd-ainstr-iw.o + +export-objs := ainstr_fm.o ainstr_simple.o ainstr_gf1.o ainstr_iw.o + +snd-ainstr-fm-objs := ainstr_fm.o +snd-ainstr-simple-objs := ainstr_simple.o +snd-ainstr-gf1-objs := ainstr_gf1.o +snd-ainstr-iw-objs := ainstr_iw.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-ainstr-fm.o +obj-$(CONFIG_SND_AZT2320) += snd-ainstr-fm.o +obj-$(CONFIG_SND_DT019X) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES18XX) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPL3SA2) += snd-ainstr-fm.o +obj-$(CONFIG_SND_AD1816A) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4232) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4236) += snd-ainstr-fm.o +obj-$(CONFIG_SND_PC98_CS4232) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES1688) += snd-ainstr-fm.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_GUSMAX) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o +obj-$(CONFIG_SND_INTERWAVE) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPTI93X) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB8) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB16) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SBAWE) += snd-ainstr-fm.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ALS4000) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CMIPCI) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4281) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES1938) += snd-ainstr-fm.o +obj-$(CONFIG_SND_FM801) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SONICVIBES) += snd-ainstr-fm.o +obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o +obj-$(CONFIG_SND_YMFPCI) += snd-ainstr-fm.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-ainstr-fm.o: $(snd-ainstr-fm-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-fm-objs) + +snd-ainstr-simple.o: $(snd-ainstr-simple-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-simple-objs) + +snd-ainstr-gf1.o: $(snd-ainstr-gf1-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-gf1-objs) + +snd-ainstr-iw.o: $(snd-ainstr-iw-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-iw-objs) diff -urN linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_fm.c linux/sound/core/seq/instr/ainstr_fm.c --- linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_fm.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/instr/ainstr_fm.c 2003-01-31 08:19:35.000000000 -0700 @@ -0,0 +1,160 @@ +/* + * FM (OPL2/3) Instrument routines + * Copyright (c) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_fm_id = SNDRV_SEQ_INSTR_ID_OPL2_3; + +static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + fm_instrument_t *ip; + fm_xinstrument_t ix; + int idx; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + /* copy instrument data */ + if (len < (long)sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != FM_STRU_INSTR) + return -EINVAL; + ip = (fm_instrument_t *)KINSTR_DATA(instr); + ip->share_id[0] = le32_to_cpu(ix.share_id[0]); + ip->share_id[1] = le32_to_cpu(ix.share_id[1]); + ip->share_id[2] = le32_to_cpu(ix.share_id[2]); + ip->share_id[3] = le32_to_cpu(ix.share_id[3]); + ip->type = ix.type; + for (idx = 0; idx < 4; idx++) { + ip->op[idx].am_vib = ix.op[idx].am_vib; + ip->op[idx].ksl_level = ix.op[idx].ksl_level; + ip->op[idx].attack_decay = ix.op[idx].attack_decay; + ip->op[idx].sustain_release = ix.op[idx].sustain_release; + ip->op[idx].wave_select = ix.op[idx].wave_select; + } + for (idx = 0; idx < 2; idx++) { + ip->feedback_connection[idx] = ix.feedback_connection[idx]; + } + ip->echo_delay = ix.echo_delay; + ip->echo_atten = ix.echo_atten; + ip->chorus_spread = ix.chorus_spread; + ip->trnsps = ix.trnsps; + ip->fix_dur = ix.fix_dur; + ip->modes = ix.modes; + ip->fix_key = ix.fix_key; + return 0; +} + +static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + fm_instrument_t *ip; + fm_xinstrument_t ix; + int idx; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < (long)sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (fm_instrument_t *)KINSTR_DATA(instr); + ix.stype = FM_STRU_INSTR; + ix.share_id[0] = cpu_to_le32(ip->share_id[0]); + ix.share_id[1] = cpu_to_le32(ip->share_id[1]); + ix.share_id[2] = cpu_to_le32(ip->share_id[2]); + ix.share_id[3] = cpu_to_le32(ip->share_id[3]); + ix.type = ip->type; + for (idx = 0; idx < 4; idx++) { + ix.op[idx].am_vib = ip->op[idx].am_vib; + ix.op[idx].ksl_level = ip->op[idx].ksl_level; + ix.op[idx].attack_decay = ip->op[idx].attack_decay; + ix.op[idx].sustain_release = ip->op[idx].sustain_release; + ix.op[idx].wave_select = ip->op[idx].wave_select; + } + for (idx = 0; idx < 2; idx++) { + ix.feedback_connection[idx] = ip->feedback_connection[idx]; + } + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + ix.echo_delay = ip->echo_delay; + ix.echo_atten = ip->echo_atten; + ix.chorus_spread = ip->chorus_spread; + ix.trnsps = ip->trnsps; + ix.fix_dur = ip->fix_dur; + ix.modes = ip->modes; + ix.fix_key = ip->fix_key; + return 0; +} + +static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + *size = sizeof(fm_xinstrument_t); + return 0; +} + +int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + // ops->private_data = private_data; + ops->add_len = sizeof(fm_instrument_t); + ops->instr_type = snd_seq_fm_id; + ops->put = snd_seq_fm_put; + ops->get = snd_seq_fm_get; + ops->get_size = snd_seq_fm_get_size; + // ops->remove = snd_seq_fm_remove; + // ops->notify = snd_seq_fm_notify; + ops->next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_fm_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_fm_exit(void) +{ +} + +module_init(alsa_ainstr_fm_init) +module_exit(alsa_ainstr_fm_exit) + +EXPORT_SYMBOL(snd_seq_fm_id); +EXPORT_SYMBOL(snd_seq_fm_init); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_gf1.c linux/sound/core/seq/instr/ainstr_gf1.c --- linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_gf1.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/instr/ainstr_gf1.c 2003-01-31 08:19:35.000000000 -0700 @@ -0,0 +1,361 @@ +/* + * GF1 (GUS) Patch - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_gf1_id = SNDRV_SEQ_INSTR_ID_GUS_PATCH; + +static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & GF1_WAVE_16BIT) + result <<= 1; + if (format & GF1_WAVE_STEREO) + result <<= 1; + return format; +} + +static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + char **data, + long *len, + int atomic) +{ + gf1_wave_t *wp, *prev; + gf1_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < (long)sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = (gf1_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->flags = xp.flags; + wp->sample_rate = le32_to_cpu(xp.sample_rate); + wp->low_frequency = le32_to_cpu(xp.low_frequency); + wp->high_frequency = le32_to_cpu(xp.high_frequency); + wp->root_frequency = le32_to_cpu(xp.root_frequency); + wp->tune = le16_to_cpu(xp.tune); + wp->balance = xp.balance; + memcpy(wp->envelope_rate, xp.envelope_rate, 6); + memcpy(wp->envelope_offset, xp.envelope_offset, 6); + wp->tremolo_sweep = xp.tremolo_sweep; + wp->tremolo_rate = xp.tremolo_rate; + wp->tremolo_depth = xp.tremolo_depth; + wp->vibrato_sweep = xp.vibrato_sweep; + wp->vibrato_rate = xp.vibrato_rate; + wp->vibrato_depth = xp.vibrato_depth; + wp->scale_frequency = le16_to_cpu(xp.scale_frequency); + wp->scale_factor = le16_to_cpu(xp.scale_factor); + real_size = snd_seq_gf1_size(wp->size, wp->format); + if ((long)real_size > *len) { + kfree(wp); + return -ENOMEM; + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + *data += real_size; + *len -= real_size; + prev = ip->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + ip->wave = wp; + } + return 0; +} + +static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops, + gf1_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + int atomic) +{ + gf1_wave_t *wave; + + while ((wave = ip->wave) != NULL) { + ip->wave = wave->next; + snd_seq_gf1_wave_free(ops, wave, atomic); + } +} + +static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + gf1_xinstrument_t ix; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < (long)sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != GF1_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + while (len > (long)sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != GF1_STRU_WAVE) { + snd_seq_gf1_instr_free(ops, ip, atomic); + return -EINVAL; + } + err = snd_seq_gf1_copy_wave_from_stream(ops, + ip, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_gf1_instr_free(ops, ip, atomic); + return err; + } + } + return 0; +} + +static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + char **data, + long *len, + int atomic) +{ + gf1_wave_t *wp; + gf1_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = ip->wave; wp; wp = wp->next) { + if (*len < (long)sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = GF1_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.flags = wp->flags; + xp.sample_rate = cpu_to_le32(wp->sample_rate); + xp.low_frequency = cpu_to_le32(wp->low_frequency); + xp.high_frequency = cpu_to_le32(wp->high_frequency); + xp.root_frequency = cpu_to_le32(wp->root_frequency); + xp.tune = cpu_to_le16(wp->tune); + xp.balance = wp->balance; + memcpy(xp.envelope_rate, wp->envelope_rate, 6); + memcpy(xp.envelope_offset, wp->envelope_offset, 6); + xp.tremolo_sweep = wp->tremolo_sweep; + xp.tremolo_rate = wp->tremolo_rate; + xp.tremolo_depth = wp->tremolo_depth; + xp.vibrato_sweep = wp->vibrato_sweep; + xp.vibrato_rate = wp->vibrato_rate; + xp.vibrato_depth = wp->vibrato_depth; + xp.scale_frequency = cpu_to_le16(wp->scale_frequency); + xp.scale_factor = cpu_to_le16(wp->scale_factor); + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_gf1_size(wp->size, wp->format); + if (*len < (long)real_size) + return -ENOMEM; + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + *data += wp->size; + *len -= wp->size; + } + return 0; +} + +static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + gf1_xinstrument_t ix; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < (long)sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + ix.stype = GF1_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + return snd_seq_gf1_copy_wave_to_stream(ops, + ip, + &instr_data, + &len, + atomic); +} + +static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + gf1_instrument_t *ip; + gf1_wave_t *wp; + + *size = 0; + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + result = sizeof(gf1_xinstrument_t); + for (wp = ip->wave; wp; wp = wp->next) { + result += sizeof(gf1_xwave_t); + result += wp->size; + } + *size = result; + return 0; +} + +static int snd_seq_gf1_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + snd_seq_gf1_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_gf1_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_gf1_init(snd_gf1_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(gf1_instrument_t); + ops->kops.instr_type = snd_seq_gf1_id; + ops->kops.put = snd_seq_gf1_put; + ops->kops.get = snd_seq_gf1_get; + ops->kops.get_size = snd_seq_gf1_get_size; + ops->kops.remove = snd_seq_gf1_remove; + ops->kops.notify = snd_seq_gf1_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_gf1_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_gf1_exit(void) +{ +} + +module_init(alsa_ainstr_gf1_init) +module_exit(alsa_ainstr_gf1_exit) + +EXPORT_SYMBOL(snd_seq_gf1_id); +EXPORT_SYMBOL(snd_seq_gf1_init); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_iw.c linux/sound/core/seq/instr/ainstr_iw.c --- linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_iw.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/instr/ainstr_iw.c 2003-01-31 08:19:36.000000000 -0700 @@ -0,0 +1,626 @@ +/* + * IWFFFF - AMD InterWave (tm) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_iwffff_id = SNDRV_SEQ_INSTR_ID_INTERWAVE; + +static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & IWFFFF_WAVE_16BIT) + result <<= 1; + if (format & IWFFFF_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp, + iwffff_xlfo_t *fx) +{ + fp->freq = le16_to_cpu(fx->freq); + fp->depth = le16_to_cpu(fx->depth); + fp->sweep = le16_to_cpu(fx->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_env_t *ep, + iwffff_xenv_t *ex, + char **data, + long *len, + int gfp_mask) +{ + __u32 stype; + iwffff_env_record_t *rp, *rp_last; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ep->flags = ex->flags; + ep->mode = ex->mode; + ep->index = ex->index; + rp_last = NULL; + while (1) { + if (*len < (long)sizeof(__u32)) + return -EINVAL; + if (copy_from_user(&stype, data, sizeof(stype))) + return -EFAULT; + if (stype == IWFFFF_STRU_WAVE) + return 0; + if (req_stype != stype) { + if (stype == IWFFFF_STRU_ENV_RECP || + stype == IWFFFF_STRU_ENV_RECV) + return 0; + } + if (*len < (long)sizeof(rx)) + return -EINVAL; + if (copy_from_user(&rx, *data, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); + if (points_size > *len) + return -EINVAL; + rp = (iwffff_env_record_t *)snd_kcalloc(sizeof(*rp) + points_size, gfp_mask); + if (rp == NULL) + return -ENOMEM; + rp->nattack = le16_to_cpu(rx.nattack); + rp->nrelease = le16_to_cpu(rx.nrelease); + rp->sustain_offset = le16_to_cpu(rx.sustain_offset); + rp->sustain_rate = le16_to_cpu(rx.sustain_rate); + rp->release_rate = le16_to_cpu(rx.release_rate); + rp->hirange = rx.hirange; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + if (copy_from_user(&px, *data, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + pp->offset = le16_to_cpu(px.offset); + pp->rate = le16_to_cpu(px.rate); + } + if (ep->record == NULL) { + ep->record = rp; + } else { + rp_last = rp; + } + rp_last = rp; + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp, *prev; + iwffff_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < (long)sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = (iwffff_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->address.memory = le32_to_cpu(xp.offset); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->sample_ratio = le32_to_cpu(xp.sample_ratio); + wp->attenuation = xp.attenuation; + wp->low_note = xp.low_note; + wp->high_note = xp.high_note; + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if ((long)real_size > *len) { + kfree(wp); + return -ENOMEM; + } + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + prev = lp->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + lp->wave = wp; + } + return 0; +} + +static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops, + iwffff_env_t *env, + int atomic) +{ + iwffff_env_record_t *rec; + + while ((rec = env->record) != NULL) { + env->record = rec->next; + kfree(rec); + } +} + +static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops, + iwffff_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops, + iwffff_instrument_t *ip, + int atomic) +{ + iwffff_layer_t *layer; + iwffff_wave_t *wave; + + while ((layer = ip->layer) != NULL) { + ip->layer = layer->next; + snd_seq_iwffff_env_free(ops, &layer->penv, atomic); + snd_seq_iwffff_env_free(ops, &layer->venv, atomic); + while ((wave = layer->wave) != NULL) { + layer->wave = wave->next; + snd_seq_iwffff_wave_free(ops, wave, atomic); + } + kfree(layer); + } +} + +static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp, *prev_lp; + iwffff_xlayer_t lx; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < (long)sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != IWFFFF_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->layer_type = le16_to_cpu(ix.layer_type); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + prev_lp = NULL; + while (len > 0) { + if (len < (long)sizeof(iwffff_xlayer_t)) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + if (copy_from_user(&lx, instr_data, sizeof(lx))) + return -EFAULT; + instr_data += sizeof(lx); + len -= sizeof(lx); + if (lx.stype != IWFFFF_STRU_LAYER) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + lp = (iwffff_layer_t *)snd_kcalloc(sizeof(*lp), gfp_mask); + if (lp == NULL) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -ENOMEM; + } + if (prev_lp) { + prev_lp->next = lp; + } else { + ip->layer = lp; + } + prev_lp = lp; + lp->flags = lx.flags; + lp->velocity_mode = lx.velocity_mode; + lp->layer_event = lx.layer_event; + lp->low_range = lx.low_range; + lp->high_range = lx.high_range; + lp->pan = lx.pan; + lp->pan_freq_scale = lx.pan_freq_scale; + lp->attenuation = lx.attenuation; + snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); + snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); + lp->freq_scale = le16_to_cpu(lx.freq_scale); + lp->freq_center = lx.freq_center; + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lp->penv, &lx.penv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lp->venv, &lx.venv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + while (len > (long)sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != IWFFFF_STRU_WAVE) + break; + err = snd_seq_iwffff_copy_wave_from_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + } + } + return 0; +} + +static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx, + iwffff_lfo_t *fp) +{ + fx->freq = cpu_to_le16(fp->freq); + fx->depth = cpu_to_le16(fp->depth); + fx->sweep = cpu_to_le16(fp->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_xenv_t *ex, + iwffff_env_t *ep, + char **data, + long *len) +{ + iwffff_env_record_t *rp; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ex->flags = ep->flags; + ex->mode = ep->mode; + ex->index = ep->index; + for (rp = ep->record; rp; rp = rp->next) { + if (*len < (long)sizeof(rx)) + return -ENOMEM; + memset(&rx, 0, sizeof(rx)); + rx.stype = req_stype; + rx.nattack = cpu_to_le16(rp->nattack); + rx.nrelease = cpu_to_le16(rp->nrelease); + rx.sustain_offset = cpu_to_le16(rp->sustain_offset); + rx.sustain_rate = cpu_to_le16(rp->sustain_rate); + rx.release_rate = cpu_to_le16(rp->release_rate); + rx.hirange = cpu_to_le16(rp->hirange); + if (copy_to_user(*data, &rx, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + if (*len < points_size) + return -ENOMEM; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + px.offset = cpu_to_le16(pp->offset); + px.rate = cpu_to_le16(pp->rate); + if (copy_to_user(*data, &px, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + } + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp; + iwffff_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = lp->wave; wp; wp = wp->next) { + if (*len < (long)sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = IWFFFF_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + if (wp->format & IWFFFF_WAVE_ROM) + xp.offset = cpu_to_le32(wp->address.memory); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.sample_ratio = cpu_to_le32(wp->sample_ratio); + xp.attenuation = wp->attenuation; + xp.low_note = wp->low_note; + xp.high_note = wp->high_note; + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if (*len < (long)real_size) + return -ENOMEM; + } + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + } + return 0; +} + +static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp; + iwffff_xlayer_t lx; + char *layer_instr_data; + int err; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < (long)sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ix.stype = IWFFFF_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.layer_type = cpu_to_le16(ip->layer_type); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + for (lp = ip->layer; lp; lp = lp->next) { + if (len < (long)sizeof(lx)) + return -ENOMEM; + memset(&lx, 0, sizeof(lx)); + lx.stype = IWFFFF_STRU_LAYER; + lx.flags = lp->flags; + lx.velocity_mode = lp->velocity_mode; + lx.layer_event = lp->layer_event; + lx.low_range = lp->low_range; + lx.high_range = lp->high_range; + lx.pan = lp->pan; + lx.pan_freq_scale = lp->pan_freq_scale; + lx.attenuation = lp->attenuation; + snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); + snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); + layer_instr_data = instr_data; + instr_data += sizeof(lx); + len -= sizeof(lx); + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lx.penv, &lp->penv, + &instr_data, &len); + if (err < 0) + return err; + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lx.venv, &lp->venv, + &instr_data, &len); + if (err < 0) + return err; + /* layer structure updating is now finished */ + if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) + return -EFAULT; + err = snd_seq_iwffff_copy_wave_to_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) + return err; + } + return 0; +} + +static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep) +{ + long result = 0; + iwffff_env_record_t *rp; + + for (rp = ep->record; rp; rp = rp->next) { + result += sizeof(iwffff_xenv_record_t); + result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + } + return 0; +} + +static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp) +{ + long result = 0; + iwffff_wave_t *wp; + + for (wp = lp->wave; wp; wp = wp->next) { + result += sizeof(iwffff_xwave_t); + if (!(wp->format & IWFFFF_WAVE_ROM)) + result += wp->size; + } + return result; +} + +static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + iwffff_instrument_t *ip; + iwffff_layer_t *lp; + + *size = 0; + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + result = sizeof(iwffff_xinstrument_t); + for (lp = ip->layer; lp; lp = lp->next) { + result += sizeof(iwffff_xlayer_t); + result += snd_seq_iwffff_env_size_in_stream(&lp->penv); + result += snd_seq_iwffff_env_size_in_stream(&lp->venv); + result += snd_seq_iwffff_wave_size_in_stream(lp); + } + *size = result; + return 0; +} + +static int snd_seq_iwffff_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + snd_seq_iwffff_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_iwffff_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(iwffff_instrument_t); + ops->kops.instr_type = snd_seq_iwffff_id; + ops->kops.put = snd_seq_iwffff_put; + ops->kops.get = snd_seq_iwffff_get; + ops->kops.get_size = snd_seq_iwffff_get_size; + ops->kops.remove = snd_seq_iwffff_remove; + ops->kops.notify = snd_seq_iwffff_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_iw_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_iw_exit(void) +{ +} + +module_init(alsa_ainstr_iw_init) +module_exit(alsa_ainstr_iw_exit) + +EXPORT_SYMBOL(snd_seq_iwffff_id); +EXPORT_SYMBOL(snd_seq_iwffff_init); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_simple.c linux/sound/core/seq/instr/ainstr_simple.c --- linux-2.4.21-rc1.orig/sound/core/seq/instr/ainstr_simple.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/instr/ainstr_simple.c 2003-01-31 08:19:37.000000000 -0700 @@ -0,0 +1,218 @@ +/* + * Simple (MOD player) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_simple_id = SNDRV_SEQ_INSTR_ID_SIMPLE; + +static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & SIMPLE_WAVE_16BIT) + result <<= 1; + if (format & SIMPLE_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_simple_instr_free(snd_simple_ops_t *ops, + simple_instrument_t *ip, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, ip, atomic); +} + +static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + simple_xinstrument_t ix; + int err, gfp_mask; + unsigned int real_size; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < (long)sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != SIMPLE_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (simple_instrument_t *)KINSTR_DATA(instr); + ip->share_id[0] = le32_to_cpu(ix.share_id[0]); + ip->share_id[1] = le32_to_cpu(ix.share_id[1]); + ip->share_id[2] = le32_to_cpu(ix.share_id[2]); + ip->share_id[3] = le32_to_cpu(ix.share_id[3]); + ip->format = le32_to_cpu(ix.format); + ip->size = le32_to_cpu(ix.size); + ip->start = le32_to_cpu(ix.start); + ip->loop_start = le32_to_cpu(ix.loop_start); + ip->loop_end = le32_to_cpu(ix.loop_end); + ip->loop_repeat = le16_to_cpu(ix.loop_repeat); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + real_size = snd_seq_simple_size(ip->size, ip->format); + if (len < (long)real_size) + return -EINVAL; + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, ip, + instr_data, real_size, atomic); + if (err < 0) + return err; + } + return 0; +} + +static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + simple_xinstrument_t ix; + int err; + unsigned int real_size; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < (long)sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (simple_instrument_t *)KINSTR_DATA(instr); + ix.stype = SIMPLE_STRU_INSTR; + ix.share_id[0] = cpu_to_le32(ip->share_id[0]); + ix.share_id[1] = cpu_to_le32(ip->share_id[1]); + ix.share_id[2] = cpu_to_le32(ip->share_id[2]); + ix.share_id[3] = cpu_to_le32(ip->share_id[3]); + ix.format = cpu_to_le32(ip->format); + ix.size = cpu_to_le32(ip->size); + ix.start = cpu_to_le32(ip->start); + ix.loop_start = cpu_to_le32(ip->loop_start); + ix.loop_end = cpu_to_le32(ip->loop_end); + ix.loop_repeat = cpu_to_le32(ip->loop_repeat); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + real_size = snd_seq_simple_size(ip->size, ip->format); + if (len < (long)real_size) + return -ENOMEM; + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, ip, + instr_data, real_size, atomic); + if (err < 0) + return err; + } + return 0; +} + +static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + simple_instrument_t *ip; + + ip = (simple_instrument_t *)KINSTR_DATA(instr); + *size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format); + return 0; +} + +static int snd_seq_simple_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + + ip = (simple_instrument_t *)KINSTR_DATA(instr); + snd_seq_simple_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_simple_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_simple_init(snd_simple_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(simple_instrument_t); + ops->kops.instr_type = snd_seq_simple_id; + ops->kops.put = snd_seq_simple_put; + ops->kops.get = snd_seq_simple_get; + ops->kops.get_size = snd_seq_simple_get_size; + ops->kops.remove = snd_seq_simple_remove; + ops->kops.notify = snd_seq_simple_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_simple_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_simple_exit(void) +{ +} + +module_init(alsa_ainstr_simple_init) +module_exit(alsa_ainstr_simple_exit) + +EXPORT_SYMBOL(snd_seq_simple_id); +EXPORT_SYMBOL(snd_seq_simple_init); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/Makefile linux/sound/core/seq/oss/Makefile --- linux-2.4.21-rc1.orig/sound/core/seq/oss/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,21 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := oss.o + +list-multi := snd-seq-oss.o + +snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ + seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ + seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o + +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) + obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o +endif + +include $(TOPDIR)/Rules.make + +snd-seq-oss.o: $(snd-seq-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-oss-objs) diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss.c linux/sound/core/seq/oss/seq_oss.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss.c 2002-08-13 10:13:35.000000000 -0600 @@ -0,0 +1,305 @@ +/* + * OSS compatible sequencer driver + * + * registration of device and proc + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "seq_oss_device.h" +#include "seq_oss_synth.h" + +/* + * module option + */ +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("OSS-compatible sequencer module"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#ifdef SNDRV_SEQ_OSS_DEBUG +MODULE_PARM(seq_oss_debug, "i"); +MODULE_PARM_DESC(seq_oss_debug, "debug option"); +int seq_oss_debug = 0; +#endif + + +/* + * prototypes + */ +static int register_device(void); +static void unregister_device(void); +static int register_proc(void); +static void unregister_proc(void); + +static int odev_open(struct inode *inode, struct file *file); +static int odev_release(struct inode *inode, struct file *file); +static ssize_t odev_read(struct file *file, char *buf, size_t count, loff_t *offset); +static ssize_t odev_write(struct file *file, const char *buf, size_t count, loff_t *offset); +static int odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static unsigned int odev_poll(struct file *file, poll_table * wait); +#ifdef CONFIG_PROC_FS +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf); +#endif + + +/* + * module interface + */ + +static int __init alsa_seq_oss_init(void) +{ + int rc; + static snd_seq_dev_ops_t ops = { + snd_seq_oss_synth_register, + snd_seq_oss_synth_unregister, + }; + + if ((rc = register_device()) < 0) + return rc; + if ((rc = register_proc()) < 0) { + unregister_device(); + return rc; + } + if ((rc = snd_seq_oss_create_client()) < 0) { + unregister_proc(); + unregister_device(); + return rc; + } + + if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, + sizeof(snd_seq_oss_reg_t))) < 0) { + snd_seq_oss_delete_client(); + unregister_proc(); + unregister_device(); + return rc; + } + + /* success */ + snd_seq_oss_synth_init(); + return 0; +} + +static void __exit alsa_seq_oss_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_oss_delete_client(); + unregister_proc(); + unregister_device(); +} + +module_init(alsa_seq_oss_init) +module_exit(alsa_seq_oss_exit) + +/* + * ALSA minor device interface + */ + +static DECLARE_MUTEX(register_mutex); + +static int +odev_open(struct inode *inode, struct file *file) +{ + int level, rc; + + if (minor(inode->i_rdev) == SNDRV_MINOR_OSS_MUSIC) + level = SNDRV_SEQ_OSS_MODE_MUSIC; + else + level = SNDRV_SEQ_OSS_MODE_SYNTH; + + down(®ister_mutex); + rc = snd_seq_oss_open(file, level); + up(®ister_mutex); + + return rc; +} + +static int +odev_release(struct inode *inode, struct file *file) +{ + seq_oss_devinfo_t *dp; + + if ((dp = file->private_data) == NULL) + return 0; + + snd_seq_oss_drain_write(dp); + + down(®ister_mutex); + snd_seq_oss_release(dp); + up(®ister_mutex); + + return 0; +} + +static ssize_t +odev_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_read(dp, buf, count); +} + + +static ssize_t +odev_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_write(dp, buf, count, file); +} + +static int +odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_ioctl(dp, cmd, arg); +} + + +static unsigned int +odev_poll(struct file *file, poll_table * wait) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return 0); + return snd_seq_oss_poll(dp, file, wait); +} + +/* + * registration of sequencer minor device + */ + +static struct file_operations seq_oss_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = odev_read, + .write = odev_write, + .open = odev_open, + .release = odev_release, + .poll = odev_poll, + .ioctl = odev_ioctl, +}; + +static snd_minor_t seq_oss_reg = { + .comment = "sequencer", + .f_ops = &seq_oss_f_ops, +}; + +static int __init +register_device(void) +{ + int rc; + + down(®ister_mutex); + if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, + NULL, 0, + &seq_oss_reg, + SNDRV_SEQ_OSS_DEVNAME)) < 0) { + snd_printk(KERN_ERR "can't register device seq\n"); + up(®ister_mutex); + return rc; + } + if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, + NULL, 0, + &seq_oss_reg, + SNDRV_SEQ_OSS_DEVNAME)) < 0) { + snd_printk(KERN_ERR "can't register device music\n"); + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0); + up(®ister_mutex); + return rc; + } + debug_printk(("device registered\n")); + up(®ister_mutex); + return 0; +} + +static void +unregister_device(void) +{ + down(®ister_mutex); + debug_printk(("device unregistered\n")); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0) + snd_printk(KERN_ERR "error unregister device music\n"); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0) + snd_printk(KERN_ERR "error unregister device seq\n"); + up(®ister_mutex); +} + +/* + * /proc interface + */ + +#ifdef CONFIG_PROC_FS + +static snd_info_entry_t *info_entry; + +static void +info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf) +{ + down(®ister_mutex); + snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR); + snd_seq_oss_system_info_read(buf); + snd_seq_oss_synth_info_read(buf); + snd_seq_oss_midi_info_read(buf); + up(®ister_mutex); +} + +#endif /* CONFIG_PROC_FS */ + +static int __init +register_proc(void) +{ +#ifdef CONFIG_PROC_FS + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root); + if (entry == NULL) + return -ENOMEM; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = NULL; + entry->c.text.read_size = 1024; + entry->c.text.read = info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + info_entry = entry; +#endif + return 0; +} + +static void +unregister_proc(void) +{ +#ifdef CONFIG_PROC_FS + if (info_entry) + snd_info_unregister(info_entry); + info_entry = NULL; +#endif +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_device.h linux/sound/core/seq/oss/seq_oss_device.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_device.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_device.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,199 @@ +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_DEVICE_H +#define __SEQ_OSS_DEVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* enable debug print */ +#define SNDRV_SEQ_OSS_DEBUG + +/* max. applications */ +#define SNDRV_SEQ_OSS_MAX_CLIENTS 16 +#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16 +#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS 32 + +/* version */ +#define SNDRV_SEQ_OSS_MAJOR_VERSION 0 +#define SNDRV_SEQ_OSS_MINOR_VERSION 1 +#define SNDRV_SEQ_OSS_TINY_VERSION 8 +#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8" + +/* device and proc interface name */ +#define SNDRV_SEQ_OSS_DEVNAME "seq_oss" +#define SNDRV_SEQ_OSS_PROCNAME "oss" + + +/* + * type definitions + */ + +typedef struct seq_oss_devinfo_t seq_oss_devinfo_t; +typedef struct seq_oss_writeq_t seq_oss_writeq_t; +typedef struct seq_oss_readq_t seq_oss_readq_t; +typedef struct seq_oss_timer_t seq_oss_timer_t; +typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t; +typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t; +typedef struct seq_oss_chinfo_t seq_oss_chinfo_t; +typedef unsigned int reltime_t; +typedef unsigned int abstime_t; +typedef union evrec_t evrec_t; + + +/* + * synthesizer channel information + */ +struct seq_oss_chinfo_t { + int note, vel; +}; + +/* + * synthesizer information + */ +struct seq_oss_synthinfo_t { + snd_seq_oss_arg_t arg; + seq_oss_chinfo_t *ch; + seq_oss_synth_sysex_t *sysex; + int nr_voices; + int opened; + int is_midi; + int midi_mapped; +}; + + +/* + * sequencer client information + */ + +struct seq_oss_devinfo_t { + + int index; /* application index */ + int cseq; /* sequencer client number */ + int port; /* sequencer port number */ + int queue; /* sequencer queue number */ + + snd_seq_addr_t addr; /* address of this device */ + + int seq_mode; /* sequencer mode */ + int file_mode; /* file access */ + + /* midi device table */ + int max_mididev; + + /* synth device table */ + int max_synthdev; + seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; + int synth_opened; + + /* output queue */ + seq_oss_writeq_t *writeq; + + /* midi input queue */ + seq_oss_readq_t *readq; + + /* timer */ + seq_oss_timer_t *timer; +}; + + +/* + * function prototypes + */ + +/* create/delete OSS sequencer client */ +int snd_seq_oss_create_client(void); +int snd_seq_oss_delete_client(void); + +/* device file interface */ +int snd_seq_oss_open(struct file *file, int level); +void snd_seq_oss_release(seq_oss_devinfo_t *dp); +int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg); +int snd_seq_oss_read(seq_oss_devinfo_t *dev, char *buf, int count); +int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt); +unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait); + +void snd_seq_oss_reset(seq_oss_devinfo_t *dp); +void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp); + +/* */ +void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time); + + +/* proc interface */ +void snd_seq_oss_system_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf); + +/* file mode macros */ +#define is_read_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_READ) +#define is_write_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_WRITE) +#define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK) + +/* dispatch event */ +inline static int +snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop) +{ + return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop); +} + +/* ioctl */ +inline static int +snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg) +{ + return snd_seq_kernel_client_ctl(dp->cseq, type, arg); +} + +/* fill the addresses in header */ +inline static void +snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, + int dest_client, int dest_port) +{ + ev->queue = dp->queue; + ev->source = dp->addr; + ev->dest.client = dest_client; + ev->dest.port = dest_port; +} + + +/* misc. functions for proc interface */ +char *enabled_str(int bool); +char *filemode_str(int fmode); + + +/* for debug */ +#ifdef SNDRV_SEQ_OSS_DEBUG +extern int seq_oss_debug; +#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0) +#else +#define debug_printk(x) /**/ +#endif + +#endif /* __SEQ_OSS_DEVICE_H */ diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_event.c linux/sound/core/seq/oss/seq_oss_event.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_event.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_event.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,447 @@ +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_device.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" + + +/* + * prototypes + */ +static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); +static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); +static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev); +static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev); +static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev); + + +/* + * convert an OSS event to ALSA event + * return 0 : enqueued + * non-zero : invalid - ignored + */ + +int +snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->s.code) { + case SEQ_EXTENDED: + return extended_event(dp, q, ev); + + case EV_CHN_VOICE: + return chn_voice_event(dp, q, ev); + + case EV_CHN_COMMON: + return chn_common_event(dp, q, ev); + + case EV_TIMING: + return timing_event(dp, q, ev); + + case EV_SEQ_LOCAL: + return local_event(dp, q, ev); + + case EV_SYSEX: + return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev); + + case SEQ_MIDIPUTC: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + /* put a midi byte */ + if (! is_write_mode(dp->file_mode)) + break; + if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE)) + break; + if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE) + return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev); + break; + + case SEQ_ECHO: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return set_echo_event(dp, q, ev); + + case SEQ_PRIVATE: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev); + + default: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return old_event(dp, q, ev); + } + return -EINVAL; +} + +/* old type events: mode1 only */ +static int +old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->s.code) { + case SEQ_NOTEOFF: + return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev); + + case SEQ_NOTEON: + return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev); + + case SEQ_WAIT: + /* skip */ + break; + + case SEQ_PGMCHANGE: + return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE, + q->n.chn, 0, q->n.note, ev); + + case SEQ_SYNCTIMER: + return snd_seq_oss_timer_reset(dp->timer); + } + + return -EINVAL; +} + +/* 8bytes extended event: mode1 only */ +static int +extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + int val; + + switch (q->e.cmd) { + case SEQ_NOTEOFF: + return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev); + + case SEQ_NOTEON: + return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev); + + case SEQ_PGMCHANGE: + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE, + q->e.chn, 0, q->e.p1, ev); + + case SEQ_AFTERTOUCH: + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS, + q->e.chn, 0, q->e.p1, ev); + + case SEQ_BALANCE: + /* convert -128:127 to 0:127 */ + val = (char)q->e.p1; + val = (val + 128) / 2; + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER, + q->e.chn, CTL_PAN, val, ev); + + case SEQ_CONTROLLER: + val = ((short)q->e.p3 << 8) | (short)q->e.p2; + switch (q->e.p1) { + case CTRL_PITCH_BENDER: /* SEQ1 V2 control */ + /* -0x2000:0x1fff */ + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_PITCHBEND, + q->e.chn, 0, val, ev); + case CTRL_PITCH_BENDER_RANGE: + /* conversion: 100/semitone -> 128/semitone */ + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_REGPARAM, + q->e.chn, 0, val*128/100, ev); + default: + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_CONTROL14, + q->e.chn, q->e.p1, val, ev); + } + + case SEQ_VOLMODE: + return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev); + + } + return -EINVAL; +} + +/* channel voice events: mode1 and 2 */ +static int +chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + if (q->v.chn >= 32) + return -EINVAL; + switch (q->v.cmd) { + case MIDI_NOTEON: + return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev); + + case MIDI_NOTEOFF: + return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev); + + case MIDI_KEY_PRESSURE: + return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS, + q->v.chn, q->v.note, q->v.parm, ev); + + } + return -EINVAL; +} + +/* channel common events: mode1 and 2 */ +static int +chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + if (q->l.chn >= 32) + return -EINVAL; + switch (q->l.cmd) { + case MIDI_PGM_CHANGE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE, + q->l.chn, 0, q->l.p1, ev); + + case MIDI_CTL_CHANGE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER, + q->l.chn, q->l.p1, q->l.val, ev); + + case MIDI_PITCH_BEND: + /* conversion: 0:0x3fff -> -0x2000:0x1fff */ + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND, + q->l.chn, 0, q->l.val - 8192, ev); + + case MIDI_CHN_PRESSURE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS, + q->l.chn, 0, q->l.val, ev); + } + return -EINVAL; +} + +/* timer events: mode1 and mode2 */ +static int +timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->t.cmd) { + case TMR_ECHO: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return set_echo_event(dp, q, ev); + else { + evrec_t tmp; + memset(&tmp, 0, sizeof(tmp)); + /* XXX: only for little-endian! */ + tmp.echo = (q->t.time << 8) | SEQ_ECHO; + return set_echo_event(dp, &tmp, ev); + } + + case TMR_STOP: + if (dp->seq_mode) + return snd_seq_oss_timer_stop(dp->timer); + return 0; + + case TMR_CONTINUE: + if (dp->seq_mode) + return snd_seq_oss_timer_continue(dp->timer); + return 0; + + case TMR_TEMPO: + if (dp->seq_mode) + return snd_seq_oss_timer_tempo(dp->timer, q->t.time); + return 0; + } + + return -EINVAL; +} + +/* local events: mode1 and 2 */ +static int +local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + return -EINVAL; +} + +/* + * process note-on event for OSS synth + * three different modes are available: + * - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode) + * Accept note 255 as volume change. + * - SNDRV_SEQ_OSS_PASS_EVENTS + * Pass all events to lowlevel driver anyway + * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000) + * Use key-pressure if note >= 128 + */ +static int +note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev) +{ + seq_oss_synthinfo_t *info = &dp->synths[dev]; + switch (info->arg.event_passing) { + case SNDRV_SEQ_OSS_PROCESS_EVENTS: + if (! info->ch || ch < 0 || ch >= info->nr_voices) { + /* pass directly */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + + if (note == 255 && info->ch[ch].note >= 0) { + /* volume control */ + int type; + //if (! vel) + /* set volume to zero -- note off */ + // type = SNDRV_SEQ_EVENT_NOTEOFF; + //else + if (info->ch[ch].vel) + /* sample already started -- volume change */ + type = SNDRV_SEQ_EVENT_KEYPRESS; + else + /* sample not started -- start now */ + type = SNDRV_SEQ_EVENT_NOTEON; + info->ch[ch].vel = vel; + return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev); + } else if (note >= 128) + return -EINVAL; /* invalid */ + + if (note != info->ch[ch].note && info->ch[ch].note >= 0) + /* note changed - note off at beginning */ + set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev); + /* set current status */ + info->ch[ch].note = note; + info->ch[ch].vel = vel; + if (vel) /* non-zero velocity - start the note now */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + return -EINVAL; + + case SNDRV_SEQ_OSS_PASS_EVENTS: + /* pass the event anyway */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + + case SNDRV_SEQ_OSS_PROCESS_KEYPRESS: + if (note >= 128) /* key pressure: shifted by 128 */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev); + else /* normal note-on event */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + return -EINVAL; +} + +/* + * process note-off event for OSS synth + */ +static int +note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev) +{ + seq_oss_synthinfo_t *info = &dp->synths[dev]; + switch (info->arg.event_passing) { + case SNDRV_SEQ_OSS_PROCESS_EVENTS: + if (! info->ch || ch < 0 || ch >= info->nr_voices) { + /* pass directly */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + + if (info->ch[ch].note >= 0) { + note = info->ch[ch].note; + info->ch[ch].vel = 0; + info->ch[ch].note = -1; + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev); + } + return -EINVAL; /* invalid */ + + case SNDRV_SEQ_OSS_PASS_EVENTS: + case SNDRV_SEQ_OSS_PROCESS_KEYPRESS: + /* pass the event anyway */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev); + + } + return -EINVAL; +} + +/* + * create a note event + */ +static int +set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + ev->type = type; + snd_seq_oss_synth_addr(dp, dev, ev); + ev->data.note.channel = ch; + ev->data.note.note = note; + ev->data.note.velocity = vel; + + return 0; +} + +/* + * create a control event + */ +static int +set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + ev->type = type; + snd_seq_oss_synth_addr(dp, dev, ev); + ev->data.control.channel = ch; + ev->data.control.param = param; + ev->data.control.value = val; + + return 0; +} + +/* + * create an echo event + */ +static int +set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev) +{ + ev->type = SNDRV_SEQ_EVENT_ECHO; + /* echo back to itself */ + snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port); + memcpy(&ev->data, rec, LONG_EVENT_SIZE); + return 0; +} + +/* + * event input callback from ALSA sequencer: + * the echo event is processed here. + */ +int +snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data; + evrec_t *rec; + + if (ev->type != SNDRV_SEQ_EVENT_ECHO) + return snd_seq_oss_midi_input(ev, direct, private_data); + + if (ev->source.client != dp->cseq) + return 0; /* ignored */ + + rec = (evrec_t*)&ev->data; + if (rec->s.code == SEQ_SYNCTIMER) { + /* sync echo back */ + snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time); + + } else { + /* echo back event */ + if (dp->readq == NULL) + return 0; + snd_seq_oss_readq_put_event(dp->readq, rec); + } + return 0; +} + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_event.h linux/sound/core/seq/oss/seq_oss_event.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_event.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_event.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,112 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_event.h - OSS event queue record + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_EVENT_H +#define __SEQ_OSS_EVENT_H + +#include "seq_oss_device.h" + +#define SHORT_EVENT_SIZE 4 +#define LONG_EVENT_SIZE 8 + +/* short event (4bytes) */ +typedef struct evrec_short_t { + unsigned char code; + unsigned char parm1; + unsigned char dev; + unsigned char parm2; +} evrec_short_t; + +/* short note events (4bytes) */ +typedef struct evrec_note_t { + unsigned char code; + unsigned char chn; + unsigned char note; + unsigned char vel; +} evrec_note_t; + +/* long timer events (8bytes) */ +typedef struct evrec_timer_t { + unsigned char code; + unsigned char cmd; + unsigned char dummy1, dummy2; + unsigned int time; +} evrec_timer_t; + +/* long extended events (8bytes) */ +typedef struct evrec_extended_t { + unsigned char code; + unsigned char cmd; + unsigned char dev; + unsigned char chn; + unsigned char p1, p2, p3, p4; +} evrec_extended_t; + +/* long channel events (8bytes) */ +typedef struct evrec_long_t { + unsigned char code; + unsigned char dev; + unsigned char cmd; + unsigned char chn; + unsigned char p1, p2; + unsigned short val; +} evrec_long_t; + +/* channel voice events (8bytes) */ +typedef struct evrec_voice_t { + unsigned char code; + unsigned char dev; + unsigned char cmd; + unsigned char chn; + unsigned char note, parm; + unsigned short dummy; +} evrec_voice_t; + +/* sysex events (8bytes) */ +typedef struct evrec_sysex_t { + unsigned char code; + unsigned char dev; + unsigned char buf[6]; +} evrec_sysex_t; + +/* event record */ +union evrec_t { + evrec_short_t s; + evrec_note_t n; + evrec_long_t l; + evrec_voice_t v; + evrec_timer_t t; + evrec_extended_t e; + evrec_sysex_t x; + unsigned int echo; + unsigned char c[LONG_EVENT_SIZE]; +}; + +#define ev_is_long(ev) ((ev)->s.code >= 128) +#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE) + +int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q); +int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop); + + +#endif /* __SEQ_OSS_EVENT_H */ diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_init.c linux/sound/core/seq/oss/seq_oss_init.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_init.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_init.c 2003-03-11 13:16:01.000000000 -0700 @@ -0,0 +1,550 @@ +/* + * OSS compatible sequencer driver + * + * open/close and reset interface + * + * Copyright (C) 1998-1999 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_device.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_writeq.h" +#include "seq_oss_readq.h" +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include + +/* + * common variables + */ +MODULE_PARM(maxqlen, "i"); +MODULE_PARM_DESC(maxqlen, "maximum queue length"); + +static int system_client = -1; /* ALSA sequencer client number */ +static int system_port = -1; + +int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN; +static int num_clients; +static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS]; + + +/* + * prototypes + */ +static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +static int translate_mode(struct file *file); +static int create_port(seq_oss_devinfo_t *dp); +static int delete_port(seq_oss_devinfo_t *dp); +static int alloc_seq_queue(seq_oss_devinfo_t *dp); +static int delete_seq_queue(int queue); +static void free_devinfo(void *private); + +#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec) + + +/* + * create sequencer client for OSS sequencer + */ +int __init +snd_seq_oss_create_client(void) +{ + int rc; + snd_seq_client_callback_t callback; + snd_seq_client_info_t info; + snd_seq_port_info_t port; + snd_seq_port_callback_t port_callback; + + /* create ALSA client */ + memset(&callback, 0, sizeof(callback)); + + callback.private_data = NULL; + callback.allow_input = 1; + callback.allow_output = 1; + + rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback); + if (rc < 0) + return rc; + + system_client = rc; + debug_printk(("new client = %d\n", rc)); + + /* set client information */ + memset(&info, 0, sizeof(info)); + info.client = system_client; + info.type = KERNEL_CLIENT; + strcpy(info.name, "OSS sequencer"); + + rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info); + + /* look up midi devices */ + snd_seq_oss_midi_lookup_ports(system_client); + + /* create annoucement receiver port */ + memset(&port, 0, sizeof(port)); + strcpy(port.name, "Receiver"); + port.addr.client = system_client; + port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ + port.type = 0; + + memset(&port_callback, 0, sizeof(port_callback)); + /* don't set port_callback.owner here. otherwise the module counter + * is incremented and we can no longer release the module.. + */ + port_callback.event_input = receive_announce; + port.kernel = &port_callback; + + call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + if ((system_port = port.addr.port) >= 0) { + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + subs.dest.client = system_client; + subs.dest.port = system_port; + call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs); + } + + + return 0; +} + + +/* + * receive annoucement from system port, and check the midi device + */ +static int +receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop) +{ + snd_seq_port_info_t pinfo; + + if (atomic) + return 0; /* it must not happen */ + + switch (ev->type) { + case SNDRV_SEQ_EVENT_PORT_START: + case SNDRV_SEQ_EVENT_PORT_CHANGE: + if (ev->data.addr.client == system_client) + break; /* ignore myself */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr = ev->data.addr; + if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0) + snd_seq_oss_midi_check_new_port(&pinfo); + break; + + case SNDRV_SEQ_EVENT_PORT_EXIT: + if (ev->data.addr.client == system_client) + break; /* ignore myself */ + snd_seq_oss_midi_check_exit_port(ev->data.addr.client, + ev->data.addr.port); + break; + } + return 0; +} + + +/* + * delete OSS sequencer client + */ +int +snd_seq_oss_delete_client(void) +{ + if (system_client >= 0) + snd_seq_delete_kernel_client(system_client); + + snd_seq_oss_midi_clear_all(); + + return 0; +} + + +/* + * open sequencer device + */ +int +snd_seq_oss_open(struct file *file, int level) +{ + int i, rc; + seq_oss_devinfo_t *dp; + + if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc device info\n"); + return -ENOMEM; + } + debug_printk(("oss_open: dp = %p\n", dp)); + + for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) { + if (client_table[i] == NULL) + break; + } + if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { + snd_printk(KERN_ERR "too many applications\n"); + kfree(dp); + return -ENOMEM; + } + + dp->index = i; + dp->cseq = system_client; + dp->port = -1; + dp->queue = -1; + dp->readq = NULL; + dp->writeq = NULL; + + /* look up synth and midi devices */ + snd_seq_oss_synth_setup(dp); + snd_seq_oss_midi_setup(dp); + + if (dp->synth_opened == 0 && dp->max_mididev == 0) { + snd_printk(KERN_ERR "no device found\n"); + rc = -ENODEV; + goto _error; + } + + /* create port */ + debug_printk(("create new port\n")); + if ((rc = create_port(dp)) < 0) { + snd_printk(KERN_ERR "can't create port\n"); + goto _error; + } + + /* allocate queue */ + debug_printk(("allocate queue\n")); + if ((rc = alloc_seq_queue(dp)) < 0) + goto _error; + + /* set address */ + dp->addr.client = dp->cseq; + dp->addr.port = dp->port; + /*dp->addr.queue = dp->queue;*/ + /*dp->addr.channel = 0;*/ + + dp->seq_mode = level; + + /* set up file mode */ + dp->file_mode = translate_mode(file); + + /* initialize read queue */ + debug_printk(("initialize read queue\n")); + if (is_read_mode(dp->file_mode)) { + if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) { + rc = -ENOMEM; + goto _error; + } + } + + /* initialize write queue */ + debug_printk(("initialize write queue\n")); + if (is_write_mode(dp->file_mode)) { + dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); + if (dp->writeq == NULL) { + rc = -ENOMEM; + goto _error; + } + } + + /* initialize timer */ + debug_printk(("initialize timer\n")); + if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { + snd_printk(KERN_ERR "can't alloc timer\n"); + rc = -ENOMEM; + goto _error; + } + debug_printk(("timer initialized\n")); + + /* set private data pointer */ + file->private_data = dp; + + /* set up for mode2 */ + if (level == SNDRV_SEQ_OSS_MODE_MUSIC) + snd_seq_oss_synth_setup_midi(dp); + else if (is_read_mode(dp->file_mode)) + snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ); + + client_table[dp->index] = dp; + num_clients++; + + debug_printk(("open done\n")); + return 0; + + _error: + snd_seq_oss_synth_cleanup(dp); + snd_seq_oss_midi_cleanup(dp); + i = dp->queue; + delete_port(dp); + delete_seq_queue(i); + + return rc; +} + +/* + * translate file flags to private mode + */ +static int +translate_mode(struct file *file) +{ + int file_mode = 0; + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + file_mode |= SNDRV_SEQ_OSS_FILE_WRITE; + if ((file->f_flags & O_ACCMODE) != O_WRONLY) + file_mode |= SNDRV_SEQ_OSS_FILE_READ; + if (file->f_flags & O_NONBLOCK) + file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK; + return file_mode; +} + + +/* + * create sequencer port + */ +static int +create_port(seq_oss_devinfo_t *dp) +{ + int rc; + snd_seq_port_info_t port; + snd_seq_port_callback_t callback; + + memset(&port, 0, sizeof(port)); + port.addr.client = dp->cseq; + sprintf(port.name, "Sequencer-%d", dp->index); + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */ + port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; + port.midi_channels = 128; + port.synth_voices = 128; + + memset(&callback, 0, sizeof(callback)); + callback.owner = THIS_MODULE; + callback.private_data = dp; + callback.event_input = snd_seq_oss_event_input; + callback.private_free = free_devinfo; + port.kernel = &callback; + + rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + if (rc < 0) + return rc; + + dp->port = port.addr.port; + debug_printk(("new port = %d\n", port.addr.port)); + + return 0; +} + +/* + * delete ALSA port + */ +static int +delete_port(seq_oss_devinfo_t *dp) +{ + snd_seq_port_info_t port_info; + + if (dp->port < 0) + return 0; + + debug_printk(("delete_port %i\n", dp->port)); + memset(&port_info, 0, sizeof(port_info)); + port_info.addr.client = dp->cseq; + port_info.addr.port = dp->port; + return snd_seq_kernel_client_ctl(dp->cseq, + SNDRV_SEQ_IOCTL_DELETE_PORT, + &port_info); +} + +/* + * allocate a queue + */ +static int +alloc_seq_queue(seq_oss_devinfo_t *dp) +{ + snd_seq_queue_info_t qinfo; + int rc; + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.owner = system_client; + qinfo.locked = 1; + strcpy(qinfo.name, "OSS Sequencer Emulation"); + if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0) + return rc; + dp->queue = qinfo.queue; + return 0; +} + +/* + * release queue + */ +static int +delete_seq_queue(int queue) +{ + snd_seq_queue_info_t qinfo; + int rc; + + if (queue < 0) + return 0; + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.queue = queue; + rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo); + if (rc < 0) + printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc); + return rc; +} + + +/* + * free device informations - private_free callback of port + */ +static void +free_devinfo(void *private) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private; + + if (dp->timer) + snd_seq_oss_timer_delete(dp->timer); + + if (dp->writeq) + snd_seq_oss_writeq_delete(dp->writeq); + + if (dp->readq) + snd_seq_oss_readq_delete(dp->readq); + + kfree(dp); +} + + +/* + * close sequencer device + */ +void +snd_seq_oss_release(seq_oss_devinfo_t *dp) +{ + int queue; + + client_table[dp->index] = NULL; + num_clients--; + + debug_printk(("resetting..\n")); + snd_seq_oss_reset(dp); + + debug_printk(("cleaning up..\n")); + snd_seq_oss_synth_cleanup(dp); + snd_seq_oss_midi_cleanup(dp); + + /* clear slot */ + debug_printk(("releasing resource..\n")); + queue = dp->queue; + if (dp->port >= 0) + delete_port(dp); + delete_seq_queue(queue); + + debug_printk(("release done\n")); +} + + +/* + * Wait until the queue is empty (if we don't have nonblock) + */ +void +snd_seq_oss_drain_write(seq_oss_devinfo_t *dp) +{ + if (! dp->timer->running) + return; + if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) && + dp->writeq) { + debug_printk(("syncing..\n")); + while (snd_seq_oss_writeq_sync(dp->writeq)) + ; + } +} + + +/* + * reset sequencer devices + */ +void +snd_seq_oss_reset(seq_oss_devinfo_t *dp) +{ + int i; + + /* reset all synth devices */ + for (i = 0; i < dp->max_synthdev; i++) + snd_seq_oss_synth_reset(dp, i); + + /* reset all midi devices */ + if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) { + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_reset(dp, i); + } + + /* remove queues */ + if (dp->readq) + snd_seq_oss_readq_clear(dp->readq); + if (dp->writeq) + snd_seq_oss_writeq_clear(dp->writeq); + + /* reset timer */ + snd_seq_oss_timer_stop(dp->timer); +} + +/* + * proc interface + */ +void +snd_seq_oss_system_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_devinfo_t *dp; + + snd_iprintf(buf, "ALSA client number %d\n", system_client); + snd_iprintf(buf, "ALSA receiver port %d\n", system_port); + + snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients); + for (i = 0; i < num_clients; i++) { + snd_iprintf(buf, "\nApplication %d: ", i); + if ((dp = client_table[i]) == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue); + snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n", + (dp->seq_mode ? "music" : "synth"), + filemode_str(dp->file_mode)); + if (dp->seq_mode) + snd_iprintf(buf, " timer tempo = %d, timebase = %d\n", + dp->timer->oss_tempo, dp->timer->oss_timebase); + snd_iprintf(buf, " max queue length %d\n", maxqlen); + if (is_read_mode(dp->file_mode) && dp->readq) + snd_seq_oss_readq_info_read(dp->readq, buf); + } +} + +/* + * misc. functions for proc interface + */ +char * +enabled_str(int bool) +{ + return bool ? "enabled" : "disabled"; +} + +char * +filemode_str(int val) +{ + static char *str[] = { + "none", "read", "write", "read/write", + }; + return str[val & SNDRV_SEQ_OSS_FILE_ACMODE]; +} + + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_ioctl.c linux/sound/core/seq/oss/seq_oss_ioctl.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_ioctl.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_ioctl.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,190 @@ +/* + * OSS compatible sequencer driver + * + * OSS compatible i/o control + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_device.h" +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" +#include "seq_oss_timer.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_event.h" + +int +snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg) +{ + int dev, val; + struct synth_info inf; + struct midi_info minf; + unsigned char ev[8]; + void *arg = (void*)carg; + snd_seq_event_t tmpev; + + switch (cmd) { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + case SNDCTL_TMR_SELECT: + case SNDCTL_SEQ_CTRLRATE: + return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg); + + case SNDCTL_SEQ_PANIC: + debug_printk(("panic\n")); + snd_seq_oss_reset(dp); + return -EINVAL; + + case SNDCTL_SEQ_SYNC: + debug_printk(("sync\n")); + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return 0; + while (snd_seq_oss_writeq_sync(dp->writeq)) + ; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; + + case SNDCTL_SEQ_RESET: + debug_printk(("reset\n")); + snd_seq_oss_reset(dp); + return 0; + + case SNDCTL_SEQ_TESTMIDI: + debug_printk(("test midi\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + return snd_seq_oss_midi_open(dp, dev, dp->file_mode); + + case SNDCTL_SEQ_GETINCOUNT: + debug_printk(("get in count\n")); + if (dp->readq == NULL || ! is_read_mode(dp->file_mode)) + return 0; + return put_user(dp->readq->qlen, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_GETOUTCOUNT: + debug_printk(("get out count\n")); + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return 0; + return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_GETTIME: + debug_printk(("get time\n")); + return put_user(snd_seq_oss_timer_cur_tick(dp->timer), (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_RESETSAMPLES: + debug_printk(("reset samples\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + + case SNDCTL_SEQ_NRSYNTHS: + debug_printk(("nr synths\n")); + return put_user(dp->max_synthdev, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_NRMIDIS: + debug_printk(("nr midis\n")); + return put_user(dp->max_mididev, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SYNTH_MEMAVL: + debug_printk(("mem avail\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + return put_user(val, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_FM_4OP_ENABLE: + debug_printk(("4op\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + return 0; + + case SNDCTL_SYNTH_INFO: + case SNDCTL_SYNTH_ID: + debug_printk(("synth info\n")); + if (copy_from_user(&inf, arg, sizeof(inf))) + return -EFAULT; + if (snd_seq_oss_synth_make_info(dp, inf.device, &inf) < 0) + return -EINVAL; + if (copy_to_user(arg, &inf, sizeof(inf))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_OUTOFBAND: + debug_printk(("out of bound\n")); + if (copy_from_user(ev, arg, 8)) + return -EFAULT; + memset(&tmpev, 0, sizeof(tmpev)); + snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client); + tmpev.time.tick = 0; + if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) { + snd_seq_oss_dispatch(dp, &tmpev, 0, 0); + } + return 0; + + case SNDCTL_MIDI_INFO: + debug_printk(("midi info\n")); + if (copy_from_user(&minf, arg, sizeof(minf))) + return -EFAULT; + if (snd_seq_oss_midi_make_info(dp, minf.device, &minf) < 0) + return -EINVAL; + if (copy_to_user(arg, &minf, sizeof(minf))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_THRESHOLD: + debug_printk(("threshold\n")); + if (! is_write_mode(dp->file_mode)) + return 0; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 1) + val = 1; + if (val >= dp->writeq->maxlen) + val = dp->writeq->maxlen - 1; + snd_seq_oss_writeq_set_output(dp->writeq, val); + return 0; + + case SNDCTL_MIDI_PRETIME: + debug_printk(("pretime\n")); + if (dp->readq == NULL || !is_read_mode(dp->file_mode)) + return 0; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val <= 0) + val = -1; + else + val = (HZ * val) / 10; + dp->readq->pre_event_timeout = val; + return put_user(val, (int *)arg) ? -EFAULT : 0; + + default: + debug_printk(("others\n")); + if (! is_write_mode(dp->file_mode)) + return -EIO; + return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg); + } + return 0; +} + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_midi.c linux/sound/core/seq/oss/seq_oss_midi.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_midi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_midi.c 2003-03-11 08:28:20.000000000 -0700 @@ -0,0 +1,706 @@ +/* + * OSS compatible sequencer driver + * + * MIDI device handlers + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_midi.h" +#include "seq_oss_readq.h" +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include +#include "../seq_lock.h" +#include + + +/* + * constants + */ +#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30 + +/* + * definition of midi device record + */ +struct seq_oss_midi_t { + int seq_device; /* device number */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + unsigned int flags; /* port capability */ + int opened; /* flag for opening */ + unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME]; + snd_midi_event_t *coder; /* MIDI event coder */ + seq_oss_devinfo_t *devinfo; /* assigned OSSseq device */ + snd_use_lock_t use_lock; +}; + + +/* + * midi device table + */ +static int max_midi_devs; +static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; + +static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + +/* + * prototypes + */ +static seq_oss_midi_t *get_mdev(int dev); +static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev); +static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev); +static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev); + +/* + * look up the existing ports + * this looks a very exhausting job. + */ +int __init +snd_seq_oss_midi_lookup_ports(int client) +{ + snd_seq_system_info_t sysinfo; + snd_seq_client_info_t clinfo; + snd_seq_port_info_t pinfo; + int rc; + + rc = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SYSTEM_INFO, &sysinfo); + if (rc < 0) + return rc; + + memset(&clinfo, 0, sizeof(clinfo)); + memset(&pinfo, 0, sizeof(pinfo)); + clinfo.client = -1; + while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, &clinfo) == 0) { + if (clinfo.client == client) + continue; /* ignore myself */ + pinfo.addr.client = clinfo.client; + pinfo.addr.port = -1; + while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, &pinfo) == 0) + snd_seq_oss_midi_check_new_port(&pinfo); + } + return 0; +} + + +/* + */ +static seq_oss_midi_t * +get_mdev(int dev) +{ + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + mdev = midi_devs[dev]; + if (mdev) + snd_use_lock_use(&mdev->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return mdev; +} + +/* + * look for the identical slot + */ +static seq_oss_midi_t * +find_slot(int client, int port) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + mdev = midi_devs[i]; + if (mdev && mdev->client == client && mdev->port == port) { + /* found! */ + snd_use_lock_use(&mdev->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return mdev; + } + } + spin_unlock_irqrestore(®ister_lock, flags); + return NULL; +} + + +#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) +#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) +/* + * register a new port if it doesn't exist yet + */ +int +snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port)); + /* the port must include generic midi */ + if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) + return 0; + /* either read or write subscribable */ + if ((pinfo->capability & PERM_WRITE) != PERM_WRITE && + (pinfo->capability & PERM_READ) != PERM_READ) + return 0; + + /* + * look for the identical slot + */ + if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) { + /* already exists */ + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + /* + * allocate midi info record + */ + if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc midi info\n"); + return -ENOMEM; + } + + /* copy the port information */ + mdev->client = pinfo->addr.client; + mdev->port = pinfo->addr.port; + mdev->flags = pinfo->capability; + mdev->opened = 0; + snd_use_lock_init(&mdev->use_lock); + + /* copy and truncate the name of synth device */ + strncpy(mdev->name, pinfo->name, sizeof(mdev->name)); + mdev->name[sizeof(mdev->name) - 1] = 0; + + /* create MIDI coder */ + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { + snd_printk(KERN_ERR "can't malloc midi coder\n"); + kfree(mdev); + return -ENOMEM; + } + /* OSS sequencer adds running status to all sequences */ + snd_midi_event_no_status(mdev->coder, 1); + + /* + * look for en empty slot + */ + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + if (midi_devs[i] == NULL) + break; + } + if (i >= max_midi_devs) { + if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_midi_event_free(mdev->coder); + kfree(mdev); + return -ENOMEM; + } + max_midi_devs++; + } + mdev->seq_device = i; + midi_devs[mdev->seq_device] = mdev; + spin_unlock_irqrestore(®ister_lock, flags); + + return 0; +} + +/* + * release the midi device if it was registered + */ +int +snd_seq_oss_midi_check_exit_port(int client, int port) +{ + seq_oss_midi_t *mdev; + unsigned long flags; + int index; + + if ((mdev = find_slot(client, port)) != NULL) { + spin_lock_irqsave(®ister_lock, flags); + midi_devs[mdev->seq_device] = NULL; + spin_unlock_irqrestore(®ister_lock, flags); + snd_use_lock_free(&mdev->use_lock); + snd_use_lock_sync(&mdev->use_lock); + if (mdev->coder) + snd_midi_event_free(mdev->coder); + kfree(mdev); + } + spin_lock_irqsave(®ister_lock, flags); + for (index = max_midi_devs - 1; index >= 0; index--) { + if (midi_devs[index]) + break; + } + max_midi_devs = index + 1; + spin_unlock_irqrestore(®ister_lock, flags); + return 0; +} + + +/* + * release the midi device if it was registered + */ +void +snd_seq_oss_midi_clear_all(void) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + if ((mdev = midi_devs[i]) != NULL) { + if (mdev->coder) + snd_midi_event_free(mdev->coder); + kfree(mdev); + midi_devs[i] = NULL; + } + } + max_midi_devs = 0; + spin_unlock_irqrestore(®ister_lock, flags); +} + + +/* + * set up midi tables + */ +void +snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp) +{ + dp->max_mididev = max_midi_devs; +} + +/* + * clean up midi tables + */ +void +snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp) +{ + int i; + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_close(dp, i); + dp->max_mididev = 0; +} + + +/* + * open all midi devices. ignore errors. + */ +void +snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode) +{ + int i; + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_open(dp, i, file_mode); +} + + +/* + * get the midi device information + */ +static seq_oss_midi_t * +get_mididev(seq_oss_devinfo_t *dp, int dev) +{ + if (dev < 0 || dev >= dp->max_mididev) + return NULL; + return get_mdev(dev); +} + + +/* + * open the midi device if not opened yet + */ +int +snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode) +{ + int perm; + seq_oss_midi_t *mdev; + snd_seq_port_subscribe_t subs; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + + /* already used? */ + if (mdev->opened && mdev->devinfo != dp) { + snd_use_lock_free(&mdev->use_lock); + return -EBUSY; + } + + perm = 0; + if (is_write_mode(fmode)) + perm |= PERM_WRITE; + if (is_read_mode(fmode)) + perm |= PERM_READ; + perm &= mdev->flags; + if (perm == 0) { + snd_use_lock_free(&mdev->use_lock); + return -ENXIO; + } + + /* already opened? */ + if ((mdev->opened & perm) == perm) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + perm &= ~mdev->opened; + + memset(&subs, 0, sizeof(subs)); + + if (perm & PERM_WRITE) { + subs.sender = dp->addr; + subs.dest.client = mdev->client; + subs.dest.port = mdev->port; + if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) + mdev->opened |= PERM_WRITE; + } + if (perm & PERM_READ) { + subs.sender.client = mdev->client; + subs.sender.port = mdev->port; + subs.dest = dp->addr; + subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP; + subs.queue = dp->queue; /* queue for timestamps */ + if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) + mdev->opened |= PERM_READ; + } + + if (! mdev->opened) { + snd_use_lock_free(&mdev->use_lock); + return -ENXIO; + } + + mdev->devinfo = dp; + snd_use_lock_free(&mdev->use_lock); + return 0; +} + +/* + * close the midi device if already opened + */ +int +snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + snd_seq_port_subscribe_t subs; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + if (! mdev->opened || mdev->devinfo != dp) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened)); + memset(&subs, 0, sizeof(subs)); + if (mdev->opened & PERM_WRITE) { + subs.sender = dp->addr; + subs.dest.client = mdev->client; + subs.dest.port = mdev->port; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); + } + if (mdev->opened & PERM_READ) { + subs.sender.client = mdev->client; + subs.sender.port = mdev->port; + subs.dest = dp->addr; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); + } + + mdev->opened = 0; + mdev->devinfo = NULL; + + snd_use_lock_free(&mdev->use_lock); + return 0; +} + +/* + * change seq capability flags to file mode flags + */ +int +snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + int mode; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return 0; + + mode = 0; + if (mdev->opened & PERM_WRITE) + mode |= SNDRV_SEQ_OSS_FILE_WRITE; + if (mdev->opened & PERM_READ) + mode |= SNDRV_SEQ_OSS_FILE_READ; + + snd_use_lock_free(&mdev->use_lock); + return mode; +} + +/* + * reset the midi device and close it: + * so far, only close the device. + */ +void +snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return; + if (! mdev->opened) { + snd_use_lock_free(&mdev->use_lock); + return; + } + + if (mdev->opened & PERM_WRITE) { + snd_seq_event_t ev; + int c; + + debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port)); + memset(&ev, 0, sizeof(ev)); + ev.dest.client = mdev->client; + ev.dest.port = mdev->port; + ev.queue = dp->queue; + ev.source.port = dp->port; + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) { + ev.type = SNDRV_SEQ_EVENT_SENSING; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */ + } + for (c = 0; c < 16; c++) { + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + ev.data.control.channel = c; + ev.data.control.param = 123; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */ + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { + ev.data.control.param = 121; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */ + ev.type = SNDRV_SEQ_EVENT_PITCHBEND; + ev.data.control.value = 0; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */ + } + } + } + snd_seq_oss_midi_close(dp, dev); + snd_use_lock_free(&mdev->use_lock); +} + + +/* + * get client/port of the specified MIDI device + */ +void +snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return; + addr->client = mdev->client; + addr->port = mdev->port; + snd_use_lock_free(&mdev->use_lock); +} + + +/* + * input callback - this can be atomic + */ +int +snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data; + seq_oss_midi_t *mdev; + int rc; + + if (dp->readq == NULL) + return 0; + if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL) + return 0; + if (! (mdev->opened & PERM_READ)) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + rc = send_synth_event(dp, ev, mdev->seq_device); + else + rc = send_midi_event(dp, ev, mdev); + + snd_use_lock_free(&mdev->use_lock); + return rc; +} + +/* + * convert ALSA sequencer event to OSS synth event + */ +static int +send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev) +{ + evrec_t ossev; + + memset(&ossev, 0, sizeof(ossev)); + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + ossev.v.cmd = MIDI_NOTEON; break; + case SNDRV_SEQ_EVENT_NOTEOFF: + ossev.v.cmd = MIDI_NOTEOFF; break; + case SNDRV_SEQ_EVENT_KEYPRESS: + ossev.v.cmd = MIDI_KEY_PRESSURE; break; + case SNDRV_SEQ_EVENT_CONTROLLER: + ossev.l.cmd = MIDI_CTL_CHANGE; break; + case SNDRV_SEQ_EVENT_PGMCHANGE: + ossev.l.cmd = MIDI_PGM_CHANGE; break; + case SNDRV_SEQ_EVENT_CHANPRESS: + ossev.l.cmd = MIDI_CHN_PRESSURE; break; + case SNDRV_SEQ_EVENT_PITCHBEND: + ossev.l.cmd = MIDI_PITCH_BEND; break; + default: + return 0; /* not supported */ + } + + ossev.v.dev = dev; + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + case SNDRV_SEQ_EVENT_NOTEOFF: + case SNDRV_SEQ_EVENT_KEYPRESS: + ossev.v.code = EV_CHN_VOICE; + ossev.v.note = ev->data.note.note; + ossev.v.parm = ev->data.note.velocity; + break; + case SNDRV_SEQ_EVENT_CONTROLLER: + case SNDRV_SEQ_EVENT_PGMCHANGE: + case SNDRV_SEQ_EVENT_CHANPRESS: + ossev.l.code = EV_CHN_COMMON; + ossev.l.p1 = ev->data.control.param; + ossev.l.val = ev->data.control.value; + break; + case SNDRV_SEQ_EVENT_PITCHBEND: + ossev.l.code = EV_CHN_COMMON; + ossev.l.val = ev->data.control.value + 8192; + break; + } + + snd_seq_oss_readq_put_event(dp->readq, &ossev); + + return 0; +} + +/* + * decode event and send MIDI bytes to read queue + */ +static int +send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev) +{ + char msg[32]; + int len; + + snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode); + if (!dp->timer->running) + len = snd_seq_oss_timer_start(dp->timer); + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, + ev->data.ext.ptr, ev->data.ext.len); + } else { + len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); + if (len > 0) + snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len); + } + + return 0; +} + + +/* + * dump midi data + * return 0 : enqueued + * non-zero : invalid - ignored + */ +int +snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) { + snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); + snd_use_lock_free(&mdev->use_lock); + return 0; + } + snd_use_lock_free(&mdev->use_lock); + return -EINVAL; +} + +/* + * create OSS compatible midi_info record + */ +int +snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENXIO; + inf->device = dev; + inf->dev_type = 0; /* FIXME: ?? */ + inf->capabilities = 0; /* FIXME: ?? */ + strncpy(inf->name, mdev->name, sizeof(inf->name)); + snd_use_lock_free(&mdev->use_lock); + return 0; +} + + +/* + * proc interface + */ +static char * +capmode_str(int val) +{ + val &= PERM_READ|PERM_WRITE; + if (val == (PERM_READ|PERM_WRITE)) + return "read/write"; + else if (val == PERM_READ) + return "read"; + else if (val == PERM_WRITE) + return "write"; + else + return "none"; +} + +void +snd_seq_oss_midi_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_midi_t *mdev; + + snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs); + for (i = 0; i < max_midi_devs; i++) { + snd_iprintf(buf, "\nmidi %d: ", i); + mdev = get_mdev(i); + if (mdev == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name, + mdev->client, mdev->port); + snd_iprintf(buf, " capability %s / opened %s\n", + capmode_str(mdev->flags), + capmode_str(mdev->opened)); + snd_use_lock_free(&mdev->use_lock); + } +} + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_midi.h linux/sound/core/seq/oss/seq_oss_midi.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_midi.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_midi.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,49 @@ +/* + * OSS compatible sequencer driver + * + * midi device information + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_MIDI_H +#define __SEQ_OSS_MIDI_H + +#include "seq_oss_device.h" +#include + +typedef struct seq_oss_midi_t seq_oss_midi_t; + +int snd_seq_oss_midi_lookup_ports(int client); +int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo); +int snd_seq_oss_midi_check_exit_port(int client, int port); +void snd_seq_oss_midi_clear_all(void); + +void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp); +void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp); + +int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode); +void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode); +int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev); +void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev); +int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private); +int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf); +void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_misc.c linux/sound/core/seq/oss/seq_oss_misc.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_misc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_misc.c 2001-12-18 01:33:30.000000000 -0700 @@ -0,0 +1,110 @@ +/*---------------------------------------------------------------- + * miscellaneous functions + *----------------------------------------------------------------*/ + +unsigned short snd_seq_oss_semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short snd_seq_oss_cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; + +/* convert from MIDI note to frequency */ +int +snd_seq_oss_note_to_freq(int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + static int notes[] = { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +unsigned long +snd_seq_oss_compute_finetune(unsigned long base_freq, int bend, int range, int vibrato_cents) +{ + unsigned long amount; + int negative, semitones, cents, multiplier = 1; + + if (!bend || !range || !base_freq) + return base_freq; + + if (range >= 8192) + range = 8192; + + bend = bend * range / 8192; /* Convert to cents */ + bend += vibrato_cents; + + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) { + multiplier *= 4; + bend -= 2400; + } + + semitones = bend / 100; + if (semitones > 99) + semitones = 99; + cents = bend % 100; + + amount = (int) (snd_seq_oss_semitone_tuning[semitones] * multiplier * + snd_seq_oss_cent_tuning[cents]) / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_readq.c linux/sound/core/seq/oss/seq_oss_readq.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_readq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_readq.c 2002-10-08 04:54:44.000000000 -0600 @@ -0,0 +1,245 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_readq.c - MIDI input queue + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_readq.h" +#include "seq_oss_event.h" +#include +#include "../seq_lock.h" + +/* + * constants + */ +//#define SNDRV_SEQ_OSS_MAX_TIMEOUT (unsigned long)(-1) +#define SNDRV_SEQ_OSS_MAX_TIMEOUT (HZ * 3600) + + +/* + * prototypes + */ + + +/* + * create a read queue + */ +seq_oss_readq_t * +snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen) +{ + seq_oss_readq_t *q; + + if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc read queue\n"); + return NULL; + } + + if ((q->q = snd_kcalloc(sizeof(evrec_t) * maxlen, GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc read queue buffer\n"); + kfree(q); + return NULL; + } + + q->maxlen = maxlen; + q->qlen = 0; + q->head = q->tail = 0; + init_waitqueue_head(&q->midi_sleep); + spin_lock_init(&q->lock); + q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT; + q->input_time = (unsigned long)-1; + + return q; +} + +/* + * delete the read queue + */ +void +snd_seq_oss_readq_delete(seq_oss_readq_t *q) +{ + if (q) { + snd_seq_oss_readq_clear(q); /* to be sure */ + if (q->q) + kfree(q->q); + kfree(q); + } +} + +/* + * reset the read queue + */ +void +snd_seq_oss_readq_clear(seq_oss_readq_t *q) +{ + if (q->qlen) { + q->qlen = 0; + q->head = q->tail = 0; + } + /* if someone sleeping, wake'em up */ + if (waitqueue_active(&q->midi_sleep)) + wake_up(&q->midi_sleep); + q->input_time = (unsigned long)-1; +} + +/* + * put a midi byte + */ +int +snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len) +{ + evrec_t rec; + int result; + + rec.c[0] = SEQ_MIDIPUTC; + rec.c[2] = dev; + rec.c[3] = 0; + + while (len-- > 0) { + rec.c[1] = *data++; + result = snd_seq_oss_readq_put_event(q, &rec); + if (result < 0) + return result; + } + return 0; +} + +/* + * copy an event to input queue: + * return zero if enqueued + */ +int +snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->qlen >= q->maxlen - 1) { + spin_unlock_irqrestore(&q->lock, flags); + return -ENOMEM; + } + + memcpy(&q->q[q->tail], ev, ev_length(ev)); + q->tail = (q->tail + 1) % q->maxlen; + q->qlen++; + + /* wake up sleeper */ + if (waitqueue_active(&q->midi_sleep)) + wake_up(&q->midi_sleep); + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + + +/* + * pop queue + */ +evrec_t * +snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags) +{ + evrec_t *p; + + spin_lock_irqsave(&q->lock, *rflags); + if (q->qlen == 0) { + if (blocking) { + spin_unlock(&q->lock); + interruptible_sleep_on_timeout(&q->midi_sleep, + q->pre_event_timeout); + spin_lock(&q->lock); + } + if (q->qlen == 0) { + spin_unlock_irqrestore(&q->lock, *rflags); + return NULL; + } + } + p = q->q + q->head; + + return p; +} + +/* + * unlock queue + */ +void +snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags) +{ + spin_unlock_irqrestore(&q->lock, flags); +} + +/* + * drain one record and unlock queue + */ +void +snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags) +{ + if (q->qlen > 0) { + q->head = (q->head + 1) % q->maxlen; + q->qlen--; + } + spin_unlock_irqrestore(&q->lock, flags); +} + +/* + * polling/select: + * return non-zero if readq is not empty. + */ +unsigned int +snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait) +{ + poll_wait(file, &q->midi_sleep, wait); + return q->qlen; +} + +/* + * put a timestamp + */ +int +snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode) +{ + if (curt != q->input_time) { + evrec_t rec; + switch (seq_mode) { + case SNDRV_SEQ_OSS_MODE_SYNTH: + rec.echo = (curt << 8) | SEQ_WAIT; + snd_seq_oss_readq_put_event(q, &rec); + break; + case SNDRV_SEQ_OSS_MODE_MUSIC: + rec.t.code = EV_TIMING; + rec.t.cmd = TMR_WAIT_ABS; + rec.t.time = curt; + snd_seq_oss_readq_put_event(q, &rec); + break; + } + q->input_time = curt; + } + return 0; +} + + +/* + * proc interface + */ +void +snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf) +{ + snd_iprintf(buf, " read queue [%s] length = %d : tick = %ld\n", + (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"), + q->qlen, q->input_time); +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_readq.h linux/sound/core/seq/oss/seq_oss_readq.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_readq.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_readq.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,53 @@ +/* + * OSS compatible sequencer driver + * read fifo queue + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_READQ_H +#define __SEQ_OSS_READQ_H + +#include "seq_oss_device.h" + + +/* + * definition of read queue + */ +struct seq_oss_readq_t { + evrec_t *q; + int qlen; + int maxlen; + int head, tail; + unsigned long pre_event_timeout; + unsigned long input_time; + wait_queue_head_t midi_sleep; + spinlock_t lock; +}; + +seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen); +void snd_seq_oss_readq_delete(seq_oss_readq_t *q); +void snd_seq_oss_readq_clear(seq_oss_readq_t *readq); +unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait); +int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len); +int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev); +int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode); +evrec_t *snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags); +void snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags); +void snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_rw.c linux/sound/core/seq/oss/seq_oss_rw.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_rw.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_rw.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,196 @@ +/* + * OSS compatible sequencer driver + * + * read/write/select interface to device file + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_device.h" +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" +#include "seq_oss_synth.h" +#include +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include "../seq_clientmgr.h" + + +/* + * protoypes + */ +static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt); + + +/* + * read interface + */ + +int +snd_seq_oss_read(seq_oss_devinfo_t *dp, char *buf, int count) +{ + seq_oss_readq_t *readq = dp->readq; + int cnt, pos; + evrec_t *q; + unsigned long flags; + + if (readq == NULL || ! is_read_mode(dp->file_mode)) + return -EIO; + + /* copy queued events to read buffer */ + cnt = count; + pos = 0; + q = snd_seq_oss_readq_pick(readq, !is_nonblock_mode(dp->file_mode), &flags); + if (q == NULL) + return 0; + do { + int ev_len; + /* tansfer the data */ + ev_len = ev_length(q); + if (copy_to_user(buf + pos, q, ev_len)) { + snd_seq_oss_readq_unlock(readq, flags); + break; + } + snd_seq_oss_readq_free(readq, flags); + pos += ev_len; + cnt -= ev_len; + if (cnt < ev_len) + break; + } while ((q = snd_seq_oss_readq_pick(readq, 0, &flags)) != NULL); + + return count - cnt; +} + + +/* + * write interface + */ + +int +snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt) +{ + int rc, c, p, ev_size; + evrec_t rec; + + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return -EIO; + + c = count; + p = 0; + while (c >= SHORT_EVENT_SIZE) { + if (copy_from_user(rec.c, buf + p, SHORT_EVENT_SIZE)) + break; + p += SHORT_EVENT_SIZE; + + if (rec.s.code == SEQ_FULLSIZE) { + /* load patch */ + int fmt = (*(unsigned short *)rec.c) & 0xffff; + return snd_seq_oss_synth_load_patch(dp, rec.s.dev, fmt, buf, p, c); + + } + if (ev_is_long(&rec)) { + /* extended code */ + if (rec.s.code == SEQ_EXTENDED && + dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + ev_size = LONG_EVENT_SIZE; + if (c < ev_size) + break; + /* copy the reset 4 bytes */ + if (copy_from_user(rec.c + SHORT_EVENT_SIZE, buf + p, + LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) + break; + p += LONG_EVENT_SIZE - SHORT_EVENT_SIZE; + + } else { + /* old-type code */ + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + ev_size = SHORT_EVENT_SIZE; + } + + /* insert queue */ + if ((rc = insert_queue(dp, &rec, opt)) < 0) + break; + + c -= ev_size; + } + + if (count == c && is_nonblock_mode(dp->file_mode)) + return -EAGAIN; + return count - c; +} + + +/* + * insert event record to write queue + * return: 0 = OK, non-zero = NG + */ +static int +insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt) +{ + int rc = 0; + snd_seq_event_t event; + + /* if this is a timing event, process the current time */ + if (snd_seq_oss_process_timer_event(dp->timer, rec)) + return 0; /* no need to insert queue */ + + /* parse this event */ + memset(&event, 0, sizeof(event)); + /* set dummy -- to be sure */ + event.type = SNDRV_SEQ_EVENT_NOTEOFF; + snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client); + + if (snd_seq_oss_process_event(dp, rec, &event)) + return 0; /* invalid event - no need to insert queue */ + + event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); + if (dp->timer->realtime || !dp->timer->running) { + snd_seq_oss_dispatch(dp, &event, 0, 0); + } else { + if (is_nonblock_mode(dp->file_mode)) + rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0); + else + rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0); + } + return rc; +} + + +/* + * select / poll + */ + +unsigned int +snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + + /* input */ + if (dp->readq && is_read_mode(dp->file_mode)) { + if (snd_seq_oss_readq_poll(dp->readq, file, wait)) + mask |= POLLIN | POLLRDNORM; + } + + /* output */ + if (dp->writeq && is_write_mode(dp->file_mode)) { + if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_synth.c linux/sound/core/seq/oss/seq_oss_synth.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_synth.c 2003-03-11 13:16:02.000000000 -0700 @@ -0,0 +1,656 @@ +/* + * OSS compatible sequencer driver + * + * synth device handlers + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "../seq_lock.h" +#include + +/* + * constants + */ +#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30 +#define MAX_SYSEX_BUFLEN 128 + + +/* + * definition of synth info records + */ + +/* sysex buffer */ +struct seq_oss_synth_sysex_t { + int len; + int skip; + unsigned char buf[MAX_SYSEX_BUFLEN]; +}; + +/* synth info */ +struct seq_oss_synth_t { + int seq_device; + + /* for synth_info */ + int synth_type; + int synth_subtype; + int nr_voices; + + char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME]; + snd_seq_oss_callback_t oper; + + int opened; + + void *private_data; + snd_use_lock_t use_lock; +}; + + +/* + * device table + */ +static int max_synth_devs; +static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; +static seq_oss_synth_t midi_synth_dev = { + -1, /* seq_device */ + SYNTH_TYPE_MIDI, /* synth_type */ + 0, /* synth_subtype */ + 16, /* nr_voices */ + "MIDI", /* name */ +}; + +static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + +/* + * prototypes + */ +static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev); +static void reset_channels(seq_oss_synthinfo_t *info); + +/* + * global initialization + */ +void __init +snd_seq_oss_synth_init(void) +{ + snd_use_lock_init(&midi_synth_dev.use_lock); +} + +/* + * registration of the synth device + */ +int +snd_seq_oss_synth_register(snd_seq_device_t *dev) +{ + int i; + seq_oss_synth_t *rec; + snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + unsigned long flags; + + if ((rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc synth info\n"); + return -ENOMEM; + } + rec->seq_device = -1; + rec->synth_type = reg->type; + rec->synth_subtype = reg->subtype; + rec->nr_voices = reg->nvoices; + rec->oper = reg->oper; + rec->private_data = reg->private_data; + rec->opened = 0; + snd_use_lock_init(&rec->use_lock); + + /* copy and truncate the name of synth device */ + strncpy(rec->name, dev->name, sizeof(rec->name)); + rec->name[sizeof(rec->name)-1] = 0; + + /* registration */ + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_synth_devs; i++) { + if (synth_devs[i] == NULL) + break; + } + if (i >= max_synth_devs) { + if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_printk(KERN_ERR "no more synth slot\n"); + kfree(rec); + return -ENOMEM; + } + max_synth_devs++; + } + rec->seq_device = i; + synth_devs[i] = rec; + debug_printk(("synth %s registered %d\n", rec->name, i)); + spin_unlock_irqrestore(®ister_lock, flags); + dev->driver_data = rec; +#ifdef SNDRV_OSS_INFO_DEV_SYNTH + if (i < SNDRV_CARDS) + snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name); +#endif + return 0; +} + + +int +snd_seq_oss_synth_unregister(snd_seq_device_t *dev) +{ + int index; + seq_oss_synth_t *rec = dev->driver_data; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (index = 0; index < max_synth_devs; index++) { + if (synth_devs[index] == rec) + break; + } + if (index >= max_synth_devs) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_printk(KERN_ERR "can't unregister synth\n"); + return -EINVAL; + } + synth_devs[index] = NULL; + if (index == max_synth_devs - 1) { + for (index--; index >= 0; index--) { + if (synth_devs[index]) + break; + } + max_synth_devs = index + 1; + } + spin_unlock_irqrestore(®ister_lock, flags); +#ifdef SNDRV_OSS_INFO_DEV_SYNTH + if (rec->seq_device < SNDRV_CARDS) + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device); +#endif + + snd_use_lock_sync(&rec->use_lock); + kfree(rec); + + return 0; +} + + +/* + */ +static seq_oss_synth_t * +get_sdev(int dev) +{ + seq_oss_synth_t *rec; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + rec = synth_devs[dev]; + if (rec) + snd_use_lock_use(&rec->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return rec; +} + + +/* + * set up synth tables + */ + +void +snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp) +{ + int i; + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + dp->max_synthdev = max_synth_devs; + dp->synth_opened = 0; + memset(dp->synths, 0, sizeof(dp->synths)); + for (i = 0; i < dp->max_synthdev; i++) { + rec = get_sdev(i); + if (rec == NULL) + continue; + if (rec->oper.open == NULL || rec->oper.close == NULL) { + snd_use_lock_free(&rec->use_lock); + continue; + } + info = &dp->synths[i]; + info->arg.app_index = dp->port; + info->arg.file_mode = dp->file_mode; + info->arg.seq_mode = dp->seq_mode; + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) + info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; + else + info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; + info->opened = 0; + if (!try_module_get(rec->oper.owner)) { + snd_use_lock_free(&rec->use_lock); + continue; + } + if (rec->oper.open(&info->arg, rec->private_data) < 0) { + module_put(rec->oper.owner); + snd_use_lock_free(&rec->use_lock); + continue; + } + info->nr_voices = rec->nr_voices; + if (info->nr_voices > 0) { + info->ch = snd_kcalloc(sizeof(seq_oss_chinfo_t) * info->nr_voices, GFP_KERNEL); + reset_channels(info); + } + debug_printk(("synth %d assigned\n", i)); + info->opened++; + rec->opened++; + dp->synth_opened++; + snd_use_lock_free(&rec->use_lock); + } +} + + +/* + * set up synth tables for MIDI emulation - /dev/music mode only + */ + +void +snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp) +{ + int i; + + if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) + return; + + for (i = 0; i < dp->max_mididev; i++) { + seq_oss_synthinfo_t *info; + info = &dp->synths[dp->max_synthdev]; + if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0) + continue; + info->arg.app_index = dp->port; + info->arg.file_mode = dp->file_mode; + info->arg.seq_mode = dp->seq_mode; + info->arg.private_data = info; + info->is_midi = 1; + info->midi_mapped = i; + info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; + snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr); + info->opened = 1; + midi_synth_dev.opened++; + dp->max_synthdev++; + if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) + break; + } +} + + +/* + * clean up synth tables + */ + +void +snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp) +{ + int i; + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return); + for (i = 0; i < dp->max_synthdev; i++) { + info = &dp->synths[i]; + if (! info->opened) + continue; + if (info->is_midi) { + if (midi_synth_dev.opened > 0) { + snd_seq_oss_midi_close(dp, info->midi_mapped); + midi_synth_dev.opened--; + } + } else { + rec = get_sdev(i); + if (rec == NULL) + continue; + if (rec->opened > 0) { + debug_printk(("synth %d closed\n", i)); + rec->oper.close(&info->arg); + module_put(rec->oper.owner); + rec->opened = 0; + } + snd_use_lock_free(&rec->use_lock); + } + if (info->sysex) { + kfree(info->sysex); + info->sysex = NULL; + } + if (info->ch) { + kfree(info->ch); + info->ch = NULL; + } + } + dp->synth_opened = 0; + dp->max_synthdev = 0; +} + +/* + * check if the specified device is MIDI mapped device + */ +static int +is_midi_dev(seq_oss_devinfo_t *dp, int dev) +{ + if (dev < 0 || dev >= dp->max_synthdev) + return 0; + if (dp->synths[dev].is_midi) + return 1; + return 0; +} + +/* + * return synth device information pointer + */ +static seq_oss_synth_t * +get_synthdev(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + if (dev < 0 || dev >= dp->max_synthdev) + return NULL; + if (! dp->synths[dev].opened) + return NULL; + if (dp->synths[dev].is_midi) + return &midi_synth_dev; + if ((rec = get_sdev(dev)) == NULL) + return NULL; + if (! rec->opened) { + snd_use_lock_free(&rec->use_lock); + return NULL; + } + return rec; +} + + +/* + * reset note and velocity on each channel. + */ +static void +reset_channels(seq_oss_synthinfo_t *info) +{ + int i; + if (info->ch == NULL || ! info->nr_voices) + return; + for (i = 0; i < info->nr_voices; i++) { + info->ch[i].note = -1; + info->ch[i].vel = 0; + } +} + + +/* + * reset synth device: + * call reset callback. if no callback is defined, send a heartbeat + * event to the corresponding port. + */ +void +snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + snd_assert(dev >= 0 && dev < dp->max_synthdev, return); + info = &dp->synths[dev]; + if (! info->opened) + return; + if (info->sysex) + info->sysex->len = 0; /* reset sysex */ + reset_channels(info); + if (info->is_midi) { + if (midi_synth_dev.opened <= 0) + return; + snd_seq_oss_midi_reset(dp, info->midi_mapped); + if (snd_seq_oss_midi_open(dp, info->midi_mapped, + dp->file_mode) < 0) { + midi_synth_dev.opened--; + info->opened = 0; + if (info->sysex) { + kfree(info->sysex); + info->sysex = NULL; + } + if (info->ch) { + kfree(info->ch); + info->ch = NULL; + } + } + return; + } + + rec = get_sdev(dev); + if (rec == NULL) + return; + if (rec->oper.reset) { + rec->oper.reset(&info->arg); + } else { + snd_seq_event_t ev; + memset(&ev, 0, sizeof(ev)); + snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client, + info->arg.addr.port); + ev.type = SNDRV_SEQ_EVENT_RESET; + snd_seq_oss_dispatch(dp, &ev, 0, 0); + } + snd_use_lock_free(&rec->use_lock); +} + + +/* + * load a patch record: + * call load_patch callback function + */ +int +snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, + const char *buf, int p, int c) +{ + seq_oss_synth_t *rec; + int rc; + + if (dev < 0 || dev >= dp->max_synthdev) + return -ENXIO; + + if (is_midi_dev(dp, dev)) + return 0; + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + + if (rec->oper.load_patch == NULL) + rc = -ENXIO; + else + rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c); + snd_use_lock_free(&rec->use_lock); + return rc; +} + +/* + * check if the device is valid synth device + */ +int +snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + rec = get_synthdev(dp, dev); + if (rec) { + snd_use_lock_free(&rec->use_lock); + return 1; + } + return 0; +} + + +/* + * receive OSS 6 byte sysex packet: + * the full sysex message will be sent if it reaches to the end of data + * (0xff). + */ +int +snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev) +{ + int i, send; + unsigned char *dest; + seq_oss_synth_sysex_t *sysex; + + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + sysex = dp->synths[dev].sysex; + if (sysex == NULL) { + sysex = snd_kcalloc(sizeof(*sysex), GFP_KERNEL); + if (sysex == NULL) + return -ENOMEM; + dp->synths[dev].sysex = sysex; + } + + send = 0; + dest = sysex->buf + sysex->len; + /* copy 6 byte packet to the buffer */ + for (i = 0; i < 6; i++) { + if (buf[i] == 0xff) { + send = 1; + break; + } + dest[i] = buf[i]; + sysex->len++; + if (sysex->len >= MAX_SYSEX_BUFLEN) { + sysex->len = 0; + sysex->skip = 1; + break; + } + } + + if (sysex->len && send) { + if (sysex->skip) { + sysex->skip = 0; + sysex->len = 0; + return -EINVAL; /* skip */ + } + /* copy the data to event record and send it */ + ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + if (snd_seq_oss_synth_addr(dp, dev, ev)) + return -EINVAL; + ev->data.ext.len = sysex->len; + ev->data.ext.ptr = sysex->buf; + sysex->len = 0; + return 0; + } + + return -EINVAL; /* skip */ +} + +/* + * fill the event source/destination addresses + */ +int +snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -EINVAL; + snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client, + dp->synths[dev].arg.addr.port); + return 0; +} + + +/* + * OSS compatible ioctl + */ +int +snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr) +{ + seq_oss_synth_t *rec; + int rc; + + if (is_midi_dev(dp, dev)) + return -ENXIO; + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + if (rec->oper.ioctl == NULL) + rc = -ENXIO; + else + rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr); + snd_use_lock_free(&rec->use_lock); + return rc; +} + + +/* + * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME + */ +int +snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev)) + return -ENXIO; + ev->type = SNDRV_SEQ_EVENT_OSS; + memcpy(ev->data.raw8.d, data, 8); + return snd_seq_oss_synth_addr(dp, dev, ev); +} + + +/* + * create OSS compatible synth_info record + */ +int +snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf) +{ + seq_oss_synth_t *rec; + + if (dp->synths[dev].is_midi) { + struct midi_info minf; + snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf); + inf->synth_type = SYNTH_TYPE_MIDI; + inf->synth_subtype = 0; + inf->nr_voices = 16; + inf->device = dev; + strncpy(inf->name, minf.name, sizeof(inf->name)); + } else { + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + inf->synth_type = rec->synth_type; + inf->synth_subtype = rec->synth_subtype; + inf->nr_voices = rec->nr_voices; + inf->device = dev; + strncpy(inf->name, rec->name, sizeof(inf->name)); + snd_use_lock_free(&rec->use_lock); + } + return 0; +} + + +/* + * proc interface + */ +void +snd_seq_oss_synth_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_synth_t *rec; + + snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs); + for (i = 0; i < max_synth_devs; i++) { + snd_iprintf(buf, "\nsynth %d: ", i); + rec = get_sdev(i); + if (rec == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "[%s]\n", rec->name); + snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n", + rec->synth_type, rec->synth_subtype, + rec->nr_voices); + snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n", + enabled_str((long)rec->oper.ioctl), + enabled_str((long)rec->oper.load_patch)); + snd_use_lock_free(&rec->use_lock); + } +} + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_synth.h linux/sound/core/seq/oss/seq_oss_synth.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_synth.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_synth.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,49 @@ +/* + * OSS compatible sequencer driver + * + * synth device information + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_SYNTH_H +#define __SEQ_OSS_SYNTH_H + +#include "seq_oss_device.h" +#include +#include + +typedef struct seq_oss_synth_t seq_oss_synth_t; + +void snd_seq_oss_synth_init(void); +int snd_seq_oss_synth_register(snd_seq_device_t *dev); +int snd_seq_oss_synth_unregister(snd_seq_device_t *dev); +void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp); +void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp); +void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp); + +void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char *buf, int p, int c); +int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev); +int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev); +int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr); +int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev); + +int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_timer.c linux/sound/core/seq/oss/seq_oss_timer.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_timer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_timer.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,283 @@ +/* + * OSS compatible sequencer driver + * + * Timer control routines + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include + +/* + */ +#define MIN_OSS_TEMPO 8 +#define MAX_OSS_TEMPO 360 +#define MIN_OSS_TIMEBASE 1 +#define MAX_OSS_TIMEBASE 1000 + +/* + */ +static void calc_alsa_tempo(seq_oss_timer_t *timer); +static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value); + + +/* + * create and register a new timer. + * if queue is not started yet, start it. + */ +seq_oss_timer_t * +snd_seq_oss_timer_new(seq_oss_devinfo_t *dp) +{ + seq_oss_timer_t *rec; + + rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL); + if (rec == NULL) + return NULL; + + rec->dp = dp; + rec->cur_tick = 0; + rec->realtime = 0; + rec->running = 0; + rec->oss_tempo = 60; + rec->oss_timebase = 100; + calc_alsa_tempo(rec); + + return rec; +} + + +/* + * delete timer. + * if no more timer exists, stop the queue. + */ +void +snd_seq_oss_timer_delete(seq_oss_timer_t *rec) +{ + if (rec) { + snd_seq_oss_timer_stop(rec); + kfree(rec); + } +} + + +/* + * process one timing event + * return 1 : event proceseed -- skip this event + * 0 : not a timer event -- enqueue this event + */ +int +snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev) +{ + abstime_t parm = ev->t.time; + + if (ev->t.code == EV_TIMING) { + switch (ev->t.cmd) { + case TMR_WAIT_REL: + parm += rec->cur_tick; + rec->realtime = 0; + /* continue to next */ + case TMR_WAIT_ABS: + if (parm == 0) { + rec->realtime = 1; + } else if (parm >= rec->cur_tick) { + rec->realtime = 0; + rec->cur_tick = parm; + } + return 1; /* skip this event */ + + case TMR_START: + snd_seq_oss_timer_start(rec); + return 1; + + } + } else if (ev->s.code == SEQ_WAIT) { + /* time = from 1 to 3 bytes */ + parm = (ev->echo >> 8) & 0xffffff; + if (parm > rec->cur_tick) { + /* set next event time */ + rec->cur_tick = parm; + rec->realtime = 0; + } + return 1; + } + + return 0; +} + + +/* + * convert tempo units + */ +static void +calc_alsa_tempo(seq_oss_timer_t *timer) +{ + timer->tempo = (60 * 1000000) / timer->oss_tempo; + timer->ppq = timer->oss_timebase; +} + + +/* + * dispatch a timer event + */ +static int +send_timer_event(seq_oss_devinfo_t *dp, int type, int value) +{ + snd_seq_event_t ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = type; + ev.source.client = dp->cseq; + ev.source.port = 0; + ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM; + ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + ev.queue = dp->queue; + ev.data.queue.queue = dp->queue; + ev.data.queue.param.value = value; + return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 0, 0); +} + +/* + * set queue tempo and start queue + */ +int +snd_seq_oss_timer_start(seq_oss_timer_t *timer) +{ + seq_oss_devinfo_t *dp = timer->dp; + snd_seq_queue_tempo_t tmprec; + + if (timer->running) + snd_seq_oss_timer_stop(timer); + + memset(&tmprec, 0, sizeof(tmprec)); + tmprec.queue = dp->queue; + tmprec.ppq = timer->ppq; + tmprec.tempo = timer->tempo; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, &tmprec); + + send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0); + timer->running = 1; + timer->cur_tick = 0; + return 0; +} + + +/* + * stop queue + */ +int +snd_seq_oss_timer_stop(seq_oss_timer_t *timer) +{ + if (! timer->running) + return 0; + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0); + timer->running = 0; + return 0; +} + + +/* + * continue queue + */ +int +snd_seq_oss_timer_continue(seq_oss_timer_t *timer) +{ + if (timer->running) + return 0; + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0); + timer->running = 1; + return 0; +} + + +/* + * change queue tempo + */ +int +snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value) +{ + if (value < MIN_OSS_TEMPO) + value = MIN_OSS_TEMPO; + else if (value > MAX_OSS_TEMPO) + value = MAX_OSS_TEMPO; + timer->oss_tempo = value; + calc_alsa_tempo(timer); + if (timer->running) + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo); + return 0; +} + + +/* + * ioctls + */ +int +snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg) +{ + int value; + + if (cmd == SNDCTL_SEQ_CTRLRATE) { + debug_printk(("ctrl rate\n")); + /* if *arg == 0, just return the current rate */ + if (get_user(value, (int *)arg)) + return -EFAULT; + if (value) + return -EINVAL; + value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60; + return put_user(value, (int *)arg) ? -EFAULT : 0; + } + + if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) + return 0; + + switch (cmd) { + case SNDCTL_TMR_START: + debug_printk(("timer start\n")); + return snd_seq_oss_timer_start(timer); + case SNDCTL_TMR_STOP: + debug_printk(("timer stop\n")); + return snd_seq_oss_timer_stop(timer); + case SNDCTL_TMR_CONTINUE: + debug_printk(("timer continue\n")); + return snd_seq_oss_timer_continue(timer); + case SNDCTL_TMR_TEMPO: + debug_printk(("timer tempo\n")); + if (get_user(value, (int *)arg)) + return -EFAULT; + return snd_seq_oss_timer_tempo(timer, value); + case SNDCTL_TMR_TIMEBASE: + debug_printk(("timer timebase\n")); + if (get_user(value, (int *)arg)) + return -EFAULT; + if (value < MIN_OSS_TIMEBASE) + value = MIN_OSS_TIMEBASE; + else if (value > MAX_OSS_TIMEBASE) + value = MAX_OSS_TIMEBASE; + timer->oss_timebase = value; + calc_alsa_tempo(timer); + return 0; + + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SELECT: + case SNDCTL_TMR_SOURCE: + debug_printk(("timer XXX\n")); + /* not supported */ + return 0; + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_timer.h linux/sound/core/seq/oss/seq_oss_timer.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_timer.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_timer.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,70 @@ +/* + * OSS compatible sequencer driver + * timer handling routines + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_TIMER_H +#define __SEQ_OSS_TIMER_H + +#include "seq_oss_device.h" + +/* + * timer information definition + */ +struct seq_oss_timer_t { + seq_oss_devinfo_t *dp; + reltime_t cur_tick; + int realtime; + int running; + int tempo, ppq; /* ALSA queue */ + int oss_tempo, oss_timebase; +}; + + +seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp); +void snd_seq_oss_timer_delete(seq_oss_timer_t *dp); + +int snd_seq_oss_timer_start(seq_oss_timer_t *timer); +int snd_seq_oss_timer_stop(seq_oss_timer_t *timer); +int snd_seq_oss_timer_continue(seq_oss_timer_t *timer); +int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value); +#define snd_seq_oss_timer_reset snd_seq_oss_timer_start + +int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg); + +/* + * get current processed time + */ +static inline abstime_t +snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer) +{ + return timer->cur_tick; +} + + +/* + * is realtime event? + */ +static inline int +snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer) +{ + return timer->realtime; +} + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_writeq.c linux/sound/core/seq/oss/seq_oss_writeq.c --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_writeq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_writeq.c 2002-10-08 04:54:45.000000000 -0600 @@ -0,0 +1,184 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_writeq.c - write queue and sync + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_oss_writeq.h" +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include +#include "../seq_lock.h" +#include "../seq_clientmgr.h" + + +/* + * create a write queue record + */ +seq_oss_writeq_t * +snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen) +{ + seq_oss_writeq_t *q; + snd_seq_client_pool_t pool; + + if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) + return NULL; + q->dp = dp; + q->maxlen = maxlen; + spin_lock_init(&q->sync_lock); + q->sync_event_put = 0; + q->sync_time = 0; + init_waitqueue_head(&q->sync_sleep); + + memset(&pool, 0, sizeof(pool)); + pool.client = dp->cseq; + pool.output_pool = maxlen; + pool.output_room = maxlen / 2; + + snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool); + + return q; +} + +/* + * delete the write queue + */ +void +snd_seq_oss_writeq_delete(seq_oss_writeq_t *q) +{ + snd_seq_oss_writeq_clear(q); /* to be sure */ + kfree(q); +} + + +/* + * reset the write queue + */ +void +snd_seq_oss_writeq_clear(seq_oss_writeq_t *q) +{ + snd_seq_remove_events_t reset; + + memset(&reset, 0, sizeof(reset)); + reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */ + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset); + + /* wake up sleepers if any */ + snd_seq_oss_writeq_wakeup(q, 0); +} + +/* + * wait until the write buffer has enough room + */ +int +snd_seq_oss_writeq_sync(seq_oss_writeq_t *q) +{ + seq_oss_devinfo_t *dp = q->dp; + abstime_t time; + unsigned long flags; + + time = snd_seq_oss_timer_cur_tick(dp->timer); + if (q->sync_time >= time) + return 0; /* already finished */ + + if (! q->sync_event_put) { + snd_seq_event_t ev; + evrec_t *rec; + + /* put echoback event */ + memset(&ev, 0, sizeof(ev)); + ev.flags = 0; + ev.type = SNDRV_SEQ_EVENT_ECHO; + ev.time.tick = time; + /* echo back to itself */ + snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port); + rec = (evrec_t*)&ev.data; + rec->t.code = SEQ_SYNCTIMER; + rec->t.time = time; + q->sync_event_put = 1; + snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0); + } + + spin_lock_irqsave(&q->sync_lock, flags); + if (! q->sync_event_put) { /* echoback event has been received */ + spin_unlock_irqrestore(&q->sync_lock, flags); + return 0; + } + + /* wait for echo event */ + spin_unlock(&q->sync_lock); + interruptible_sleep_on_timeout(&q->sync_sleep, HZ); + spin_lock(&q->sync_lock); + if (signal_pending(current)) { + /* interrupted - return 0 to finish sync */ + q->sync_event_put = 0; + spin_unlock_irqrestore(&q->sync_lock, flags); + return 0; + } + spin_unlock_irqrestore(&q->sync_lock, flags); + if (q->sync_time >= time) + return 0; + else + return 1; +} + +/* + * wake up sync - echo event was catched + */ +void +snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time) +{ + unsigned long flags; + + spin_lock_irqsave(&q->sync_lock, flags); + q->sync_time = time; + q->sync_event_put = 0; + if (waitqueue_active(&q->sync_sleep)) { + wake_up(&q->sync_sleep); + } + spin_unlock_irqrestore(&q->sync_lock, flags); +} + + +/* + * return the unused pool size + */ +int +snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q) +{ + snd_seq_client_pool_t pool; + pool.client = q->dp->cseq; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool); + return pool.output_free; +} + + +/* + * set output threshold size from ioctl + */ +void +snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val) +{ + snd_seq_client_pool_t pool; + pool.client = q->dp->cseq; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool); + pool.output_room = val; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool); +} + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_writeq.h linux/sound/core/seq/oss/seq_oss_writeq.h --- linux-2.4.21-rc1.orig/sound/core/seq/oss/seq_oss_writeq.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/oss/seq_oss_writeq.h 2003-04-29 23:22:24.000000000 -0600 @@ -0,0 +1,50 @@ +/* + * OSS compatible sequencer driver + * write priority queue + * + * Copyright (C) 1998,99 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_WRITEQ_H +#define __SEQ_OSS_WRITEQ_H + +#include "seq_oss_device.h" + + +struct seq_oss_writeq_t { + seq_oss_devinfo_t *dp; + int maxlen; + abstime_t sync_time; + int sync_event_put; + wait_queue_head_t sync_sleep; + spinlock_t sync_lock; +}; + + +/* + * seq_oss_writeq.c + */ +seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen); +void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q); +int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time); +int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size); + + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq.c linux/sound/core/seq/seq.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq.c 2002-12-02 08:11:45.000000000 -0700 @@ -0,0 +1,141 @@ +/* + * ALSA sequencer main module + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include +#include "seq_clientmgr.h" +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_lock.h" +#include "seq_timer.h" +#include "seq_system.h" +#include "seq_info.h" +#include + +int seq_client_load[64] = {[0 ... 63] = -1}; +int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL; +int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE; +int seq_default_timer_card = -1; +int seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM; +int seq_default_timer_subdevice = 0; +int seq_default_timer_resolution = 0; /* Hz */ + +MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +MODULE_PARM(seq_client_load, "1-64i"); +MODULE_PARM_DESC(seq_client_load, "The numbers of global (system) clients to load through kmod."); +MODULE_PARM(seq_default_timer_class, "i"); +MODULE_PARM_DESC(seq_default_timer_class, "The default timer class."); +MODULE_PARM(seq_default_timer_sclass, "i"); +MODULE_PARM_DESC(seq_default_timer_sclass, "The default timer slave class."); +MODULE_PARM(seq_default_timer_card, "i"); +MODULE_PARM_DESC(seq_default_timer_card, "The default timer card number."); +MODULE_PARM(seq_default_timer_device, "i"); +MODULE_PARM_DESC(seq_default_timer_device, "The default timer device number."); +MODULE_PARM(seq_default_timer_subdevice, "i"); +MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice number."); +MODULE_PARM(seq_default_timer_resolution, "i"); +MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz."); + +/* + * INIT PART + */ + + +static int __init alsa_seq_init(void) +{ + int err; + + if ((err = client_init_data()) < 0) + return err; + + /* init memory, room for selected events */ + if ((err = snd_sequencer_memory_init()) < 0) + return err; + + /* init event queues */ + if ((err = snd_seq_queues_init()) < 0) + return err; + + /* register sequencer device */ + if ((err = snd_sequencer_device_init()) < 0) + return err; + + /* register proc interface */ + if ((err = snd_seq_info_init()) < 0) + return err; + + /* register our internal client */ + if ((err = snd_seq_system_client_init()) < 0) + return err; + + return 0; +} + +static void __exit alsa_seq_exit(void) +{ + /* unregister our internal client */ + snd_seq_system_client_done(); + + /* unregister proc interface */ + snd_seq_info_done(); + + /* delete timing queues */ + snd_seq_queues_delete(); + + /* unregister sequencer device */ + snd_sequencer_device_done(); + + /* release event memory */ + snd_sequencer_memory_done(); +} + +module_init(alsa_seq_init) +module_exit(alsa_seq_exit) + + /* seq_clientmgr.c */ +EXPORT_SYMBOL(snd_seq_create_kernel_client); +EXPORT_SYMBOL(snd_seq_delete_kernel_client); +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); +EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); +EXPORT_SYMBOL(snd_seq_kernel_client_ctl); +EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); + /* seq_memory.c */ +EXPORT_SYMBOL(snd_seq_expand_var_event); +EXPORT_SYMBOL(snd_seq_dump_var_event); + /* seq_ports.c */ +EXPORT_SYMBOL(snd_seq_event_port_attach); +EXPORT_SYMBOL(snd_seq_event_port_detach); + /* seq_lock.c */ +#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG) +/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/ +/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/ +EXPORT_SYMBOL(snd_use_lock_sync_helper); +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_clientmgr.c linux/sound/core/seq/seq_clientmgr.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_clientmgr.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_clientmgr.c 2003-01-31 08:19:32.000000000 -0700 @@ -0,0 +1,2509 @@ +/* + * ALSA sequencer Client Manager + * Copyright (c) 1998-2001 by Frank van de Pol + * Jaroslav Kysela + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "seq_clientmgr.h" +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_timer.h" +#include "seq_info.h" +#include "seq_system.h" +#include +#if defined(CONFIG_SND_BIT32_EMUL) || defined(CONFIG_SND_BIT32_EMUL_MODULE) +#include "../ioctl32/ioctl32.h" +#endif + +/* Client Manager + + * this module handles the connections of userland and kernel clients + * + */ + +#define SNDRV_SEQ_LFLG_INPUT 0x0001 +#define SNDRV_SEQ_LFLG_OUTPUT 0x0002 +#define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT) + +static spinlock_t clients_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(register_mutex); + +/* + * client table + */ +static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; +static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS]; +static usage_t client_usage = {0, 0}; + +/* + * prototypes + */ +static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop); +static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop); + +/* + */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* + */ +static inline unsigned short snd_seq_file_flags(struct file *file) +{ + switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_WRITE: + return SNDRV_SEQ_LFLG_OUTPUT; + case FMODE_READ: + return SNDRV_SEQ_LFLG_INPUT; + default: + return SNDRV_SEQ_LFLG_OPEN; + } +} + +static inline int snd_seq_write_pool_allocated(client_t *client) +{ + return snd_seq_total_cells(client->pool) > 0; +} + +/* return pointer to client structure for specified id */ +static client_t *clientptr(int clientid) +{ + if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { + snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid); + return NULL; + } + return clienttab[clientid]; +} + +extern int seq_client_load[]; + +client_t *snd_seq_client_use_ptr(int clientid) +{ + unsigned long flags; + client_t *client; + + if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { + snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid); + return NULL; + } + spin_lock_irqsave(&clients_lock, flags); + client = clientptr(clientid); + if (client) + goto __lock; + if (clienttablock[clientid]) { + spin_unlock_irqrestore(&clients_lock, flags); + return NULL; + } + spin_unlock_irqrestore(&clients_lock, flags); +#ifdef CONFIG_KMOD + if (!in_interrupt() && current->fs->root) { + static char client_requested[64]; + static char card_requested[SNDRV_CARDS]; + if (clientid < 64) { + int idx; + char name[32]; + + if (! client_requested[clientid]) { + client_requested[clientid] = 1; + for (idx = 0; idx < 64; idx++) { + if (seq_client_load[idx] < 0) + break; + if (seq_client_load[idx] == clientid) { + sprintf(name, "snd-seq-client-%i", clientid); + request_module(name); + break; + } + } + } + } else if (clientid >= 64 && clientid < 128) { + int card = (clientid - 64) / 8; + if (card < snd_ecards_limit) { + if (! card_requested[card]) { + card_requested[card] = 1; + snd_request_card(card); + } + snd_seq_device_load_drivers(); + } + } + spin_lock_irqsave(&clients_lock, flags); + client = clientptr(clientid); + if (client) + goto __lock; + spin_unlock_irqrestore(&clients_lock, flags); + } +#endif + return NULL; + + __lock: + snd_use_lock_use(&client->use_lock); + spin_unlock_irqrestore(&clients_lock, flags); + return client; +} + +static void usage_alloc(usage_t * res, int num) +{ + res->cur += num; + if (res->cur > res->peak) + res->peak = res->cur; +} + +static void usage_free(usage_t * res, int num) +{ + res->cur -= num; +} + +/* initialise data structures */ +int __init client_init_data(void) +{ + /* zap out the client table */ + memset(&clienttablock, 0, sizeof(clienttablock)); + memset(&clienttab, 0, sizeof(clienttab)); + return 0; +} + + +static client_t *seq_create_client1(int client_index, int poolsize) +{ + unsigned long flags; + int c; + client_t *client; + + /* init client data */ + client = snd_kcalloc(sizeof(client_t), GFP_KERNEL); + if (client == NULL) + return NULL; + client->pool = snd_seq_pool_new(poolsize); + if (client->pool == NULL) { + kfree(client); + return NULL; + } + client->type = NO_CLIENT; + snd_use_lock_init(&client->use_lock); + rwlock_init(&client->ports_lock); + init_MUTEX(&client->ports_mutex); + INIT_LIST_HEAD(&client->ports_list_head); + + /* find free slot in the client table */ + spin_lock_irqsave(&clients_lock, flags); + if (client_index < 0) { + for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) { + if (clienttab[c] || clienttablock[c]) + continue; + clienttab[client->number = c] = client; + spin_unlock_irqrestore(&clients_lock, flags); + return client; + } + } else { + if (clienttab[client_index] == NULL && !clienttablock[client_index]) { + clienttab[client->number = client_index] = client; + spin_unlock_irqrestore(&clients_lock, flags); + return client; + } + } + spin_unlock_irqrestore(&clients_lock, flags); + snd_seq_pool_delete(&client->pool); + kfree(client); + return NULL; /* no free slot found or busy, return failure code */ +} + + +static int seq_free_client1(client_t *client) +{ + unsigned long flags; + + snd_assert(client != NULL, return -EINVAL); + snd_seq_delete_all_ports(client); + snd_seq_queue_client_leave(client->number); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 1; + clienttab[client->number] = NULL; + spin_unlock_irqrestore(&clients_lock, flags); + snd_use_lock_sync(&client->use_lock); + snd_seq_queue_client_termination(client->number); + if (client->pool) + snd_seq_pool_delete(&client->pool); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 0; + spin_unlock_irqrestore(&clients_lock, flags); + return 0; +} + + +static void seq_free_client(client_t * client) +{ + down(®ister_mutex); + switch (client->type) { + case NO_CLIENT: + snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number); + break; + case USER_CLIENT: + case KERNEL_CLIENT: + seq_free_client1(client); + usage_free(&client_usage, 1); + break; + + default: + snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type); + } + up(®ister_mutex); + + snd_seq_system_client_ev_client_exit(client->number); +} + + + +/* -------------------------------------------------------- */ + +/* create a user client */ +static int snd_seq_open(struct inode *inode, struct file *file) +{ + int c, mode; /* client id */ + client_t *client; + user_client_t *user; + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS); + if (client == NULL) { + up(®ister_mutex); + return -ENOMEM; /* failure code */ + } + + mode = snd_seq_file_flags(file); + if (mode & SNDRV_SEQ_LFLG_INPUT) + client->accept_input = 1; + if (mode & SNDRV_SEQ_LFLG_OUTPUT) + client->accept_output = 1; + + user = &client->data.user; + user->fifo = NULL; + user->fifo_pool_size = 0; + + if (mode & SNDRV_SEQ_LFLG_INPUT) { + user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS; + user->fifo = snd_seq_fifo_new(user->fifo_pool_size); + if (user->fifo == NULL) { + seq_free_client1(client); + kfree(client); + up(®ister_mutex); + return -ENOMEM; + } + } + + usage_alloc(&client_usage, 1); + client->type = USER_CLIENT; + up(®ister_mutex); + + c = client->number; + (user_client_t *) file->private_data = client; + + /* fill client data */ + user->file = file; + sprintf(client->name, "Client-%d", c); + + /* make others aware this new client */ + snd_seq_system_client_ev_client_start(c); + + return 0; +} + +/* delete a user client */ +static int snd_seq_release(struct inode *inode, struct file *file) +{ + client_t *client = (client_t *) file->private_data; + + if (client) { + seq_free_client(client); + if (client->data.user.fifo) + snd_seq_fifo_delete(&client->data.user.fifo); + kfree(client); + } + + return 0; +} + + +/* handle client read() */ +/* possible error values: + * -ENXIO invalid client or file open mode + * -ENOSPC FIFO overflow (the flag is cleared after this error report) + * -EINVAL no enough user-space buffer to write the whole event + * -EFAULT seg. fault during copy to user space + */ +static ssize_t snd_seq_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + client_t *client = (client_t *) file->private_data; + fifo_t *fifo; + int err; + long result = 0; + snd_seq_event_cell_t *cell; + + if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT)) + return -ENXIO; + + if (verify_area(VERIFY_WRITE, buf, count)) + return -EFAULT; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if (!client->accept_input || (fifo = client->data.user.fifo) == NULL) + return -ENXIO; + + if (atomic_read(&fifo->overflow) > 0) { + /* buffer overflow is detected */ + snd_seq_fifo_clear(fifo); + /* return error code */ + return -ENOSPC; + } + + cell = NULL; + err = 0; + snd_seq_fifo_lock(fifo); + + /* while data available in queue */ + while (count >= sizeof(snd_seq_event_t)) { + int nonblock; + + nonblock = (file->f_flags & O_NONBLOCK) || result > 0; + if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) { + break; + } + if (snd_seq_ev_is_variable(&cell->event)) { + snd_seq_event_t tmpev; + tmpev = cell->event; + tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) { + err = -EFAULT; + break; + } + count -= sizeof(snd_seq_event_t); + buf += sizeof(snd_seq_event_t); + err = snd_seq_expand_var_event(&cell->event, count, buf, 0, sizeof(snd_seq_event_t)); + if (err < 0) + break; + result += err; + count -= err; + buf += err; + } else { + copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t)); + count -= sizeof(snd_seq_event_t); + buf += sizeof(snd_seq_event_t); + } + snd_seq_cell_free(cell); + cell = NULL; /* to be sure */ + result += sizeof(snd_seq_event_t); + } + + if (err < 0) { + if (cell) + snd_seq_fifo_cell_putback(fifo, cell); + if (err == -EAGAIN && result > 0) + err = 0; + } + snd_seq_fifo_unlock(fifo); + + return (err < 0) ? err : result; +} + + +/* + * check access permission to the port + */ +static int check_port_perm(client_port_t *port, unsigned int flags) +{ + if ((port->capability & flags) != flags) + return 0; + return flags; +} + +/* + * check if the destination client is available, and return the pointer + * if filter is non-zero, client filter bitmap is tested. + */ +static client_t *get_event_dest_client(snd_seq_event_t *event, int filter) +{ + client_t *dest; + + dest = snd_seq_client_use_ptr(event->dest.client); + if (dest == NULL) + return NULL; + if (! dest->accept_input) + goto __not_avail; + if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) && + ! test_bit(event->type, dest->event_filter)) + goto __not_avail; + if (filter && !(dest->filter & filter)) + goto __not_avail; + + return dest; /* ok - accessible */ +__not_avail: + snd_seq_client_unlock(dest); + return NULL; +} + + +/* + * Return the error event. + * + * If the receiver client is a user client, the original event is + * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If + * the original event is also variable length, the external data is + * copied after the event record. + * If the receiver client is a kernel client, the original event is + * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra + * kmalloc. + */ +static int bounce_error_event(client_t *client, snd_seq_event_t *event, + int err, int atomic, int hop) +{ + snd_seq_event_t bounce_ev; + int result; + + if (client == NULL || + ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) || + ! client->accept_input) + return 0; /* ignored */ + + /* set up quoted error */ + memset(&bounce_ev, 0, sizeof(bounce_ev)); + bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; + bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; + bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + bounce_ev.dest.client = client->number; + bounce_ev.dest.port = event->source.port; + bounce_ev.data.quote.origin = event->dest; + bounce_ev.data.quote.event = event; + bounce_ev.data.quote.value = -err; /* use positive value */ + result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1); + if (result < 0) { + client->event_lost++; + return result; + } + + return result; +} + + +/* + * deliver an event to the specified destination. + * if filter is non-zero, client filter bitmap is tested. + * + * RETURN VALUE: 0 : if succeeded + * <0 : error + */ +static int snd_seq_deliver_single_event(client_t *client, + snd_seq_event_t *event, + int filter, int atomic, int hop) +{ + client_t *dest = NULL; + client_port_t *dest_port = NULL; + int result = -ENOENT; + int direct, quoted = 0; + + direct = snd_seq_ev_is_direct(event); + + dest = get_event_dest_client(event, filter); + if (dest == NULL) + goto __skip; + dest_port = snd_seq_port_use_ptr(dest, event->dest.port); + if (dest_port == NULL) + goto __skip; + + /* check permission */ + if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) { + result = -EPERM; + goto __skip; + } + + /* expand the quoted event */ + if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) { + quoted = 1; + event = event->data.quote.event; + if (event == NULL) { + snd_printd("seq: quoted event is NULL\n"); + result = 0; /* do not send bounce error */ + goto __skip; + } + } + + switch (dest->type) { + case USER_CLIENT: + if (dest->data.user.fifo) + result = snd_seq_fifo_event_in(dest->data.user.fifo, event); + break; + + case KERNEL_CLIENT: + if (dest_port->event_input == NULL) + break; + result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop); + break; + default: + break; + } + + __skip: + if (dest_port) + snd_seq_port_unlock(dest_port); + if (dest) + snd_seq_client_unlock(dest); + + if (result < 0 && !direct) { + if (quoted) { + /* return directly to the original source */ + dest = snd_seq_client_use_ptr(event->source.client); + result = bounce_error_event(dest, event, result, atomic, hop); + snd_seq_client_unlock(dest); + } else { + result = bounce_error_event(client, event, result, atomic, hop); + } + } + return result; +} + + +static void snd_seq_subs_update_event_header(subscribers_t *subs, snd_seq_event_t *event) +{ + if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) { + /* convert time according to flag with subscription */ + queue_t *q; + q = queueptr(subs->info.queue); + if (q) { + event->queue = subs->info.queue; + event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK; + if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) { + event->time.time = snd_seq_timer_get_cur_time(q->timer); + event->flags |= SNDRV_SEQ_TIME_STAMP_REAL; + } else { + event->time.tick = snd_seq_timer_get_cur_tick(q->timer); + event->flags |= SNDRV_SEQ_TIME_STAMP_TICK; + } + queuefree(q); + } + } +} + +/* + * send the event to all subscribers: + */ +static int deliver_to_subscribers(client_t *client, + snd_seq_event_t *event, + int atomic, int hop) +{ + subscribers_t *subs; + int err = 0, num_ev = 0; + snd_seq_event_t event_saved; + client_port_t *src_port; + struct list_head *p; + port_subs_info_t *grp; + + src_port = snd_seq_port_use_ptr(client, event->source.port); + if (src_port == NULL) + return -EINVAL; /* invalid source port */ + /* save original event record */ + event_saved = *event; + grp = &src_port->c_src; + + /* lock list */ + if (atomic) + read_lock(&grp->list_lock); + else + down_read(&grp->list_mutex); + list_for_each(p, &grp->list_head) { + subs = list_entry(p, subscribers_t, src_list); + event->dest = subs->info.dest; + snd_seq_subs_update_event_header(subs, event); + err = snd_seq_deliver_single_event(client, event, + 0, atomic, hop); + if (err < 0) + break; + num_ev++; + /* restore original event record */ + *event = event_saved; + } + if (atomic) + read_unlock(&grp->list_lock); + else + up_read(&grp->list_mutex); + *event = event_saved; /* restore */ + snd_seq_port_unlock(src_port); + return (err < 0) ? err : num_ev; +} + + +#ifdef SUPPORT_BROADCAST +/* + * broadcast to all ports: + */ +static int port_broadcast_event(client_t *client, + snd_seq_event_t *event, + int atomic, int hop) +{ + int num_ev = 0, err = 0; + client_t *dest_client; + struct list_head *p; + + dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST); + if (dest_client == NULL) + return 0; /* no matching destination */ + + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + client_port_t *port = list_entry(p, client_port_t, list); + event->dest.port = port->addr.port; + /* pass NULL as source client to avoid error bounce */ + err = snd_seq_deliver_single_event(NULL, event, + SNDRV_SEQ_FILTER_BROADCAST, + atomic, hop); + if (err < 0) + break; + num_ev++; + } + read_unlock(&client->ports_lock); + snd_seq_client_unlock(dest_client); + event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ + return (err < 0) ? err : num_ev; +} + +/* + * send the event to all clients: + * if destination port is also ADDRESS_BROADCAST, deliver to all ports. + */ +static int broadcast_event(client_t *client, + snd_seq_event_t *event, int atomic, int hop) +{ + int err = 0, num_ev = 0; + int dest; + snd_seq_addr_t addr; + + addr = event->dest; /* save */ + + for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) { + /* don't send to itself */ + if (dest == client->number) + continue; + event->dest.client = dest; + event->dest.port = addr.port; + if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST) + err = port_broadcast_event(client, event, atomic, hop); + else + /* pass NULL as source client to avoid error bounce */ + err = snd_seq_deliver_single_event(NULL, event, + SNDRV_SEQ_FILTER_BROADCAST, + atomic, hop); + if (err < 0) + break; + num_ev += err; + } + event->dest = addr; /* restore */ + return (err < 0) ? err : num_ev; +} + + +/* multicast - not supported yet */ +static int multicast_event(client_t *client, snd_seq_event_t *event, + int atomic, int hop) +{ + snd_printd("seq: multicast not supported yet.\n"); + return 0; /* ignored */ +} +#endif /* SUPPORT_BROADCAST */ + + +/* deliver an event to the destination port(s). + * if the event is to subscribers or broadcast, the event is dispatched + * to multiple targets. + * + * RETURN VALUE: n > 0 : the number of delivered events. + * n == 0 : the event was not passed to any client. + * n < 0 : error - event was not processed. + */ +int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, + int atomic, int hop) +{ + int result; + + hop++; + if (hop >= SNDRV_SEQ_MAX_HOPS) { + snd_printd("too long delivery path (%d:%d->%d:%d)\n", + event->source.client, event->source.port, + event->dest.client, event->dest.port); + return -EMLINK; + } + + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS || + event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) + result = deliver_to_subscribers(client, event, atomic, hop); +#ifdef SUPPORT_BROADCAST + else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST || + event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST) + result = broadcast_event(client, event, atomic, hop); + else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS) + result = multicast_event(client, event, atomic, hop); + else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST) + result = port_broadcast_event(client, event, atomic, hop); +#endif + else + result = snd_seq_deliver_single_event(client, event, 0, atomic, hop); + + return result; +} + +/* + * dispatch an event cell: + * This function is called only from queue check routines in timer + * interrupts or after enqueued. + * The event cell shall be released or re-queued in this function. + * + * RETURN VALUE: n > 0 : the number of delivered events. + * n == 0 : the event was not passed to any client. + * n < 0 : error - event was not processed. + */ +int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop) +{ + client_t *client; + int result; + + snd_assert(cell != NULL, return -EINVAL); + + client = snd_seq_client_use_ptr(cell->event.source.client); + if (client == NULL) { + snd_seq_cell_free(cell); /* release this cell */ + return -EINVAL; + } + + if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) { + /* NOTE event: + * the event cell is re-used as a NOTE-OFF event and + * enqueued again. + */ + snd_seq_event_t tmpev, *ev; + + /* reserve this event to enqueue note-off later */ + tmpev = cell->event; + tmpev.type = SNDRV_SEQ_EVENT_NOTEON; + result = snd_seq_deliver_event(client, &tmpev, atomic, hop); + + /* + * This was originally a note event. We now re-use the + * cell for the note-off event. + */ + + ev = &cell->event; + ev->type = SNDRV_SEQ_EVENT_NOTEOFF; + ev->flags |= SNDRV_SEQ_PRIORITY_HIGH; + + /* add the duration time */ + switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + ev->time.tick += ev->data.note.duration; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + /* unit for duration is ms */ + ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000); + ev->time.time.tv_sec += ev->data.note.duration / 1000 + + ev->time.time.tv_nsec / 1000000000; + ev->time.time.tv_nsec %= 1000000000; + break; + } + ev->data.note.velocity = ev->data.note.off_velocity; + + /* Now queue this cell as the note off event */ + if (snd_seq_enqueue_event(cell, atomic, hop) < 0) + snd_seq_cell_free(cell); /* release this cell */ + + } else { + /* Normal events: + * event cell is freed after processing the event + */ + + result = snd_seq_deliver_event(client, &cell->event, atomic, hop); + snd_seq_cell_free(cell); + } + + snd_seq_client_unlock(client); + return result; +} + + +/* Allocate a cell from client pool and enqueue it to queue: + * if pool is empty and blocking is TRUE, sleep until a new cell is + * available. + */ +static int snd_seq_client_enqueue_event(client_t *client, + snd_seq_event_t *event, + struct file *file, int blocking, + int atomic, int hop) +{ + snd_seq_event_cell_t *cell; + int err; + + /* special queue values - force direct passing */ + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { + event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + event->queue = SNDRV_SEQ_QUEUE_DIRECT; + } else +#ifdef SUPPORT_BROADCAST + if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) { + event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST; + event->queue = SNDRV_SEQ_QUEUE_DIRECT; + } +#endif + if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { + /* check presence of source port */ + client_port_t *src_port = snd_seq_port_use_ptr(client, event->source.port); + if (src_port == NULL) + return -EINVAL; + snd_seq_port_unlock(src_port); + } + + /* direct event processing without enqueued */ + if (snd_seq_ev_is_direct(event)) { + if (event->type == SNDRV_SEQ_EVENT_NOTE) + return -EINVAL; /* this event must be enqueued! */ + return snd_seq_deliver_event(client, event, atomic, hop); + } + + /* Not direct, normal queuing */ + if (snd_seq_queue_is_used(event->queue, client->number) <= 0) + return -EINVAL; /* invalid queue */ + if (! snd_seq_write_pool_allocated(client)) + return -ENXIO; /* queue is not allocated */ + + /* allocate an event cell */ + err = snd_seq_event_dup(client->pool, event, &cell, !blocking && !atomic, file); + if (err < 0) + return err; + + /* we got a cell. enqueue it. */ + if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) { + snd_seq_cell_free(cell); + return err; + } + + return 0; +} + + +/* + * check validity of event type and data length. + * return non-zero if invalid. + */ +static int check_event_type_and_length(snd_seq_event_t *ev) +{ + switch (snd_seq_ev_length_type(ev)) { + case SNDRV_SEQ_EVENT_LENGTH_FIXED: + if (snd_seq_ev_is_variable_type(ev)) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: + if (! snd_seq_ev_is_variable_type(ev) || + (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARUSR: + if (! snd_seq_ev_is_instr_type(ev) || + ! snd_seq_ev_is_direct(ev)) + return -EINVAL; + break; + } + return 0; +} + + +/* handle write() */ +/* possible error values: + * -ENXIO invalid client or file open mode + * -ENOMEM malloc failed + * -EFAULT seg. fault during copy from user space + * -EINVAL invalid event + * -EAGAIN no space in output pool + * -EINTR interrupts while sleep + * -EMLINK too many hops + * others depends on return value from driver callback + */ +static ssize_t snd_seq_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + client_t *client = (client_t *) file->private_data; + int written = 0, len; + int err = -EINVAL; + snd_seq_event_t event; + + if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) + return -ENXIO; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if (!client->accept_output || client->pool == NULL) + return -ENXIO; + + /* allocate the pool now if the pool is not allocated yet */ + if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { + if (snd_seq_pool_init(client->pool) < 0) + return -ENOMEM; + } + + /* only process whole events */ + while (count >= sizeof(snd_seq_event_t)) { + /* Read in the event header from the user */ + len = sizeof(event); + if (copy_from_user(&event, buf, len)) { + err = -EFAULT; + break; + } + event.source.client = client->number; /* fill in client number */ + /* Check for extension data length */ + if (check_event_type_and_length(&event)) { + err = -EINVAL; + break; + } + + /* check for special events */ + if (event.type == SNDRV_SEQ_EVENT_NONE) + goto __skip_event; + else if (snd_seq_ev_is_reserved(&event)) { + err = -EINVAL; + break; + } + + if (snd_seq_ev_is_variable(&event)) { + int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK; + if ((size_t)(extlen + len) > count) { + /* back out, will get an error this time or next */ + err = -EINVAL; + break; + } + /* set user space pointer */ + event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR; + event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t); + len += extlen; /* increment data length */ + } else { +#if defined(CONFIG_SND_BIT32_EMUL) || defined(CONFIG_SND_BIT32_EMUL_MODULE) + if (client->convert32 && snd_seq_ev_is_varusr(&event)) { + void *ptr = (void*)A(event.data.raw32.d[1]); + event.data.ext.ptr = ptr; + } +#endif + } + + /* ok, enqueue it */ + err = snd_seq_client_enqueue_event(client, &event, file, + !(file->f_flags & O_NONBLOCK), + 0, 0); + if (err < 0) + break; + + __skip_event: + /* Update pointers and counts */ + count -= len; + buf += len; + written += len; + } + + return written ? written : err; +} + + +/* + * handle polling + */ +static unsigned int snd_seq_poll(struct file *file, poll_table * wait) +{ + client_t *client = (client_t *) file->private_data; + unsigned int mask = 0; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) && + client->data.user.fifo) { + + /* check if data is available in the outqueue */ + if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait)) + mask |= POLLIN | POLLRDNORM; + } + + if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) { + + /* check if data is available in the pool */ + if (!snd_seq_write_pool_allocated(client) || + snd_seq_pool_poll_wait(client->pool, file, wait)) + mask |= POLLOUT | POLLWRNORM; + } + + return mask; +} + + +/*-----------------------------------------------------*/ + + +/* SYSTEM_INFO ioctl() */ +static int snd_seq_ioctl_system_info(client_t *client, unsigned long arg) +{ + snd_seq_system_info_t info; + + memset(&info, 0, sizeof(info)); + /* fill the info fields */ + info.queues = SNDRV_SEQ_MAX_QUEUES; + info.clients = SNDRV_SEQ_MAX_CLIENTS; + info.ports = 256; /* fixed limit */ + info.channels = 256; /* fixed limit */ + info.cur_clients = client_usage.cur; + info.cur_queues = snd_seq_queue_get_cur_queues(); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* RUNNING_MODE ioctl() */ +static int snd_seq_ioctl_running_mode(client_t *client, unsigned long arg) +{ + struct sndrv_seq_running_info info; + client_t *cptr; + int err = 0; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* requested client number */ + cptr = snd_seq_client_use_ptr(info.client); + if (cptr == NULL) + return -ENOENT; /* don't change !!! */ + +#ifdef SNDRV_BIG_ENDIAN + if (! info.big_endian) { + err = -EINVAL; + goto __err; + } +#else + if (info.big_endian) { + err = -EINVAL; + goto __err; + } + +#endif + if (info.cpu_mode > sizeof(long)) { + err = -EINVAL; + goto __err; + } + cptr->convert32 = (info.cpu_mode < sizeof(long)); + __err: + snd_seq_client_unlock(cptr); + return err; +} + +/* CLIENT_INFO ioctl() */ +static void get_client_info(client_t *cptr, snd_seq_client_info_t *info) +{ + info->client = cptr->number; + + /* fill the info fields */ + info->type = cptr->type; + strcpy(info->name, cptr->name); + info->filter = cptr->filter; + info->event_lost = cptr->event_lost; + memcpy(info->event_filter, cptr->event_filter, 32); + info->num_ports = cptr->num_ports; + memset(info->reserved, 0, sizeof(info->reserved)); +} + +static int snd_seq_ioctl_get_client_info(client_t * client, unsigned long arg) +{ + client_t *cptr; + snd_seq_client_info_t client_info; + + if (copy_from_user(&client_info, (void*)arg, sizeof(client_info))) + return -EFAULT; + + /* requested client number */ + cptr = snd_seq_client_use_ptr(client_info.client); + if (cptr == NULL) + return -ENOENT; /* don't change !!! */ + + get_client_info(cptr, &client_info); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &client_info, sizeof(client_info))) + return -EFAULT; + return 0; +} + + +/* CLIENT_INFO ioctl() */ +static int snd_seq_ioctl_set_client_info(client_t * client, unsigned long arg) +{ + snd_seq_client_info_t client_info; + + if (copy_from_user(&client_info, (void*)arg, sizeof(client_info))) + return -EFAULT; + + /* it is not allowed to set the info fields for an another client */ + if (client->number != client_info.client) + return -EPERM; + /* also client type must be set now */ + if (client->type != client_info.type) + return -EINVAL; + + /* fill the info fields */ + if (client_info.name[0]) { + strncpy(client->name, client_info.name, sizeof(client->name)-1); + client->name[sizeof(client->name)-1] = '\0'; + } + client->filter = client_info.filter; + client->event_lost = client_info.event_lost; + memcpy(client->event_filter, client_info.event_filter, 32); + + return 0; +} + + +/* + * CREATE PORT ioctl() + */ +static int snd_seq_ioctl_create_port(client_t * client, unsigned long arg) +{ + client_port_t *port; + snd_seq_port_info_t info; + snd_seq_port_callback_t *callback; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* it is not allowed to create the port for an another client */ + if (info.addr.client != client->number) + return -EPERM; + + port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1); + if (port == NULL) + return -ENOMEM; + + if (client->type == USER_CLIENT && info.kernel) { + snd_seq_delete_port(client, port->addr.port); + return -EINVAL; + } + if (client->type == KERNEL_CLIENT) { + if ((callback = info.kernel) != NULL) { + if (callback->owner) + port->owner = callback->owner; + port->private_data = callback->private_data; + port->private_free = callback->private_free; + port->callback_all = callback->callback_all; + port->event_input = callback->event_input; + port->c_src.open = callback->subscribe; + port->c_src.close = callback->unsubscribe; + port->c_dest.open = callback->use; + port->c_dest.close = callback->unuse; + } + } + + info.addr = port->addr; + + snd_seq_set_port_info(port, &info); + snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* + * DELETE PORT ioctl() + */ +static int snd_seq_ioctl_delete_port(client_t * client, unsigned long arg) +{ + snd_seq_port_info_t info; + int err; + + /* set passed parameters */ + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* it is not allowed to remove the port for an another client */ + if (info.addr.client != client->number) + return -EPERM; + + err = snd_seq_delete_port(client, info.addr.port); + if (err >= 0) + snd_seq_system_client_ev_port_exit(client->number, info.addr.port); + return err; +} + + +/* + * GET_PORT_INFO ioctl() (on any client) + */ +static int snd_seq_ioctl_get_port_info(client_t *client, unsigned long arg) +{ + client_t *cptr; + client_port_t *port; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + cptr = snd_seq_client_use_ptr(info.addr.client); + if (cptr == NULL) + return -ENXIO; + + port = snd_seq_port_use_ptr(cptr, info.addr.port); + if (port == NULL) { + snd_seq_client_unlock(cptr); + return -ENOENT; /* don't change */ + } + + /* get port info */ + snd_seq_get_port_info(port, &info); + snd_seq_port_unlock(port); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* + * SET_PORT_INFO ioctl() (only ports on this/own client) + */ +static int snd_seq_ioctl_set_port_info(client_t * client, unsigned long arg) +{ + client_port_t *port; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.addr.client != client->number) /* only set our own ports ! */ + return -EPERM; + port = snd_seq_port_use_ptr(client, info.addr.port); + if (port) { + snd_seq_set_port_info(port, &info); + snd_seq_port_unlock(port); + } + return 0; +} + + +/* + * port subscription (connection) + */ +#define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) +#define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) + +static int check_subscription_permission(client_t *client, client_port_t *sport, + client_port_t *dport, + snd_seq_port_subscribe_t *subs) +{ + if (client->number != subs->sender.client && + client->number != subs->dest.client) { + /* connection by third client - check export permission */ + if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) + return -EPERM; + if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) + return -EPERM; + } + + /* check read permission */ + /* if sender or receiver is the subscribing client itself, + * no permission check is necessary + */ + if (client->number != subs->sender.client) { + if (! check_port_perm(sport, PERM_RD)) + return -EPERM; + } + /* check write permission */ + if (client->number != subs->dest.client) { + if (! check_port_perm(dport, PERM_WR)) + return -EPERM; + } + return 0; +} + +/* + * send an subscription notify event to user client: + * client must be user client. + */ +int snd_seq_client_notify_subscription(int client, int port, + snd_seq_port_subscribe_t *info, int evtype) +{ + snd_seq_event_t event; + + memset(&event, 0, sizeof(event)); + event.type = evtype; + event.data.connect.dest = info->dest; + event.data.connect.sender = info->sender; + + return snd_seq_system_notify(client, port, &event); /* non-atomic */ +} + + +/* + * add to port's subscription list IOCTL interface + */ +static int snd_seq_ioctl_subscribe_port(client_t * client, unsigned long arg) +{ + int result = -EINVAL; + client_t *receiver = NULL, *sender = NULL; + client_port_t *sport = NULL, *dport = NULL; + snd_seq_port_subscribe_t subs; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + goto __end; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + goto __end; + + result = check_subscription_permission(client, sport, dport, &subs); + if (result < 0) + goto __end; + + /* connect them */ + result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs); + if (! result) /* broadcast announce */ + snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, + &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + __end: + if (sport) + snd_seq_port_unlock(sport); + if (dport) + snd_seq_port_unlock(dport); + if (sender) + snd_seq_client_unlock(sender); + if (receiver) + snd_seq_client_unlock(receiver); + return result; +} + + +/* + * remove from port's subscription list + */ +static int snd_seq_ioctl_unsubscribe_port(client_t * client, unsigned long arg) +{ + int result = -ENXIO; + client_t *receiver = NULL, *sender = NULL; + client_port_t *sport = NULL, *dport = NULL; + snd_seq_port_subscribe_t subs; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + goto __end; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + goto __end; + + result = check_subscription_permission(client, sport, dport, &subs); + if (result < 0) + goto __end; + + result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs); + if (! result) /* broadcast announce */ + snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, + &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + __end: + if (sport) + snd_seq_port_unlock(sport); + if (dport) + snd_seq_port_unlock(dport); + if (sender) + snd_seq_client_unlock(sender); + if (receiver) + snd_seq_client_unlock(receiver); + return result; +} + + +/* CREATE_QUEUE ioctl() */ +static int snd_seq_ioctl_create_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + int result; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + result = snd_seq_queue_alloc(client->number, info.locked, info.flags); + if (result < 0) + return result; + + q = queueptr(result); + if (q == NULL) + return -EINVAL; + + info.queue = q->queue; + info.locked = q->locked; + info.owner = q->owner; + + /* set queue name */ + if (! info.name[0]) + sprintf(info.name, "Queue-%d", q->queue); + strncpy(q->name, info.name, sizeof(q->name)-1); + q->name[sizeof(q->name)-1] = 0; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* DELETE_QUEUE ioctl() */ +static int snd_seq_ioctl_delete_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + return snd_seq_queue_delete(client->number, info.queue); +} + +/* GET_QUEUE_INFO ioctl() */ +static int snd_seq_ioctl_get_queue_info(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + q = queueptr(info.queue); + if (q == NULL) + return -EINVAL; + + memset(&info, 0, sizeof(info)); + info.queue = q->queue; + info.owner = q->owner; + info.locked = q->locked; + strncpy(info.name, q->name, sizeof(info.name) - 1); + info.name[sizeof(info.name)-1] = 0; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* SET_QUEUE_INFO ioctl() */ +static int snd_seq_ioctl_set_queue_info(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.owner != client->number) + return -EINVAL; + + /* change owner/locked permission */ + if (snd_seq_queue_check_access(info.queue, client->number)) { + if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0) + return -EPERM; + if (info.locked) + snd_seq_queue_use(info.queue, client->number, 1); + } else { + return -EPERM; + } + + q = queueptr(info.queue); + if (! q) + return -EINVAL; + if (q->owner != client->number) { + queuefree(q); + return -EPERM; + } + strncpy(q->name, info.name, sizeof(q->name) - 1); + q->name[sizeof(q->name)-1] = 0; + queuefree(q); + + return 0; +} + +/* GET_NAMED_QUEUE ioctl() */ +static int snd_seq_ioctl_get_named_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + q = snd_seq_queue_find_name(info.name); + if (q == NULL) + return -EINVAL; + info.queue = q->queue; + info.owner = q->owner; + info.locked = q->locked; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* GET_QUEUE_STATUS ioctl() */ +static int snd_seq_ioctl_get_queue_status(client_t * client, unsigned long arg) +{ + snd_seq_queue_status_t status; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&status, (void*)arg, sizeof(status))) + return -EFAULT; + + queue = queueptr(status.queue); + if (queue == NULL) + return -EINVAL; + memset(&status, 0, sizeof(status)); + status.queue = queue->queue; + + tmr = queue->timer; + status.events = queue->tickq->cells + queue->timeq->cells; + + status.time = snd_seq_timer_get_cur_time(tmr); + status.tick = snd_seq_timer_get_cur_tick(tmr); + + status.running = tmr->running; + + status.flags = queue->flags; + queuefree(queue); + + if (copy_to_user((void*)arg, &status, sizeof(status))) + return -EFAULT; + return 0; +} + + +/* GET_QUEUE_TEMPO ioctl() */ +static int snd_seq_ioctl_get_queue_tempo(client_t * client, unsigned long arg) +{ + snd_seq_queue_tempo_t tempo; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&tempo, (void*)arg, sizeof(tempo))) + return -EFAULT; + + queue = queueptr(tempo.queue); + if (queue == NULL) + return -EINVAL; + memset(&tempo, 0, sizeof(tempo)); + tempo.queue = queue->queue; + + tmr = queue->timer; + + tempo.tempo = tmr->tempo; + tempo.ppq = tmr->ppq; + tempo.skew_value = tmr->skew; + tempo.skew_base = tmr->skew_base; + queuefree(queue); + + if (copy_to_user((void*)arg, &tempo, sizeof(tempo))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_TEMPO ioctl() */ +static int snd_seq_ioctl_set_queue_tempo(client_t * client, unsigned long arg) +{ + int result; + snd_seq_queue_tempo_t tempo; + + if (copy_from_user(&tempo, (void*)arg, sizeof(tempo))) + return -EFAULT; + + if (snd_seq_queue_check_access(tempo.queue, client->number)) { + result = snd_seq_queue_timer_set_tempo(tempo.queue, client->number, &tempo); + if (result < 0) + return result; + } else { + return -EPERM; + } + + return 0; +} + + +/* GET_QUEUE_TIMER ioctl() */ +static int snd_seq_ioctl_get_queue_timer(client_t * client, unsigned long arg) +{ + snd_seq_queue_timer_t timer; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&timer, (void*)arg, sizeof(timer))) + return -EFAULT; + + queue = queueptr(timer.queue); + if (queue == NULL) + return -EINVAL; + + if (down_interruptible(&queue->timer_mutex)) { + queuefree(queue); + return -ERESTARTSYS; + } + tmr = queue->timer; + memset(&timer, 0, sizeof(timer)); + timer.queue = queue->queue; + + timer.type = tmr->type; + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { + timer.u.alsa.id = tmr->alsa_id; + timer.u.alsa.resolution = tmr->preferred_resolution; + } + up(&queue->timer_mutex); + queuefree(queue); + + if (copy_to_user((void*)arg, &timer, sizeof(timer))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_TIMER ioctl() */ +static int snd_seq_ioctl_set_queue_timer(client_t * client, unsigned long arg) +{ + int result = 0; + snd_seq_queue_timer_t timer; + + if (copy_from_user(&timer, (void*)arg, sizeof(timer))) + return -EFAULT; + + if (timer.type != SNDRV_SEQ_TIMER_ALSA) + return -EINVAL; + + if (snd_seq_queue_check_access(timer.queue, client->number)) { + queue_t *q; + seq_timer_t *tmr; + + q = queueptr(timer.queue); + if (q == NULL) + return -ENXIO; + if (down_interruptible(&q->timer_mutex)) { + queuefree(q); + return -ERESTARTSYS; + } + tmr = q->timer; + snd_seq_queue_timer_close(timer.queue); + tmr->type = timer.type; + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { + tmr->alsa_id = timer.u.alsa.id; + tmr->preferred_resolution = timer.u.alsa.resolution; + } + result = snd_seq_queue_timer_open(timer.queue); + up(&q->timer_mutex); + queuefree(q); + } else { + return -EPERM; + } + + return result; +} + + +/* GET_QUEUE_CLIENT ioctl() */ +static int snd_seq_ioctl_get_queue_client(client_t * client, unsigned long arg) +{ + snd_seq_queue_client_t info; + int used; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + used = snd_seq_queue_is_used(info.queue, client->number); + if (used < 0) + return -EINVAL; + info.used = used; + info.client = client->number; + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_CLIENT ioctl() */ +static int snd_seq_ioctl_set_queue_client(client_t * client, unsigned long arg) +{ + int err; + snd_seq_queue_client_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.used >= 0) { + err = snd_seq_queue_use(info.queue, client->number, info.used); + if (err < 0) + return err; + } + + return snd_seq_ioctl_get_queue_client(client, arg); +} + + +/* GET_CLIENT_POOL ioctl() */ +static int snd_seq_ioctl_get_client_pool(client_t * client, unsigned long arg) +{ + snd_seq_client_pool_t info; + client_t *cptr; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + cptr = snd_seq_client_use_ptr(info.client); + if (cptr == NULL) + return -ENOENT; + memset(&info, 0, sizeof(info)); + info.output_pool = cptr->pool->size; + info.output_room = cptr->pool->room; + info.output_free = info.output_pool; + if (cptr->pool) + info.output_free = snd_seq_unused_cells(cptr->pool); + if (cptr->type == USER_CLIENT) { + info.input_pool = cptr->data.user.fifo_pool_size; + info.input_free = info.input_pool; + if (cptr->data.user.fifo) + info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool); + } else { + info.input_pool = 0; + info.input_free = 0; + } + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* SET_CLIENT_POOL ioctl() */ +static int snd_seq_ioctl_set_client_pool(client_t * client, unsigned long arg) +{ + snd_seq_client_pool_t info; + int rc; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (client->number != info.client) + return -EINVAL; /* can't change other clients */ + + if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS && + (! snd_seq_write_pool_allocated(client) || + info.output_pool != client->pool->size)) { + if (snd_seq_write_pool_allocated(client)) { + /* remove all existing cells */ + snd_seq_queue_client_leave_cells(client->number); + snd_seq_pool_done(client->pool); + } + client->pool->size = info.output_pool; + rc = snd_seq_pool_init(client->pool); + if (rc < 0) + return rc; + } + if (client->type == USER_CLIENT && client->data.user.fifo != NULL && + info.input_pool >= 1 && + info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS && + info.input_pool != client->data.user.fifo_pool_size) { + /* change pool size */ + rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool); + if (rc < 0) + return rc; + client->data.user.fifo_pool_size = info.input_pool; + } + if (info.output_room >= 1 && + info.output_room <= client->pool->size) { + client->pool->room = info.output_room; + } + + return snd_seq_ioctl_get_client_pool(client, arg); +} + + +/* REMOVE_EVENTS ioctl() */ +static int snd_seq_ioctl_remove_events(client_t * client, unsigned long arg) +{ + snd_seq_remove_events_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* + * Input mostly not implemented XXX. + */ + if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) { + /* + * No restrictions so for a user client we can clear + * the whole fifo + */ + if (client->type == USER_CLIENT) + snd_seq_fifo_clear(client->data.user.fifo); + } + + if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT) + snd_seq_queue_remove_cells(client->number, &info); + + return 0; +} + + +/* + * get subscription info + */ +static int snd_seq_ioctl_get_subscription(client_t *client, unsigned long arg) +{ + int result; + client_t *sender = NULL; + client_port_t *sport = NULL; + snd_seq_port_subscribe_t subs; + subscribers_t *p; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + result = -EINVAL; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest); + if (p) { + result = 0; + subs = p->info; + } else + result = -ENOENT; + + __end: + if (sport) + snd_seq_port_unlock(sport); + if (sender) + snd_seq_client_unlock(sender); + if (result >= 0) { + if (copy_to_user((void*)arg, &subs, sizeof(subs))) + return -EFAULT; + } + return result; +} + + +/* + * get subscription info - check only its presence + */ +static int snd_seq_ioctl_query_subs(client_t *client, unsigned long arg) +{ + int result = -ENXIO; + client_t *cptr = NULL; + client_port_t *port = NULL; + snd_seq_query_subs_t subs; + port_subs_info_t *group; + struct list_head *p; + int i; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL) + goto __end; + if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL) + goto __end; + + switch (subs.type) { + case SNDRV_SEQ_QUERY_SUBS_READ: + group = &port->c_src; + break; + case SNDRV_SEQ_QUERY_SUBS_WRITE: + group = &port->c_dest; + break; + default: + goto __end; + } + + down_read(&group->list_mutex); + /* search for the subscriber */ + subs.num_subs = group->count; + i = 0; + result = -ENOENT; + list_for_each(p, &group->list_head) { + if (i++ == subs.index) { + /* found! */ + subscribers_t *s; + if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) { + s = list_entry(p, subscribers_t, src_list); + subs.addr = s->info.dest; + } else { + s = list_entry(p, subscribers_t, dest_list); + subs.addr = s->info.sender; + } + subs.flags = s->info.flags; + subs.queue = s->info.queue; + result = 0; + break; + } + } + up_read(&group->list_mutex); + + __end: + if (port) + snd_seq_port_unlock(port); + if (cptr) + snd_seq_client_unlock(cptr); + if (result >= 0) { + if (copy_to_user((void*)arg, &subs, sizeof(subs))) + return -EFAULT; + } + return result; +} + + +/* + * query next client + */ +static int snd_seq_ioctl_query_next_client(client_t *client, unsigned long arg) +{ + client_t *cptr = NULL; + snd_seq_client_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* search for next client */ + info.client++; + if (info.client < 0) + info.client = 0; + for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) { + cptr = snd_seq_client_use_ptr(info.client); + if (cptr) + break; /* found */ + } + if (cptr == NULL) + return -ENOENT; + + get_client_info(cptr, &info); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* + * query next port + */ +static int snd_seq_ioctl_query_next_port(client_t *client, unsigned long arg) +{ + client_t *cptr; + client_port_t *port = NULL; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + cptr = snd_seq_client_use_ptr(info.addr.client); + if (cptr == NULL) + return -ENXIO; + + /* search for next port */ + info.addr.port++; + port = snd_seq_port_query_nearest(cptr, &info); + if (port == NULL) { + snd_seq_client_unlock(cptr); + return -ENOENT; + } + + /* get port info */ + info.addr = port->addr; + snd_seq_get_port_info(port, &info); + snd_seq_port_unlock(port); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* -------------------------------------------------------- */ + +static struct seq_ioctl_table { + unsigned int cmd; + int (*func)(client_t *client, unsigned long arg); +} ioctl_tables[] = { + { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info }, + { SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode }, + { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info }, + { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info }, + { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port }, + { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port }, + { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info }, + { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info }, + { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port }, + { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port }, + { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue }, + { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info }, + { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client }, + { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool }, + { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool }, + { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription }, + { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client }, + { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port }, + { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events }, + { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs }, + { 0, NULL }, +}; + +static int snd_seq_do_ioctl(client_t *client, unsigned int cmd, unsigned long arg) +{ + struct seq_ioctl_table *p; + + switch (cmd) { + case SNDRV_SEQ_IOCTL_PVERSION: + /* return sequencer version number */ + return put_user(SNDRV_SEQ_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_SEQ_IOCTL_CLIENT_ID: + /* return the id of this client */ + return put_user(client->number, (int *)arg) ? -EFAULT : 0; + } + + if (! arg) + return -EFAULT; + for (p = ioctl_tables; p->cmd; p++) { + if (p->cmd == cmd) + return p->func(client, arg); + } + snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n", + cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -ENOTTY; +} + + +static int snd_seq_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + client_t *client = (client_t *) file->private_data; + + snd_assert(client != NULL, return -ENXIO); + + return snd_seq_do_ioctl(client, cmd, arg); +} + + +/* -------------------------------------------------------- */ + + +/* exported to kernel modules */ +int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t * callback) +{ + client_t *client; + + snd_assert(! in_interrupt(), return -EBUSY); + + if (callback == NULL) + return -EINVAL; + if (card && client_index > 7) + return -EINVAL; + if (card == NULL && client_index > 63) + return -EINVAL; + if (card) + client_index += 64 + (card->number << 3); + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + /* empty write queue as default */ + client = seq_create_client1(client_index, 0); + if (client == NULL) { + up(®ister_mutex); + return -EBUSY; /* failure code */ + } + usage_alloc(&client_usage, 1); + + client->accept_input = callback->allow_output; + client->accept_output = callback->allow_input; + + /* fill client data */ + client->data.kernel.card = card; + client->data.kernel.private_data = callback->private_data; + sprintf(client->name, "Client-%d", client->number); + + client->type = KERNEL_CLIENT; + up(®ister_mutex); + + /* make others aware this new client */ + snd_seq_system_client_ev_client_start(client->number); + + /* return client number to caller */ + return client->number; +} + +/* exported to kernel modules */ +int snd_seq_delete_kernel_client(int client) +{ + client_t *ptr; + + snd_assert(! in_interrupt(), return -EBUSY); + + ptr = clientptr(client); + if (ptr == NULL) + return -EINVAL; + + seq_free_client(ptr); + kfree(ptr); + return 0; +} + + +/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue + * and snd_seq_kernel_client_enqueue_blocking + */ +static int kernel_client_enqueue(int client, snd_seq_event_t *ev, + struct file *file, int blocking, + int atomic, int hop) +{ + client_t *cptr; + int result; + + snd_assert(ev != NULL, return -EINVAL); + + if (ev->type == SNDRV_SEQ_EVENT_NONE) + return 0; /* ignore this */ + if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR || + ev->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) + return -EINVAL; /* quoted events can't be enqueued */ + + /* fill in client number */ + ev->source.client = client; + + if (check_event_type_and_length(ev)) + return -EINVAL; + + cptr = snd_seq_client_use_ptr(client); + if (cptr == NULL) + return -EINVAL; + + if (! cptr->accept_output) + result = -EPERM; + else /* send it */ + result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop); + + snd_seq_client_unlock(cptr); + return result; +} + +/* + * exported, called by kernel clients to enqueue events (w/o blocking) + * + * RETURN VALUE: zero if succeed, negative if error + */ +int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t * ev, + int atomic, int hop) +{ + return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop); +} + +/* + * exported, called by kernel clients to enqueue events (with blocking) + * + * RETURN VALUE: zero if succeed, negative if error + */ +int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, + struct file *file, + int atomic, int hop) +{ + return kernel_client_enqueue(client, ev, file, 1, atomic, hop); +} + + +/* + * exported, called by kernel clients to dispatch events directly to other + * clients, bypassing the queues. Event time-stamp will be updated. + * + * RETURN VALUE: negative = delivery failed, + * zero, or positive: the number of delivered events + */ +int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t * ev, + int atomic, int hop) +{ + client_t *cptr; + int result; + + snd_assert(ev != NULL, return -EINVAL); + + /* fill in client number */ + ev->queue = SNDRV_SEQ_QUEUE_DIRECT; + ev->source.client = client; + + if (check_event_type_and_length(ev)) + return -EINVAL; + + cptr = snd_seq_client_use_ptr(client); + if (cptr == NULL) + return -EINVAL; + + if (!cptr->accept_output) + result = -EPERM; + else + result = snd_seq_deliver_event(cptr, ev, atomic, hop); + + snd_seq_client_unlock(cptr); + return result; +} + + +/* + * exported, called by kernel clients to perform same functions as with + * userland ioctl() + */ +int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) +{ + client_t *client; + mm_segment_t fs; + int result; + + client = clientptr(clientid); + if (client == NULL) + return -ENXIO; + fs = snd_enter_user(); + result = snd_seq_do_ioctl(client, cmd, (unsigned long)arg); + snd_leave_user(fs); + return result; +} + + +/* exported (for OSS emulator) */ +int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait) +{ + client_t *client; + + client = clientptr(clientid); + if (client == NULL) + return -ENXIO; + + if (! snd_seq_write_pool_allocated(client)) + return 1; + if (snd_seq_pool_poll_wait(client->pool, file, wait)) + return 1; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +/* + * /proc interface + */ +static void snd_seq_info_dump_subscribers(snd_info_buffer_t *buffer, port_subs_info_t *group, int is_src, char *msg) +{ + struct list_head *p; + subscribers_t *s; + int count = 0; + + down_read(&group->list_mutex); + if (list_empty(&group->list_head)) { + up_read(&group->list_mutex); + return; + } + snd_iprintf(buffer, msg); + list_for_each(p, &group->list_head) { + if (is_src) + s = list_entry(p, subscribers_t, src_list); + else + s = list_entry(p, subscribers_t, dest_list); + if (count++) + snd_iprintf(buffer, ", "); + snd_iprintf(buffer, "%d:%d", + is_src ? s->info.dest.client : s->info.sender.client, + is_src ? s->info.dest.port : s->info.sender.port); + if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) + snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue); + if (group->exclusive) + snd_iprintf(buffer, "[ex]"); + } + up_read(&group->list_mutex); + snd_iprintf(buffer, "\n"); +} + +#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-') +#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-') +#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e') + +#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-') + +static void snd_seq_info_dump_ports(snd_info_buffer_t *buffer, client_t *client) +{ + struct list_head *l; + + down(&client->ports_mutex); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n", + p->addr.port, p->name, + FLAG_PERM_RD(p->capability), + FLAG_PERM_WR(p->capability), + FLAG_PERM_EX(p->capability), + FLAG_PERM_DUPLEX(p->capability)); + snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: "); + snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: "); + } + up(&client->ports_mutex); +} + + +/* exported to seq_info.c */ +void snd_seq_info_clients_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + extern void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t * pool, char *space); + int c; + client_t *client; + + snd_iprintf(buffer, "Client info\n"); + snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur); + snd_iprintf(buffer, " peak clients : %d\n", client_usage.peak); + snd_iprintf(buffer, " max clients : %d\n", SNDRV_SEQ_MAX_CLIENTS); + snd_iprintf(buffer, "\n"); + + /* list the client table */ + for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) { + client = snd_seq_client_use_ptr(c); + if (client == NULL) + continue; + if (client->type == NO_CLIENT) { + snd_seq_client_unlock(client); + continue; + } + + snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n", + c, client->name, + client->type == USER_CLIENT ? "User" : "Kernel"); + snd_seq_info_dump_ports(buffer, client); + if (snd_seq_write_pool_allocated(client)) { + snd_iprintf(buffer, " Output pool :\n"); + snd_seq_info_pool(buffer, client->pool, " "); + } + if (client->type == USER_CLIENT && client->data.user.fifo && + client->data.user.fifo->pool) { + snd_iprintf(buffer, " Input pool :\n"); + snd_seq_info_pool(buffer, client->data.user.fifo->pool, " "); + } + snd_seq_client_unlock(client); + } +} + + +/*---------------------------------------------------------------------------*/ + + +/* + * REGISTRATION PART + */ + +static struct file_operations snd_seq_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = snd_seq_read, + .write = snd_seq_write, + .open = snd_seq_open, + .release = snd_seq_release, + .poll = snd_seq_poll, + .ioctl = snd_seq_ioctl, +}; + +static snd_minor_t snd_seq_reg = +{ + .comment = "sequencer", + .f_ops = &snd_seq_f_ops, +}; + + +/* + * register sequencer device + */ +int __init snd_sequencer_device_init(void) +{ + int err; + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) { + up(®ister_mutex); + return err; + } + + up(®ister_mutex); + + return 0; +} + + + +/* + * unregister sequencer device + */ +void __exit snd_sequencer_device_done(void) +{ + snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0); +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_clientmgr.h linux/sound/core/seq/seq_clientmgr.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_clientmgr.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_clientmgr.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,105 @@ +/* + * ALSA sequencer Client Manager + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_CLIENTMGR_H +#define __SND_SEQ_CLIENTMGR_H + +#include +#include +#include "seq_fifo.h" +#include "seq_ports.h" +#include "seq_lock.h" + + +/* client manager */ + +struct _snd_seq_user_client { + struct file *file; /* file struct of client */ + /* ... */ + + /* fifo */ + fifo_t *fifo; /* queue for incoming events */ + int fifo_pool_size; +}; + +struct _snd_seq_kernel_client { + snd_card_t *card; + /* pointer to client functions */ + void *private_data; /* private data for client */ + /* ... */ +}; + + +struct _snd_seq_client { + snd_seq_client_type_t type; + unsigned int accept_input: 1, + accept_output: 1; + char name[64]; /* client name */ + int number; /* client number */ + unsigned int filter; /* filter flags */ + DECLARE_BITMAP(event_filter, 256); + snd_use_lock_t use_lock; + int event_lost; + /* ports */ + int num_ports; /* number of ports */ + struct list_head ports_list_head; + rwlock_t ports_lock; + struct semaphore ports_mutex; + int convert32; /* convert 32->64bit */ + + /* output pool */ + pool_t *pool; /* memory pool for this client */ + + union { + user_client_t user; + kernel_client_t kernel; + } data; +}; + +/* usage statistics */ +typedef struct { + int cur; + int peak; +} usage_t; + + +extern int client_init_data(void); +extern int snd_sequencer_device_init(void); +extern void snd_sequencer_device_done(void); + +/* get locked pointer to client */ +extern client_t *snd_seq_client_use_ptr(int clientid); + +/* unlock pointer to client */ +#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock) + +/* dispatch event to client(s) */ +extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop); + +/* exported to other modules */ +extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data); +extern int snd_seq_unregister_kernel_client(int client); +extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); +int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop); +int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait); +int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype); +int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, int atomic, int hop); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_device.c linux/sound/core/seq/seq_device.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_device.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_device.c 2003-02-15 07:58:06.000000000 -0700 @@ -0,0 +1,548 @@ +/* + * ALSA sequencer device management + * Copyright (c) 1999 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + *---------------------------------------------------------------- + * + * This device handler separates the card driver module from sequencer + * stuff (sequencer core, synth drivers, etc), so that user can avoid + * to spend unnecessary resources e.g. if he needs only listening to + * MP3s. + * + * The card (or lowlevel) driver creates a sequencer device entry + * via snd_seq_device_new(). This is an entry pointer to communicate + * with the sequencer device "driver", which is involved with the + * actual part to communicate with the sequencer core. + * Each sequencer device entry has an id string and the corresponding + * driver with the same id is loaded when required. For example, + * lowlevel codes to access emu8000 chip on sbawe card are included in + * emu8000-synth module. To activate this module, the hardware + * resources like i/o port are passed via snd_seq_device argument. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ALSA sequencer device management"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +/* + * driver list + */ +typedef struct ops_list ops_list_t; + +/* driver state */ +#define DRIVER_EMPTY 0 +#define DRIVER_LOADED (1<<0) +#define DRIVER_REQUESTED (1<<1) +#define DRIVER_LOCKED (1<<2) + +struct ops_list { + char id[ID_LEN]; /* driver id */ + int driver; /* driver state */ + int used; /* reference counter */ + int argsize; /* argument size */ + + /* operators */ + snd_seq_dev_ops_t ops; + + /* registred devices */ + struct list_head dev_list; /* list of devices */ + int num_devices; /* number of associated devices */ + int num_init_devices; /* number of initialized devices */ + struct semaphore reg_mutex; + + struct list_head list; /* next driver */ +}; + + +static LIST_HEAD(opslist); +static int num_ops; +static DECLARE_MUTEX(ops_mutex); +static snd_info_entry_t *info_entry = NULL; + +/* + * prototypes + */ +static int snd_seq_device_free(snd_seq_device_t *dev); +static int snd_seq_device_dev_free(snd_device_t *device); +static int snd_seq_device_dev_register(snd_device_t *device); +static int snd_seq_device_dev_disconnect(snd_device_t *device); +static int snd_seq_device_dev_unregister(snd_device_t *device); + +static int init_device(snd_seq_device_t *dev, ops_list_t *ops); +static int free_device(snd_seq_device_t *dev, ops_list_t *ops); +static ops_list_t *find_driver(char *id, int create_if_empty); +static ops_list_t *create_driver(char *id); +static void unlock_driver(ops_list_t *ops); +static void remove_drivers(void); + +/* + * show all drivers and their status + */ + +static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + struct list_head *head; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", + ops->id, + ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), + ops->driver & DRIVER_REQUESTED ? ",requested" : "", + ops->driver & DRIVER_LOCKED ? ",locked" : "", + ops->num_devices); + } + up(&ops_mutex); +} + +/* + * load all registered drivers (called from seq_clientmgr.c) + */ + +void snd_seq_device_load_drivers(void) +{ +#ifdef CONFIG_KMOD + struct list_head *head; + char modname[64]; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (! (ops->driver & DRIVER_LOADED) && + ! (ops->driver & DRIVER_REQUESTED)) { + ops->used++; + up(&ops_mutex); + ops->driver |= DRIVER_REQUESTED; + sprintf(modname, "snd-%s", ops->id); + request_module(modname); + down(&ops_mutex); + ops->used--; + } + } + up(&ops_mutex); +#endif +} + +/* + * register a sequencer device + * card = card info (NULL allowed) + * device = device number (if any) + * id = id of driver + * result = return pointer (NULL allowed if unnecessary) + */ +int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, + snd_seq_device_t **result) +{ + snd_seq_device_t *dev; + ops_list_t *ops; + int err; + static snd_device_ops_t dops = { + .dev_free = snd_seq_device_dev_free, + .dev_register = snd_seq_device_dev_register, + .dev_disconnect = snd_seq_device_dev_disconnect, + .dev_unregister = snd_seq_device_dev_unregister + }; + + if (result) + *result = NULL; + + snd_assert(id != NULL, return -EINVAL); + + ops = find_driver(id, 1); + if (ops == NULL) + return -ENOMEM; + + dev = snd_magic_kcalloc(snd_seq_device_t, sizeof(*dev) + argsize, GFP_KERNEL); + if (dev == NULL) { + unlock_driver(ops); + return -ENOMEM; + } + + /* set up device info */ + dev->card = card; + dev->device = device; + strncpy(dev->id, id, sizeof(dev->id) - 1); + dev->id[sizeof(dev->id) - 1] = 0; + dev->argsize = argsize; + dev->status = SNDRV_SEQ_DEVICE_FREE; + + /* add this device to the list */ + down(&ops->reg_mutex); + list_add_tail(&dev->list, &ops->dev_list); + ops->num_devices++; + up(&ops->reg_mutex); + + unlock_driver(ops); + + if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { + snd_seq_device_free(dev); + return err; + } + + if (result) + *result = dev; + + return 0; +} + +/* + * free the existing device + */ +static int snd_seq_device_free(snd_seq_device_t *dev) +{ + ops_list_t *ops; + + snd_assert(dev != NULL, return -EINVAL); + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENXIO; + + /* remove the device from the list */ + down(&ops->reg_mutex); + list_del(&dev->list); + ops->num_devices--; + up(&ops->reg_mutex); + + free_device(dev, ops); + if (dev->private_free) + dev->private_free(dev); + snd_magic_kfree(dev); + + unlock_driver(ops); + + return 0; +} + +static int snd_seq_device_dev_free(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + return snd_seq_device_free(dev); +} + +/* + * register the device + */ +static int snd_seq_device_dev_register(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + ops_list_t *ops; + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENOENT; + + /* initialize this device if the corresponding driver was + * already loaded + */ + if (ops->driver & DRIVER_LOADED) + init_device(dev, ops); + + unlock_driver(ops); + return 0; +} + +/* + * disconnect the device + */ +static int snd_seq_device_dev_disconnect(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + ops_list_t *ops; + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENOENT; + + free_device(dev, ops); + + unlock_driver(ops); + return 0; +} + +/* + * unregister the existing device + */ +static int snd_seq_device_dev_unregister(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + return snd_seq_device_free(dev); +} + +/* + * register device driver + * id = driver id + * entry = driver operators - duplicated to each instance + */ +int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize) +{ + struct list_head *head; + ops_list_t *ops; + + if (id == NULL || entry == NULL || + entry->init_device == NULL || entry->free_device == NULL) + return -EINVAL; + + ops = find_driver(id, 1); + if (ops == NULL) + return -ENOMEM; + if (ops->driver & DRIVER_LOADED) { + snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id); + unlock_driver(ops); + return -EBUSY; + } + + down(&ops->reg_mutex); + /* copy driver operators */ + ops->ops = *entry; + ops->driver |= DRIVER_LOADED; + ops->argsize = argsize; + + /* initialize existing devices if necessary */ + list_for_each(head, &ops->dev_list) { + snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); + init_device(dev, ops); + } + up(&ops->reg_mutex); + + unlock_driver(ops); + + return 0; +} + + +/* + * create driver record + */ +static ops_list_t * create_driver(char *id) +{ + ops_list_t *ops; + + ops = kmalloc(sizeof(*ops), GFP_KERNEL); + if (ops == NULL) + return ops; + memset(ops, 0, sizeof(*ops)); + + /* set up driver entry */ + strncpy(ops->id, id, sizeof(ops->id) - 1); + ops->id[sizeof(ops->id) - 1] = 0; + init_MUTEX(&ops->reg_mutex); + ops->driver = DRIVER_EMPTY; + INIT_LIST_HEAD(&ops->dev_list); + /* lock this instance */ + ops->used = 1; + + /* register driver entry */ + down(&ops_mutex); + list_add_tail(&ops->list, &opslist); + num_ops++; + up(&ops_mutex); + + return ops; +} + + +/* + * unregister the specified driver + */ +int snd_seq_device_unregister_driver(char *id) +{ + struct list_head *head; + ops_list_t *ops; + + ops = find_driver(id, 0); + if (ops == NULL) + return -ENXIO; + if (! (ops->driver & DRIVER_LOADED) || + (ops->driver & DRIVER_LOCKED)) { + snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); + unlock_driver(ops); + return -EBUSY; + } + + /* close and release all devices associated with this driver */ + down(&ops->reg_mutex); + ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ + list_for_each(head, &ops->dev_list) { + snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); + free_device(dev, ops); + } + + ops->driver = 0; + if (ops->num_init_devices > 0) + snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); + up(&ops->reg_mutex); + + unlock_driver(ops); + + /* remove empty driver entries */ + remove_drivers(); + + return 0; +} + + +/* + * remove empty driver entries + */ +static void remove_drivers(void) +{ + struct list_head *head; + + down(&ops_mutex); + head = opslist.next; + while (head != &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (! (ops->driver & DRIVER_LOADED) && + ops->used == 0 && ops->num_devices == 0) { + head = head->next; + list_del(&ops->list); + kfree(ops); + num_ops--; + } else + head = head->next; + } + up(&ops_mutex); +} + +/* + * initialize the device - call init_device operator + */ +static int init_device(snd_seq_device_t *dev, ops_list_t *ops) +{ + if (! (ops->driver & DRIVER_LOADED)) + return 0; /* driver is not loaded yet */ + if (dev->status != SNDRV_SEQ_DEVICE_FREE) + return 0; /* already initialized */ + if (ops->argsize != dev->argsize) { + snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); + return -EINVAL; + } + if (ops->ops.init_device(dev) >= 0) { + dev->status = SNDRV_SEQ_DEVICE_REGISTERED; + ops->num_init_devices++; + } else { + snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id); + } + + return 0; +} + +/* + * release the device - call free_device operator + */ +static int free_device(snd_seq_device_t *dev, ops_list_t *ops) +{ + int result; + + if (! (ops->driver & DRIVER_LOADED)) + return 0; /* driver is not loaded yet */ + if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) + return 0; /* not registered */ + if (ops->argsize != dev->argsize) { + snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); + return -EINVAL; + } + if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { + dev->status = SNDRV_SEQ_DEVICE_FREE; + dev->driver_data = NULL; + ops->num_init_devices--; + } else { + snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id); + } + + return 0; +} + +/* + * find the matching driver with given id + */ +static ops_list_t * find_driver(char *id, int create_if_empty) +{ + struct list_head *head; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (strcmp(ops->id, id) == 0) { + ops->used++; + up(&ops_mutex); + return ops; + } + } + up(&ops_mutex); + if (create_if_empty) + return create_driver(id); + return NULL; +} + +static void unlock_driver(ops_list_t *ops) +{ + down(&ops_mutex); + ops->used--; + up(&ops_mutex); +} + + +/* + * module part + */ + +static int __init alsa_seq_device_init(void) +{ + info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root); + if (info_entry == NULL) + return -ENOMEM; + info_entry->content = SNDRV_INFO_CONTENT_TEXT; + info_entry->c.text.read_size = 2048; + info_entry->c.text.read = snd_seq_device_info; + if (snd_info_register(info_entry) < 0) { + snd_info_free_entry(info_entry); + return -ENOMEM; + } + return 0; +} + +static void __exit alsa_seq_device_exit(void) +{ + remove_drivers(); + snd_info_unregister(info_entry); + if (num_ops) + snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); +} + +module_init(alsa_seq_device_init) +module_exit(alsa_seq_device_exit) + +EXPORT_SYMBOL(snd_seq_device_load_drivers); +EXPORT_SYMBOL(snd_seq_device_new); +EXPORT_SYMBOL(snd_seq_device_register_driver); +EXPORT_SYMBOL(snd_seq_device_unregister_driver); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_dummy.c linux/sound/core/seq/seq_dummy.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_dummy.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_dummy.c 2002-05-25 04:26:07.000000000 -0600 @@ -0,0 +1,274 @@ +/* + * ALSA sequencer MIDI-through client + * Copyright (c) 1999-2000 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "seq_clientmgr.h" +#include +#include + +/* + + Sequencer MIDI-through client + + This gives a simple midi-through client. All the normal input events + are redirected to output port immediately. + The routing can be done via aconnect program in alsa-utils. + + Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY). + If you want to auto-load this module, you may add the following alias + in your /etc/conf.modules file. + + alias snd-seq-client-62 snd-seq-dummy + + The module is loaded on demand for client 62, or /proc/asound/seq/ + is accessed. If you don't need this module to be loaded, alias + snd-seq-client-62 as "off". This will help modprobe. + + The number of ports to be created can be specified via the module + paramter "ports". For example, to create four ports, add the + following option in /etc/modules.conf: + + option snd-seq-dummy ports=4 + + The modle option "duplex=1" enables duplex operation to the port. + In duplex mode, a pair of ports are created instead of single port, + and events are tunneled between pair-ports. For example, input to + port A is sent to output port of another port B and vice versa. + In duplex mode, each port has DUPLEX capability. + + */ + + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ALSA sequencer MIDI-through client"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +MODULE_PARM(ports, "i"); +MODULE_PARM_DESC(ports, "number of ports to be created"); +MODULE_PARM(duplex, "i"); +MODULE_PARM_DESC(duplex, "create DUPLEX ports"); +int ports = 1; +int duplex = 0; + +typedef struct snd_seq_dummy_port { + int client; + int port; + int duplex; + int connect; +} snd_seq_dummy_port_t; + +static int my_client = -1; + +/* + * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events + * to subscribers. + * Note: this callback is called only after all subscribers are removed. + */ +static int +dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_seq_dummy_port_t *p; + int i; + snd_seq_event_t ev; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + memset(&ev, 0, sizeof(ev)); + if (p->duplex) + ev.source.port = p->connect; + else + ev.source.port = p->port; + ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + for (i = 0; i < 16; i++) { + ev.data.control.channel = i; + ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF; + snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); + ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS; + snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); + } + return 0; +} + +/* + * event input callback - just redirect events to subscribers + */ +static int +dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) +{ + snd_seq_dummy_port_t *p; + snd_seq_event_t tmpev; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM || + ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) + return 0; /* ignore system messages */ + /* save the original sender */ + tmpev.type = SNDRV_SEQ_EVENT_KERNEL_QUOTE; + tmpev.flags = (ev->flags & ~SNDRV_SEQ_EVENT_LENGTH_MASK) + | SNDRV_SEQ_EVENT_LENGTH_FIXED; + tmpev.tag = ev->tag; + tmpev.time = ev->time; + tmpev.data.quote.origin = ev->source; + tmpev.data.quote.event = ev; + if (p->duplex) + tmpev.source.port = p->connect; + else + tmpev.source.port = p->port; + tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop); +} + +/* + * free_private callback + */ +static void +dummy_free(void *private_data) +{ + snd_seq_dummy_port_t *p; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return); + snd_magic_kfree(p); +} + +/* + * create a port + */ +static snd_seq_dummy_port_t __init * +create_port(int idx, int type) +{ + snd_seq_port_info_t pinfo; + snd_seq_port_callback_t pcb; + snd_seq_dummy_port_t *rec; + + if ((rec = snd_magic_kcalloc(snd_seq_dummy_port_t, 0, GFP_KERNEL)) == NULL) + return NULL; + + rec->client = my_client; + rec->duplex = duplex; + rec->connect = 0; + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr.client = my_client; + if (duplex) + sprintf(pinfo.name, "Midi Through Port-%d:%c", idx, + (type ? 'B' : 'A')); + else + sprintf(pinfo.name, "Midi Through Port-%d", idx); + pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + if (duplex) + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + memset(&pcb, 0, sizeof(pcb)); + pcb.owner = THIS_MODULE; + pcb.unuse = dummy_unuse; + pcb.event_input = dummy_input; + pcb.private_free = dummy_free; + pcb.private_data = rec; + pinfo.kernel = &pcb; + if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) { + snd_magic_kfree(rec); + return NULL; + } + rec->port = pinfo.addr.port; + return rec; +} + +/* + * register client and create ports + */ +static int __init +register_client(void) +{ + snd_seq_client_callback_t cb; + snd_seq_client_info_t cinfo; + snd_seq_dummy_port_t *rec1, *rec2; + int i; + + if (ports < 1) { + snd_printk(KERN_ERR "invalid number of ports %d\n", ports); + return -EINVAL; + } + + /* create client */ + memset(&cb, 0, sizeof(cb)); + cb.allow_input = 1; + cb.allow_output = 1; + my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb); + if (my_client < 0) + return my_client; + + /* set client name */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = my_client; + cinfo.type = KERNEL_CLIENT; + strcpy(cinfo.name, "Midi Through"); + snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + /* create ports */ + for (i = 0; i < ports; i++) { + rec1 = create_port(i, 0); + if (rec1 == NULL) { + snd_seq_delete_kernel_client(my_client); + return -ENOMEM; + } + if (duplex) { + rec2 = create_port(i, 1); + if (rec2 == NULL) { + snd_seq_delete_kernel_client(my_client); + return -ENOMEM; + } + rec1->connect = rec2->port; + rec2->connect = rec1->port; + } + } + + return 0; +} + +/* + * delete client if exists + */ +static void __exit +delete_client(void) +{ + if (my_client >= 0) + snd_seq_delete_kernel_client(my_client); +} + +/* + * Init part + */ + +static int __init alsa_seq_dummy_init(void) +{ + return register_client(); +} + +static void __exit alsa_seq_dummy_exit(void) +{ + delete_client(); +} + +module_init(alsa_seq_dummy_init) +module_exit(alsa_seq_dummy_exit) diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_fifo.c linux/sound/core/seq/seq_fifo.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_fifo.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_fifo.c 2002-10-08 04:54:44.000000000 -0600 @@ -0,0 +1,260 @@ +/* + * ALSA sequencer FIFO + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "seq_fifo.h" +#include "seq_lock.h" + + +/* FIFO */ + +/* create new fifo */ +fifo_t *snd_seq_fifo_new(int poolsize) +{ + fifo_t *f; + + f = snd_kcalloc(sizeof(fifo_t), GFP_KERNEL); + if (f == NULL) { + snd_printd("malloc failed for snd_seq_fifo_new() \n"); + return NULL; + } + + f->pool = snd_seq_pool_new(poolsize); + if (f->pool == NULL) { + kfree(f); + return NULL; + } + if (snd_seq_pool_init(f->pool) < 0) { + snd_seq_pool_delete(&f->pool); + kfree(f); + return NULL; + } + + spin_lock_init(&f->lock); + snd_use_lock_init(&f->use_lock); + init_waitqueue_head(&f->input_sleep); + atomic_set(&f->overflow, 0); + + f->head = NULL; + f->tail = NULL; + f->cells = 0; + + return f; +} + +void snd_seq_fifo_delete(fifo_t **fifo) +{ + fifo_t *f; + + snd_assert(fifo != NULL, return); + f = *fifo; + snd_assert(f != NULL, return); + *fifo = NULL; + + snd_seq_fifo_clear(f); + + /* wake up clients if any */ + if (waitqueue_active(&f->input_sleep)) + wake_up(&f->input_sleep); + + /* release resources...*/ + /*....................*/ + + if (f->pool) { + snd_seq_pool_done(f->pool); + snd_seq_pool_delete(&f->pool); + } + + kfree(f); +} + +static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f); + +/* clear queue */ +void snd_seq_fifo_clear(fifo_t *f) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + /* clear overflow flag */ + atomic_set(&f->overflow, 0); + + snd_use_lock_sync(&f->use_lock); + spin_lock_irqsave(&f->lock, flags); + /* drain the fifo */ + while ((cell = fifo_cell_out(f)) != NULL) { + snd_seq_cell_free(cell); + } + spin_unlock_irqrestore(&f->lock, flags); +} + + +/* enqueue event to fifo */ +int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + int err; + + snd_assert(f != NULL, return -EINVAL); + + snd_use_lock_use(&f->use_lock); + err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ + if (err < 0) { + if (err == -ENOMEM) + atomic_inc(&f->overflow); + snd_use_lock_free(&f->use_lock); + return err; + } + + /* append new cells to fifo */ + spin_lock_irqsave(&f->lock, flags); + if (f->tail != NULL) + f->tail->next = cell; + f->tail = cell; + if (f->head == NULL) + f->head = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + + /* wakeup client */ + if (waitqueue_active(&f->input_sleep)) + wake_up(&f->input_sleep); + + snd_use_lock_free(&f->use_lock); + + return 0; /* success */ + +} + +/* dequeue cell from fifo */ +static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f) +{ + snd_seq_event_cell_t *cell; + + if ((cell = f->head) != NULL) { + f->head = cell->next; + + /* reset tail if this was the last element */ + if (f->tail == cell) + f->tail = NULL; + + cell->next = NULL; + f->cells--; + } + + return cell; +} + +/* dequeue cell from fifo and copy on user space */ +int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + snd_assert(f != NULL, return -EINVAL); + + *cellp = NULL; + spin_lock_irqsave(&f->lock, flags); + while ((cell = fifo_cell_out(f)) == NULL) { + if (nonblock) { + /* non-blocking - return immediately */ + spin_unlock_irqrestore(&f->lock, flags); + return -EAGAIN; + } + spin_unlock(&f->lock); + interruptible_sleep_on(&f->input_sleep); + spin_lock(&f->lock); + + if (signal_pending(current)) { + spin_unlock_irqrestore(&f->lock, flags); + return -ERESTARTSYS; + } + } + *cellp = cell; + spin_unlock_irqrestore(&f->lock, flags); + + return 0; +} + + +void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell) +{ + unsigned long flags; + + if (cell) { + spin_lock_irqsave(&f->lock, flags); + cell->next = f->head; + f->head = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + } +} + + +/* polling; return non-zero if queue is available */ +int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait) +{ + poll_wait(file, &f->input_sleep, wait); + return (f->cells > 0); +} + +/* change the size of pool; all old events are removed */ +int snd_seq_fifo_resize(fifo_t *f, int poolsize) +{ + unsigned long flags; + pool_t *newpool, *oldpool; + snd_seq_event_cell_t *cell, *next, *oldhead; + + snd_assert(f != NULL && f->pool != NULL, return -EINVAL); + + /* allocate new pool */ + newpool = snd_seq_pool_new(poolsize); + if (newpool == NULL) + return -ENOMEM; + if (snd_seq_pool_init(newpool) < 0) { + snd_seq_pool_delete(&newpool); + return -ENOMEM; + } + + spin_lock_irqsave(&f->lock, flags); + /* remember old pool */ + oldpool = f->pool; + oldhead = f->head; + /* exchange pools */ + f->pool = newpool; + f->head = NULL; + f->tail = NULL; + f->cells = 0; + /* NOTE: overflow flag is not cleared */ + spin_unlock_irqrestore(&f->lock, flags); + + /* release cells in old pool */ + for (cell = oldhead; cell; cell = next) { + next = cell->next; + snd_seq_cell_free(cell); + } + snd_seq_pool_delete(&oldpool); + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_fifo.h linux/sound/core/seq/seq_fifo.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_fifo.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_fifo.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,72 @@ +/* + * ALSA sequencer FIFO + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_FIFO_H +#define __SND_SEQ_FIFO_H + +#include "seq_memory.h" +#include "seq_lock.h" + + +/* === FIFO === */ + +typedef struct { + pool_t *pool; /* FIFO pool */ + snd_seq_event_cell_t* head; /* pointer to head of fifo */ + snd_seq_event_cell_t* tail; /* pointer to tail of fifo */ + int cells; + spinlock_t lock; + snd_use_lock_t use_lock; + wait_queue_head_t input_sleep; + atomic_t overflow; + +} fifo_t; + +/* create new fifo (constructor) */ +extern fifo_t *snd_seq_fifo_new(int poolsize); + +/* delete fifo (destructor) */ +extern void snd_seq_fifo_delete(fifo_t **f); + + +/* enqueue event to fifo */ +extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event); + +/* lock fifo from release */ +#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock) +#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock) + +/* get a cell from fifo - fifo should be locked */ +int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock); + +/* free dequeued cell - fifo should be locked */ +extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell); + +/* clean up queue */ +extern void snd_seq_fifo_clear(fifo_t *f); + +/* polling */ +extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait); + +/* resize pool in fifo */ +int snd_seq_fifo_resize(fifo_t *f, int poolsize); + + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_info.c linux/sound/core/seq/seq_info.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_info.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_info.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,75 @@ +/* + * ALSA sequencer /proc interface + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include "seq_info.h" +#include "seq_clientmgr.h" +#include "seq_timer.h" + + +static snd_info_entry_t *queues_entry; +static snd_info_entry_t *clients_entry; +static snd_info_entry_t *timer_entry; + + +static snd_info_entry_t * __init +create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *)) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root); + if (entry == NULL) + return NULL; + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = size; + entry->c.text.read = read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return NULL; + } + return entry; +} + + +/* create all our /proc entries */ +int __init snd_seq_info_init(void) +{ + queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES), + snd_seq_info_queues_read); + clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS), + snd_seq_info_clients_read); + timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read); + return 0; +} + +int __exit snd_seq_info_done(void) +{ + if (queues_entry) + snd_info_unregister(queues_entry); + if (clients_entry) + snd_info_unregister(clients_entry); + if (timer_entry) + snd_info_unregister(timer_entry); + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_info.h linux/sound/core/seq/seq_info.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_info.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_info.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,36 @@ +/* + * ALSA sequencer /proc info + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_INFO_H +#define __SND_SEQ_INFO_H + +#include +#include + +void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); +void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); +void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); + + +int snd_seq_info_init( void ); +int snd_seq_info_done( void ); + + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_instr.c linux/sound/core/seq/seq_instr.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_instr.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_instr.c 2003-01-31 08:19:33.000000000 -0700 @@ -0,0 +1,671 @@ +/* + * Generic Instrument routines for ALSA sequencer + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "seq_clientmgr.h" +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + + +static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list) +{ + if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { + spin_lock_irqsave(&list->ops_lock, list->ops_flags); + } else { + down(&list->ops_mutex); + } +} + +static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list) +{ + if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { + spin_unlock_irqrestore(&list->ops_lock, list->ops_flags); + } else { + up(&list->ops_mutex); + } +} + +snd_seq_kcluster_t *snd_seq_cluster_new(int atomic) +{ + snd_seq_kcluster_t *cluster; + + cluster = (snd_seq_kcluster_t *) snd_kcalloc(sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL); + return cluster; +} + +void snd_seq_cluster_free(snd_seq_kcluster_t *cluster, int atomic) +{ + if (cluster == NULL) + return; + kfree(cluster); +} + +snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic) +{ + snd_seq_kinstr_t *instr; + + instr = (snd_seq_kinstr_t *) snd_kcalloc(sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); + if (instr == NULL) + return NULL; + instr->add_len = add_len; + return instr; +} + +int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic) +{ + int result = 0; + + if (instr == NULL) + return -EINVAL; + if (instr->ops && instr->ops->remove) + result = instr->ops->remove(instr->ops->private_data, instr, 1); + if (!result) + kfree(instr); + return result; +} + +snd_seq_kinstr_list_t *snd_seq_instr_list_new(void) +{ + snd_seq_kinstr_list_t *list; + + list = (snd_seq_kinstr_list_t *) snd_kcalloc(sizeof(snd_seq_kinstr_list_t), GFP_KERNEL); + if (list == NULL) + return NULL; + spin_lock_init(&list->lock); + spin_lock_init(&list->ops_lock); + init_MUTEX(&list->ops_mutex); + list->owner = -1; + return list; +} + +void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr) +{ + snd_seq_kinstr_list_t *list; + snd_seq_kinstr_t *instr; + snd_seq_kcluster_t *cluster; + int idx; + unsigned long flags; + + if (list_ptr == NULL) + return; + list = *list_ptr; + *list_ptr = NULL; + if (list == NULL) + return; + + for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { + while ((instr = list->hash[idx]) != NULL) { + list->hash[idx] = instr->next; + list->count--; + spin_lock_irqsave(&list->lock, flags); + while (instr->use) { + spin_unlock_irqrestore(&list->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&list->lock, flags); + } + spin_unlock_irqrestore(&list->lock, flags); + if (snd_seq_instr_free(instr, 0)<0) + snd_printk(KERN_WARNING "instrument free problem\n"); + } + while ((cluster = list->chash[idx]) != NULL) { + list->chash[idx] = cluster->next; + list->ccount--; + snd_seq_cluster_free(cluster, 0); + } + } + kfree(list); +} + +static int instr_free_compare(snd_seq_kinstr_t *instr, + snd_seq_instr_header_t *ifree, + unsigned int client) +{ + switch (ifree->cmd) { + case SNDRV_SEQ_INSTR_FREE_CMD_ALL: + /* all, except private for other clients */ + if ((instr->instr.std & 0xff000000) == 0) + return 0; + if (((instr->instr.std >> 24) & 0xff) == client) + return 0; + return 1; + case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE: + /* all my private instruments */ + if ((instr->instr.std & 0xff000000) == 0) + return 1; + if (((instr->instr.std >> 24) & 0xff) == client) + return 0; + return 1; + case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER: + /* all my private instruments */ + if ((instr->instr.std & 0xff000000) == 0) { + if (instr->instr.cluster == ifree->id.cluster) + return 0; + return 1; + } + if (((instr->instr.std >> 24) & 0xff) == client) { + if (instr->instr.cluster == ifree->id.cluster) + return 0; + } + return 1; + } + return 1; +} + +int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list, + snd_seq_instr_header_t *ifree, + int client, + int atomic) +{ + snd_seq_kinstr_t *instr, *prev, *next, *flist; + int idx; + unsigned long flags; + + snd_instr_lock_ops(list); + for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { + spin_lock_irqsave(&list->lock, flags); + instr = list->hash[idx]; + prev = flist = NULL; + while (instr) { + while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) { + prev = instr; + instr = instr->next; + } + if (instr == NULL) + continue; + if (instr->ops && instr->ops->notify) + instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); + next = instr->next; + if (prev == NULL) { + list->hash[idx] = next; + } else { + prev->next = next; + } + list->count--; + instr->next = flist; + flist = instr; + instr = next; + } + spin_unlock_irqrestore(&list->lock, flags); + while (flist) { + instr = flist; + flist = instr->next; + while (instr->use) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + if (snd_seq_instr_free(instr, atomic)<0) + snd_printk(KERN_WARNING "instrument free problem\n"); + instr = next; + } + } + snd_instr_unlock_ops(list); + return 0; +} + +static int compute_hash_instr_key(snd_seq_instr_t *instr) +{ + int result; + + result = instr->bank | (instr->prg << 16); + result += result >> 24; + result += result >> 16; + result += result >> 8; + return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); +} + +#if 0 +static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster) +{ + int result; + + result = cluster; + result += result >> 24; + result += result >> 16; + result += result >> 8; + return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); +} +#endif + +static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact) +{ + if (exact) { + if (i1->cluster != i2->cluster || + i1->bank != i2->bank || + i1->prg != i2->prg) + return 1; + if ((i1->std & 0xff000000) != (i2->std & 0xff000000)) + return 1; + if (!(i1->std & i2->std)) + return 1; + return 0; + } else { + unsigned int client_check; + + if (i2->cluster && i1->cluster != i2->cluster) + return 1; + client_check = i2->std & 0xff000000; + if (client_check) { + if ((i1->std & 0xff000000) != client_check) + return 1; + } else { + if ((i1->std & i2->std) != i2->std) + return 1; + } + return i1->bank != i2->bank || i1->prg != i2->prg; + } +} + +snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list, + snd_seq_instr_t *instr, + int exact, + int follow_alias) +{ + unsigned long flags; + int depth = 0; + snd_seq_kinstr_t *result; + + if (list == NULL || instr == NULL) + return NULL; + spin_lock_irqsave(&list->lock, flags); + __again: + result = list->hash[compute_hash_instr_key(instr)]; + while (result) { + if (!compare_instr(&result->instr, instr, exact)) { + if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) { + instr = (snd_seq_instr_t *)KINSTR_DATA(result); + if (++depth > 10) + goto __not_found; + goto __again; + } + result->use++; + spin_unlock_irqrestore(&list->lock, flags); + return result; + } + result = result->next; + } + __not_found: + spin_unlock_irqrestore(&list->lock, flags); + return NULL; +} + +void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list, + snd_seq_kinstr_t *instr) +{ + unsigned long flags; + + if (list == NULL || instr == NULL) + return; + spin_lock_irqsave(&list->lock, flags); + if (instr->use <= 0) { + snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); + } else { + instr->use--; + } + spin_unlock_irqrestore(&list->lock, flags); +} + +static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type) +{ + while (ops) { + if (!strcmp(ops->instr_type, instr_type)) + return ops; + ops = ops->next; + } + return NULL; +} + +static int instr_result(snd_seq_event_t *ev, + int type, int result, + int atomic) +{ + snd_seq_event_t sev; + + memset(&sev, 0, sizeof(sev)); + sev.type = SNDRV_SEQ_EVENT_RESULT; + sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED | + SNDRV_SEQ_PRIORITY_NORMAL; + sev.source = ev->dest; + sev.dest = ev->source; + sev.data.result.event = type; + sev.data.result.result = result; +#if 0 + printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n", + type, result, + sev.queue, + sev.source.client, sev.source.port, + sev.dest.client, sev.dest.port); +#endif + return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0); +} + +static int instr_begin(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + if (list->owner >= 0 && list->owner != ev->source.client) { + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic); + } + list->owner = ev->source.client; + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic); +} + +static int instr_end(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + + /* TODO: timeout handling */ + spin_lock_irqsave(&list->lock, flags); + if (list->owner == ev->source.client) { + list->owner = -1; + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic); + } + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic); +} + +static int instr_info(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_format_info(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_reset(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_status(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_put(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + snd_seq_instr_header_t put; + snd_seq_kinstr_t *instr; + int result = -EINVAL, len, key; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) + goto __return; + + if (ev->data.ext.len < sizeof(snd_seq_instr_header_t)) + goto __return; + if (copy_from_user(&put, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) { + result = -EFAULT; + goto __return; + } + snd_instr_lock_ops(list); + if (put.id.instr.std & 0xff000000) { /* private instrument */ + put.id.instr.std &= 0x00ffffff; + put.id.instr.std |= (unsigned int)ev->source.client << 24; + } + if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) { + snd_seq_instr_free_use(list, instr); + snd_instr_unlock_ops(list); + result = -EBUSY; + goto __return; + } + ops = instr_ops(ops, put.data.data.format); + if (ops == NULL) { + snd_instr_unlock_ops(list); + goto __return; + } + len = ops->add_len; + if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS) + len = sizeof(snd_seq_instr_t); + instr = snd_seq_instr_new(len, atomic); + if (instr == NULL) { + snd_instr_unlock_ops(list); + result = -ENOMEM; + goto __return; + } + instr->ops = ops; + instr->instr = put.id.instr; + strncpy(instr->name, put.data.name, sizeof(instr->name)-1); + instr->name[sizeof(instr->name)-1] = '\0'; + instr->type = put.data.type; + if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) { + result = ops->put(ops->private_data, + instr, + ev->data.ext.ptr + sizeof(snd_seq_instr_header_t), + ev->data.ext.len - sizeof(snd_seq_instr_header_t), + atomic, + put.cmd); + if (result < 0) { + snd_seq_instr_free(instr, atomic); + snd_instr_unlock_ops(list); + goto __return; + } + } + key = compute_hash_instr_key(&instr->instr); + spin_lock_irqsave(&list->lock, flags); + instr->next = list->hash[key]; + list->hash[key] = instr; + list->count++; + spin_unlock_irqrestore(&list->lock, flags); + snd_instr_unlock_ops(list); + result = 0; + __return: + instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic); + return result; +} + +static int instr_get(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_free(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + snd_seq_instr_header_t ifree; + snd_seq_kinstr_t *instr, *prev; + int result = -EINVAL; + unsigned long flags; + unsigned int hash; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) + goto __return; + + if (ev->data.ext.len < sizeof(snd_seq_instr_header_t)) + goto __return; + if (copy_from_user(&ifree, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) { + result = -EFAULT; + goto __return; + } + if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL || + ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE || + ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) { + result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic); + goto __return; + } + if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) { + if (ifree.id.instr.std & 0xff000000) { + ifree.id.instr.std &= 0x00ffffff; + ifree.id.instr.std |= (unsigned int)ev->source.client << 24; + } + hash = compute_hash_instr_key(&ifree.id.instr); + snd_instr_lock_ops(list); + spin_lock_irqsave(&list->lock, flags); + instr = list->hash[hash]; + prev = NULL; + while (instr) { + if (!compare_instr(&instr->instr, &ifree.id.instr, 1)) + goto __free_single; + prev = instr; + instr = instr->next; + } + result = -ENOENT; + spin_unlock_irqrestore(&list->lock, flags); + snd_instr_unlock_ops(list); + goto __return; + + __free_single: + if (prev) { + prev->next = instr->next; + } else { + list->hash[hash] = instr->next; + } + if (instr->ops && instr->ops->notify) + instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); + while (instr->use) { + spin_unlock_irqrestore(&list->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&list->lock, flags); + } + spin_unlock_irqrestore(&list->lock, flags); + result = snd_seq_instr_free(instr, atomic); + snd_instr_unlock_ops(list); + goto __return; + } + + __return: + instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic); + return result; +} + +static int instr_list(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_cluster(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int client, + int atomic, + int hop) +{ + int direct = 0; + + snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL); + if (snd_seq_ev_is_direct(ev)) { + direct = 1; + switch (ev->type) { + case SNDRV_SEQ_EVENT_INSTR_BEGIN: + return instr_begin(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_END: + return instr_end(ops, list, ev, atomic, hop); + } + } + if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct) + return -EINVAL; + switch (ev->type) { + case SNDRV_SEQ_EVENT_INSTR_INFO: + return instr_info(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_FINFO: + return instr_format_info(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_RESET: + return instr_reset(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_STATUS: + return instr_status(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_PUT: + return instr_put(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_GET: + return instr_get(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_FREE: + return instr_free(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_LIST: + return instr_list(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_CLUSTER: + return instr_cluster(ops, list, ev, atomic, hop); + } + return -EINVAL; +} + +/* + * Init part + */ + +static int __init alsa_seq_instr_init(void) +{ + return 0; +} + +static void __exit alsa_seq_instr_exit(void) +{ +} + +module_init(alsa_seq_instr_init) +module_exit(alsa_seq_instr_exit) + +EXPORT_SYMBOL(snd_seq_instr_list_new); +EXPORT_SYMBOL(snd_seq_instr_list_free); +EXPORT_SYMBOL(snd_seq_instr_list_free_cond); +EXPORT_SYMBOL(snd_seq_instr_find); +EXPORT_SYMBOL(snd_seq_instr_free_use); +EXPORT_SYMBOL(snd_seq_instr_event); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_lock.c linux/sound/core/seq/seq_lock.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_lock.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_lock.c 2002-12-02 08:11:45.000000000 -0700 @@ -0,0 +1,86 @@ +/* + * Do sleep inside a spin-lock + * Copyright (c) 1999 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "seq_lock.h" + +#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG) + +#if 0 /* NOT USED */ +/* (interruptible) sleep_on during the specified spinlock */ +void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock) +{ + wait_queue_t wait; + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + + add_wait_queue(p, &wait); + + spin_unlock(lock); + schedule(); + spin_lock_irq(lock); + + remove_wait_queue(p, &wait); +} + +/* (interruptible) sleep_on with timeout during the specified spinlock */ +long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout) +{ + wait_queue_t wait; + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + + add_wait_queue(p, &wait); + + spin_unlock(lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(lock); + + remove_wait_queue(p, &wait); + + return timeout; +} +#endif /* NOT USED */ + +/* wait until all locks are released */ +void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) +{ + int max_count = 5 * HZ; + + if (atomic_read(lockp) < 0) { + printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); + return; + } + while (atomic_read(lockp) > 0) { + if (max_count == 0) { + snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + max_count--; + } +} + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_lock.h linux/sound/core/seq/seq_lock.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_lock.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_lock.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,33 @@ +#ifndef __SND_SEQ_LOCK_H +#define __SND_SEQ_LOCK_H + +#include + +#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG) + +typedef atomic_t snd_use_lock_t; + +/* initialize lock */ +#define snd_use_lock_init(lockp) atomic_set(lockp, 0) + +/* increment lock */ +#define snd_use_lock_use(lockp) atomic_inc(lockp) + +/* release lock */ +#define snd_use_lock_free(lockp) atomic_dec(lockp) + +/* wait until all locks are released */ +void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line); +#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__) + +#else /* SMP || CONFIG_SND_DEBUG */ + +typedef spinlock_t snd_use_lock_t; /* dummy */ +#define snd_use_lock_init(lockp) /**/ +#define snd_use_lock_use(lockp) /**/ +#define snd_use_lock_free(lockp) /**/ +#define snd_use_lock_sync(lockp) /**/ + +#endif /* SMP || CONFIG_SND_DEBUG */ + +#endif /* __SND_SEQ_LOCK_H */ diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_memory.c linux/sound/core/seq/seq_memory.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_memory.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_memory.c 2002-10-08 08:54:33.000000000 -0600 @@ -0,0 +1,517 @@ +/* + * ALSA sequencer Memory Manager + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * 2000 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#include +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_info.h" +#include "seq_lock.h" + +/* semaphore in struct file record */ +#define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem) + + +inline static int snd_seq_pool_available(pool_t *pool) +{ + return pool->total_elements - atomic_read(&pool->counter); +} + +inline static int snd_seq_output_ok(pool_t *pool) +{ + return snd_seq_pool_available(pool) >= pool->room; +} + +/* + * Variable length event: + * The event like sysex uses variable length type. + * The external data may be stored in three different formats. + * 1) kernel space + * This is the normal case. + * ext.data.len = length + * ext.data.ptr = buffer pointer + * 2) user space + * When an event is generated via read(), the external data is + * kept in user space until expanded. + * ext.data.len = length | SNDRV_SEQ_EXT_USRPTR + * ext.data.ptr = userspace pointer + * 3) chained cells + * When the variable length event is enqueued (in prioq or fifo), + * the external data is decomposed to several cells. + * ext.data.len = length | SNDRV_SEQ_EXT_CHAINED + * ext.data.ptr = the additiona cell head + * -> cell.next -> cell.next -> .. + */ + +/* + * exported: + * call dump function to expand external data. + */ + +static int get_var_len(const snd_seq_event_t *event) +{ + if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + return -EINVAL; + + return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; +} + +int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data) +{ + int len, err; + snd_seq_event_cell_t *cell; + + if ((len = get_var_len(event)) <= 0) + return len; + + if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { + char buf[32]; + char *curptr = event->data.ext.ptr; + while (len > 0) { + int size = sizeof(buf); + if (len < size) + size = len; + if (copy_from_user(buf, curptr, size) < 0) + return -EFAULT; + err = func(private_data, buf, size); + if (err < 0) + return err; + curptr += size; + len -= size; + } + return 0; + } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) { + return func(private_data, event->data.ext.ptr, len); + } + + cell = (snd_seq_event_cell_t*)event->data.ext.ptr; + for (; len > 0 && cell; cell = cell->next) { + int size = sizeof(snd_seq_event_t); + if (len < size) + size = len; + err = func(private_data, &cell->event, size); + if (err < 0) + return err; + len -= size; + } + return 0; +} + + +/* + * exported: + * expand the variable length event to linear buffer space. + */ + +static int seq_copy_in_kernel(char **bufptr, const void *src, int size) +{ + memcpy(*bufptr, src, size); + *bufptr += size; + return 0; +} + +static int seq_copy_in_user(char **bufptr, const void *src, int size) +{ + if (copy_to_user(*bufptr, src, size)) + return -EFAULT; + *bufptr += size; + return 0; +} + +int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned) +{ + int len, newlen; + int err; + + if ((len = get_var_len(event)) < 0) + return len; + newlen = len; + if (size_aligned > 0) + newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned; + if (count < newlen) + return -EAGAIN; + + if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { + if (! in_kernel) + return -EINVAL; + if (copy_from_user(buf, event->data.ext.ptr, len) < 0) + return -EFAULT; + return newlen; + } + err = snd_seq_dump_var_event(event, + in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel : + (snd_seq_dump_func_t)seq_copy_in_user, + &buf); + return err < 0 ? err : newlen; +} + + +/* + * release this cell, free extended data if available + */ + +static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell) +{ + cell->next = pool->free; + pool->free = cell; + atomic_dec(&pool->counter); +} + +void snd_seq_cell_free(snd_seq_event_cell_t * cell) +{ + unsigned long flags; + pool_t *pool; + + snd_assert(cell != NULL, return); + pool = cell->pool; + snd_assert(pool != NULL, return); + + spin_lock_irqsave(&pool->lock, flags); + free_cell(pool, cell); + if (snd_seq_ev_is_variable(&cell->event)) { + if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) { + snd_seq_event_cell_t *curp, *nextptr; + curp = cell->event.data.ext.ptr; + for (; curp; curp = nextptr) { + nextptr = curp->next; + curp->next = pool->free; + free_cell(pool, curp); + } + } + } + if (waitqueue_active(&pool->output_sleep)) { + /* has enough space now? */ + if (snd_seq_output_ok(pool)) + wake_up(&pool->output_sleep); + } + spin_unlock_irqrestore(&pool->lock, flags); +} + + +/* + * allocate an event cell. + */ +int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + int err = -EAGAIN; + + if (pool == NULL) + return -EINVAL; + + *cellp = NULL; + + spin_lock_irqsave(&pool->lock, flags); + if (pool->ptr == NULL) { /* not initialized */ + snd_printd("seq: pool is not initialized\n"); + err = -EINVAL; + goto __error; + } + while (pool->free == NULL && ! nonblock && ! pool->closing) { + + spin_unlock(&pool->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) + /* change semaphore to allow other clients + to access device file */ + if (file) + up(&semaphore_of(file)); +#endif + interruptible_sleep_on(&pool->output_sleep); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) + /* restore semaphore again */ + if (file) + down(&semaphore_of(file)); +#endif + spin_lock(&pool->lock); + /* interrupted? */ + if (signal_pending(current)) { + err = -ERESTARTSYS; + goto __error; + } + } + if (pool->closing) { /* closing.. */ + err = -ENOMEM; + goto __error; + } + + cell = pool->free; + if (cell) { + int used; + pool->free = cell->next; + atomic_inc(&pool->counter); + used = atomic_read(&pool->counter); + if (pool->max_used < used) + pool->max_used = used; + pool->event_alloc_success++; + /* clear cell pointers */ + cell->next = NULL; + err = 0; + } else + pool->event_alloc_failures++; + *cellp = cell; + +__error: + spin_unlock_irqrestore(&pool->lock, flags); + return err; +} + + +/* + * duplicate the event to a cell. + * if the event has external data, the data is decomposed to additional + * cells. + */ +int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) +{ + int ncells, err; + unsigned int extlen; + snd_seq_event_cell_t *cell; + + *cellp = NULL; + + ncells = 0; + extlen = 0; + if (snd_seq_ev_is_variable(event)) { + extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; + ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + } + if (ncells >= pool->total_elements) + return -ENOMEM; + + err = snd_seq_cell_alloc(pool, &cell, nonblock, file); + if (err < 0) + return err; + + /* copy the event */ + cell->event = *event; + + /* decompose */ + if (snd_seq_ev_is_variable(event)) { + int len = extlen; + int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED; + int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR; + snd_seq_event_cell_t *src, *tmp, *tail; + char *buf; + + cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED; + cell->event.data.ext.ptr = NULL; + + src = (snd_seq_event_cell_t*)event->data.ext.ptr; + buf = (char *)event->data.ext.ptr; + tail = NULL; + + while (ncells-- > 0) { + int size = sizeof(snd_seq_event_t); + if (len < size) + size = len; + err = snd_seq_cell_alloc(pool, &tmp, nonblock, file); + if (err < 0) + goto __error; + if (cell->event.data.ext.ptr == NULL) + cell->event.data.ext.ptr = tmp; + if (tail) + tail->next = tmp; + tail = tmp; + /* copy chunk */ + if (is_chained && src) { + tmp->event = src->event; + src = src->next; + } else if (is_usrptr) { + if (copy_from_user(&tmp->event, buf, size)) { + err = -EFAULT; + goto __error; + } + } else { + memcpy(&tmp->event, buf, size); + } + buf += size; + len -= size; + } + } + + *cellp = cell; + return 0; + +__error: + snd_seq_cell_free(cell); + return err; +} + + +/* poll wait */ +int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait) +{ + poll_wait(file, &pool->output_sleep, wait); + return snd_seq_output_ok(pool); +} + + +/* allocate room specified number of events */ +int snd_seq_pool_init(pool_t *pool) +{ + int cell; + snd_seq_event_cell_t *cellptr; + unsigned long flags; + + snd_assert(pool != NULL, return -EINVAL); + if (pool->ptr) /* should be atomic? */ + return 0; + + pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size); + if (pool->ptr == NULL) { + snd_printd("seq: malloc for sequencer events failed\n"); + return -ENOMEM; + } + + /* add new cells to the free cell list */ + spin_lock_irqsave(&pool->lock, flags); + pool->free = NULL; + + for (cell = 0; cell < pool->size; cell++) { + cellptr = pool->ptr + cell; + cellptr->pool = pool; + cellptr->next = pool->free; + pool->free = cellptr; + } + pool->room = (pool->size + 1) / 2; + + /* init statistics */ + pool->max_used = 0; + pool->total_elements = pool->size; + spin_unlock_irqrestore(&pool->lock, flags); + return 0; +} + +/* remove events */ +int snd_seq_pool_done(pool_t *pool) +{ + unsigned long flags; + snd_seq_event_cell_t *ptr; + int max_count = 5 * HZ; + + snd_assert(pool != NULL, return -EINVAL); + + /* wait for closing all threads */ + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 1; + spin_unlock_irqrestore(&pool->lock, flags); + + if (waitqueue_active(&pool->output_sleep)) + wake_up(&pool->output_sleep); + + while (atomic_read(&pool->counter) > 0) { + if (max_count == 0) { + snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + max_count--; + } + + /* release all resources */ + spin_lock_irqsave(&pool->lock, flags); + ptr = pool->ptr; + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + spin_unlock_irqrestore(&pool->lock, flags); + + if (ptr) + vfree(ptr); + + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 0; + spin_unlock_irqrestore(&pool->lock, flags); + + return 0; +} + + +/* init new memory pool */ +pool_t *snd_seq_pool_new(int poolsize) +{ + pool_t *pool; + + /* create pool block */ + pool = snd_kcalloc(sizeof(pool_t), GFP_KERNEL); + if (pool == NULL) { + snd_printd("seq: malloc failed for pool\n"); + return NULL; + } + spin_lock_init(&pool->lock); + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + atomic_set(&pool->counter, 0); + pool->closing = 0; + init_waitqueue_head(&pool->output_sleep); + + pool->size = poolsize; + + /* init statistics */ + pool->max_used = 0; + return pool; +} + +/* remove memory pool */ +int snd_seq_pool_delete(pool_t **ppool) +{ + pool_t *pool = *ppool; + + *ppool = NULL; + if (pool == NULL) + return 0; + snd_seq_pool_done(pool); + kfree(pool); + return 0; +} + +/* initialize sequencer memory */ +int __init snd_sequencer_memory_init(void) +{ + return 0; +} + +/* release sequencer memory */ +void __exit snd_sequencer_memory_done(void) +{ +} + + +/* exported to seq_clientmgr.c */ +void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space) +{ + if (pool == NULL) + return; + snd_iprintf(buffer, "%sPool size : %d\n", space, pool->total_elements); + snd_iprintf(buffer, "%sCells in use : %d\n", space, atomic_read(&pool->counter)); + snd_iprintf(buffer, "%sPeak cells in use : %d\n", space, pool->max_used); + snd_iprintf(buffer, "%sAlloc success : %d\n", space, pool->event_alloc_success); + snd_iprintf(buffer, "%sAlloc failures : %d\n", space, pool->event_alloc_failures); +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_memory.h linux/sound/core/seq/seq_memory.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_memory.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_memory.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,105 @@ +/* + * ALSA sequencer Memory Manager + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_MEMORYMGR_H +#define __SND_SEQ_MEMORYMGR_H + +#include +#include + +typedef struct pool pool_t; + +/* container for sequencer event (internal use) */ +typedef struct snd_seq_event_cell_t { + snd_seq_event_t event; + pool_t *pool; /* used pool */ + struct snd_seq_event_cell_t *next; /* next cell */ +} snd_seq_event_cell_t; + +/* design note: the pool is a contigious block of memory, if we dynamicly + want to add additional cells to the pool be better store this in another + pool as we need to know the base address of the pool when releasing + memory. */ + +struct pool { + snd_seq_event_cell_t *ptr; /* pointer to first event chunk */ + snd_seq_event_cell_t *free; /* pointer to the head of the free list */ + + int total_elements; /* pool size actually allocated */ + atomic_t counter; /* cells free */ + + int size; /* pool size to be allocated */ + int room; /* watermark for sleep/wakeup */ + + int closing; + + /* statistics */ + int max_used; + int event_alloc_nopool; + int event_alloc_failures; + int event_alloc_success; + + /* Write locking */ + wait_queue_head_t output_sleep; + + /* Pool lock */ + spinlock_t lock; +}; + +extern void snd_seq_cell_free(snd_seq_event_cell_t* cell); +int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file); + +int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file); + +/* return number of unused (free) cells */ +static inline int snd_seq_unused_cells(pool_t *pool) +{ + return pool ? pool->total_elements - atomic_read(&pool->counter) : 0; +} + +/* return total number of allocated cells */ +static inline int snd_seq_total_cells(pool_t *pool) +{ + return pool ? pool->total_elements : 0; +} + +/* init pool - allocate events */ +int snd_seq_pool_init(pool_t *pool); + +/* done pool - free events */ +int snd_seq_pool_done(pool_t *pool); + +/* create pool */ +pool_t *snd_seq_pool_new(int poolsize); + +/* remove pool */ +int snd_seq_pool_delete(pool_t **pool); + +/* init memory */ +int snd_sequencer_memory_init(void); + +/* release event memory */ +void snd_sequencer_memory_done(void); + +/* polling */ +int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait); + + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_midi.c linux/sound/core/seq/seq_midi.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_midi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_midi.c 2003-02-27 04:10:36.000000000 -0700 @@ -0,0 +1,482 @@ +/* + * Generic MIDI synth driver for ALSA sequencer + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* +Possible options for midisynth module: + - automatic opening of midi ports on first received event or subscription + (close will be performed when client leaves) +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +int output_buffer_size = PAGE_SIZE; +MODULE_PARM(output_buffer_size, "i"); +MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes."); +int input_buffer_size = PAGE_SIZE; +MODULE_PARM(input_buffer_size, "i"); +MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes."); + +/* data for this midi synth driver */ +typedef struct { + snd_card_t *card; + int device; + int subdevice; + snd_rawmidi_file_t input_rfile; + snd_rawmidi_file_t output_rfile; + int seq_client; + int seq_port; + snd_midi_event_t *parser; +} seq_midisynth_t; + +typedef struct { + int seq_client; + int num_ports; + int ports_per_device[SNDRV_RAWMIDI_DEVICES]; + seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES]; +} seq_midisynth_client_t; + +static seq_midisynth_client_t *synths[SNDRV_CARDS]; +static DECLARE_MUTEX(register_mutex); + +/* handle rawmidi input event (MIDI v1.0 stream) */ +static void snd_midi_input_event(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime; + seq_midisynth_t *msynth; + snd_seq_event_t ev; + char buf[16], *pbuf; + long res, count; + + if (substream == NULL) + return; + runtime = substream->runtime; + msynth = (seq_midisynth_t *) runtime->private_data; + if (msynth == NULL) + return; + memset(&ev, 0, sizeof(ev)); + while (runtime->avail > 0) { + res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); + if (res <= 0) + continue; + if (msynth->parser == NULL) + continue; + pbuf = buf; + while (res > 0) { + count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev); + if (count < 0) + break; + pbuf += count; + res -= count; + if (ev.type != SNDRV_SEQ_EVENT_NONE) { + ev.source.port = msynth->seq_port; + ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0); + /* clear event and reset header */ + memset(&ev, 0, sizeof(ev)); + } + } + } +} + +static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count) +{ + snd_rawmidi_runtime_t *runtime; + int tmp; + + snd_assert(substream != NULL || buf != NULL, return -EINVAL); + runtime = substream->runtime; + if ((tmp = runtime->avail) < count) { + snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp); + return -ENOMEM; + } + if (snd_rawmidi_kernel_write(substream, buf, count) < count) + return -EINVAL; + return 0; +} + +static int event_process_midi(snd_seq_event_t * ev, int direct, + void *private_data, int atomic, int hop) +{ + seq_midisynth_t *msynth = (seq_midisynth_t *) private_data; + unsigned char msg[10]; /* buffer for constructing midi messages */ + snd_rawmidi_substream_t *substream; + int res; + + snd_assert(msynth != NULL, return -EINVAL); + substream = msynth->output_rfile.output; + if (substream == NULL) + return -ENODEV; + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { + /* invalid event */ + snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); + return 0; + } + res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); + snd_midi_event_reset_decode(msynth->parser); + if (res < 0) + return res; + } else { + if (msynth->parser == NULL) + return -EIO; + res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); + if (res < 0) + return res; + if ((res = dump_midi(substream, msg, res)) < 0) { + snd_midi_event_reset_decode(msynth->parser); + return res; + } + } + return 0; +} + + +static int snd_seq_midisynth_new(seq_midisynth_t *msynth, + snd_card_t *card, + int device, + int subdevice) +{ + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0) + return -ENOMEM; + msynth->card = card; + msynth->device = device; + msynth->subdevice = subdevice; + return 0; +} + +/* open associated midi device for input */ +static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + snd_rawmidi_runtime_t *runtime; + snd_rawmidi_params_t params; + + /* open midi port */ + if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) { + snd_printd("midi input open failed!!!\n"); + return err; + } + runtime = msynth->input_rfile.input->runtime; + memset(¶ms, 0, sizeof(params)); + params.avail_min = 1; + params.buffer_size = input_buffer_size; + if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms)) < 0) { + snd_rawmidi_kernel_release(&msynth->input_rfile); + return err; + } + runtime->event = snd_midi_input_event; + runtime->private_data = msynth; + snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); + return 0; +} + +/* close associated midi device for input */ +static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + + snd_assert(msynth->input_rfile.input != NULL, return -EINVAL); + err = snd_rawmidi_kernel_release(&msynth->input_rfile); + return err; +} + +/* open associated midi device for output */ +static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + snd_rawmidi_params_t params; + + /* open midi port */ + if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) { + snd_printd("midi output open failed!!!\n"); + return err; + } + memset(¶ms, 0, sizeof(params)); + params.avail_min = 1; + params.buffer_size = output_buffer_size; + if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) { + snd_rawmidi_kernel_release(&msynth->output_rfile); + return err; + } + return 0; +} + +/* close associated midi device for output */ +static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + unsigned char buf = 0xff; /* MIDI reset */ + + snd_assert(msynth->output_rfile.output != NULL, return -EINVAL); + /* sending single MIDI reset message to shut the device up */ + snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1); + snd_rawmidi_drain_output(msynth->output_rfile.output); + return snd_rawmidi_kernel_release(&msynth->output_rfile); +} + +/* delete given midi synth port */ +static void snd_seq_midisynth_delete(seq_midisynth_t *msynth) +{ + snd_seq_port_info_t port; + + if (msynth == NULL) + return; + + if (msynth->seq_client > 0) { + /* delete port */ + memset(&port, 0, sizeof(port)); + port.addr.client = msynth->seq_client; + port.addr.port = msynth->seq_port; + snd_seq_kernel_client_ctl(port.addr.client, SNDRV_SEQ_IOCTL_DELETE_PORT, &port); + } + + if (msynth->parser) + snd_midi_event_free(msynth->parser); +} + +/* set our client name */ +static int set_client_name(seq_midisynth_client_t *client, snd_card_t *card, + snd_rawmidi_info_t *rmidi) +{ + snd_seq_client_info_t cinfo; + const char *name; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client->seq_client; + cinfo.type = KERNEL_CLIENT; + name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI"; + snprintf(cinfo.name, sizeof(cinfo.name), "Rawmidi %d - %s", card->number, name); + return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); +} + +/* register new midi synth port */ +int +snd_seq_midisynth_register_port(snd_seq_device_t *dev) +{ + seq_midisynth_client_t *client; + seq_midisynth_t *msynth, *ms; + snd_seq_port_info_t port; + snd_rawmidi_info_t info; + int newclient = 0; + unsigned int p, ports; + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_card_t *card = dev->card; + int device = dev->device; + unsigned int input_count = 0, output_count = 0; + + snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL); + info.device = device; + info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + info.subdevice = 0; + if (snd_rawmidi_info_select(card, &info) >= 0) + output_count = info.subdevices_count; + info.stream = SNDRV_RAWMIDI_STREAM_INPUT; + if (snd_rawmidi_info_select(card, &info) >= 0) { + input_count = info.subdevices_count; + } + ports = output_count; + if (ports < input_count) + ports = input_count; + if (ports == 0) + return -ENODEV; + if (ports > (256 / SNDRV_RAWMIDI_DEVICES)) + ports = 256 / SNDRV_RAWMIDI_DEVICES; + + down(®ister_mutex); + client = synths[card->number]; + if (client == NULL) { + newclient = 1; + client = snd_kcalloc(sizeof(seq_midisynth_client_t), GFP_KERNEL); + if (client == NULL) { + up(®ister_mutex); + return -ENOMEM; + } + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = client; + callbacks.allow_input = callbacks.allow_output = 1; + client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks); + if (client->seq_client < 0) { + kfree(client); + up(®ister_mutex); + return -ENOMEM; + } + set_client_name(client, card, &info); + } else if (device == 0) + set_client_name(client, card, &info); /* use the first device's name */ + + msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL); + if (msynth == NULL) + goto __nomem; + + for (p = 0; p < ports; p++) { + ms = &msynth[p]; + + if (snd_seq_midisynth_new(ms, card, device, p) < 0) + goto __nomem; + + /* declare port */ + memset(&port, 0, sizeof(port)); + port.addr.client = client->seq_client; + port.addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + memset(&info, 0, sizeof(info)); + info.device = device; + if (p < output_count) + info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + else + info.stream = SNDRV_RAWMIDI_STREAM_INPUT; + info.subdevice = p; + if (snd_rawmidi_info_select(card, &info) >= 0) + strcpy(port.name, info.subname); + if (! port.name[0]) { + if (info.name[0]) { + if (ports > 1) + snprintf(port.name, sizeof(port.name), "%s-%d", info.name, p); + else + snprintf(port.name, sizeof(port.name), "%s", info.name); + } else { + /* last resort */ + if (ports > 1) + sprintf(port.name, "MIDI %d-%d-%d", card->number, device, p); + else + sprintf(port.name, "MIDI %d-%d", card->number, device); + } + } + if ((info.flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count) + port.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + if ((info.flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count) + port.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + if ((port.capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) && + info.flags & SNDRV_RAWMIDI_INFO_DUPLEX) + port.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + port.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + port.midi_channels = 16; + memset(&pcallbacks, 0, sizeof(pcallbacks)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.private_data = ms; + pcallbacks.subscribe = midisynth_subscribe; + pcallbacks.unsubscribe = midisynth_unsubscribe; + pcallbacks.use = midisynth_use; + pcallbacks.unuse = midisynth_unuse; + pcallbacks.event_input = event_process_midi; + port.kernel = &pcallbacks; + if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &port)<0) + goto __nomem; + ms->seq_client = client->seq_client; + ms->seq_port = port.addr.port; + } + client->ports_per_device[device] = ports; + client->ports[device] = msynth; + client->num_ports++; + if (newclient) + synths[card->number] = client; + up(®ister_mutex); + return 0; /* success */ + + __nomem: + if (msynth != NULL) { + for (p = 0; p < ports; p++) + snd_seq_midisynth_delete(&msynth[p]); + kfree(msynth); + } + if (newclient) { + snd_seq_delete_kernel_client(client->seq_client); + kfree(client); + } + up(®ister_mutex); + return -ENOMEM; +} + +/* release midi synth port */ +int +snd_seq_midisynth_unregister_port(snd_seq_device_t *dev) +{ + seq_midisynth_client_t *client; + seq_midisynth_t *msynth; + snd_card_t *card = dev->card; + int device = dev->device, p, ports; + + down(®ister_mutex); + client = synths[card->number]; + if (client == NULL || client->ports[device] == NULL) { + up(®ister_mutex); + return -ENODEV; + } + snd_seq_event_port_detach(client->seq_client, client->ports[device]->seq_port); + ports = client->ports_per_device[device]; + client->ports_per_device[device] = 0; + msynth = client->ports[device]; + client->ports[device] = NULL; + snd_runtime_check(msynth != NULL || ports <= 0, goto __skip); + for (p = 0; p < ports; p++) + snd_seq_midisynth_delete(&msynth[p]); + kfree(msynth); + __skip: + client->num_ports--; + if (client->num_ports <= 0) { + snd_seq_delete_kernel_client(client->seq_client); + synths[card->number] = NULL; + kfree(client); + } + up(®ister_mutex); + return 0; +} + + +static int __init alsa_seq_midi_init(void) +{ + static snd_seq_dev_ops_t ops = { + snd_seq_midisynth_register_port, + snd_seq_midisynth_unregister_port, + }; + memset(&synths, 0, sizeof(synths)); + snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); + return 0; +} + +static void __exit alsa_seq_midi_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); +} + +module_init(alsa_seq_midi_init) +module_exit(alsa_seq_midi_exit) diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_midi_emul.c linux/sound/core/seq/seq_midi_emul.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_midi_emul.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_midi_emul.c 2003-02-25 05:35:44.000000000 -0700 @@ -0,0 +1,740 @@ +/* + * GM/GS/XG midi module. + * + * Copyright (C) 1999 Steve Ratcliffe + * + * Based on awe_wave.c by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * This module is used to keep track of the current midi state. + * It can be used for drivers that are required to emulate midi when + * the hardware doesn't. + * + * It was written for a AWE64 driver, but there should be no AWE specific + * code in here. If there is it should be reported as a bug. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe"); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +/* Prototypes for static functions */ +static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel); +static void do_control(snd_midi_op_t *ops, void *private, + snd_midi_channel_set_t *chset, snd_midi_channel_t *chan, + int control, int value); +static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset); +static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); +static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); +void snd_midi_reset_controllers(snd_midi_channel_t *chan); +static void reset_all_channels(snd_midi_channel_set_t *chset); + + +/* + * Process an event in a driver independent way. This means dealing + * with RPN, NRPN, SysEx etc that are defined for common midi applications + * such as GM, GS and XG. + * There modes that this module will run in are: + * Generic MIDI - no interpretation at all, it will just save current values + * of controlers etc. + * GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN, + * SysEx will be interpreded as defined in General Midi. + * GS - You can use all gs_ prefixed elements of chan. Codes for GS will be + * interpreted. + * XG - You can use all xg_ prefixed elements of chan. Codes for XG will + * be interpreted. + */ +void +snd_midi_process_event(snd_midi_op_t *ops, + snd_seq_event_t *ev, snd_midi_channel_set_t *chanset) +{ + snd_midi_channel_t *chan; + void *drv; + int dest_channel = 0; + + if (ev == NULL || chanset == NULL) { + snd_printd("ev or chanbase NULL (snd_midi_process_event)\n"); + return; + } + if (chanset->channels == NULL) + return; + + if (snd_seq_ev_is_channel_type(ev)) { + dest_channel = ev->data.note.channel; + if (dest_channel >= chanset->max_channels) { + snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels); + return; + } + } + + chan = chanset->channels + dest_channel; + drv = chanset->private_data; + + /* EVENT_NOTE should be processed before queued */ + if (ev->type == SNDRV_SEQ_EVENT_NOTE) + return; + + /* Make sure that we don't have a note on that should really be + * a note off */ + if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) + ev->type = SNDRV_SEQ_EVENT_NOTEOFF; + + /* Make sure the note is within array range */ + if (ev->type == SNDRV_SEQ_EVENT_NOTEON || + ev->type == SNDRV_SEQ_EVENT_NOTEOFF || + ev->type == SNDRV_SEQ_EVENT_KEYPRESS) { + if (ev->data.note.note >= 128) + return; + } + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) { + if (ops->note_off) + ops->note_off(drv, ev->data.note.note, 0, chan); + } + chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON; + if (ops->note_on) + ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan); + break; + case SNDRV_SEQ_EVENT_NOTEOFF: + if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON)) + break; + if (ops->note_off) + note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity); + break; + case SNDRV_SEQ_EVENT_KEYPRESS: + if (ops->key_press) + ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan); + break; + case SNDRV_SEQ_EVENT_CONTROLLER: + do_control(ops, drv, chanset, chan, + ev->data.control.param, ev->data.control.value); + break; + case SNDRV_SEQ_EVENT_PGMCHANGE: + chan->midi_program = ev->data.control.value; + break; + case SNDRV_SEQ_EVENT_PITCHBEND: + chan->midi_pitchbend = ev->data.control.value; + if (ops->control) + ops->control(drv, MIDI_CTL_PITCHBEND, chan); + break; + case SNDRV_SEQ_EVENT_CHANPRESS: + chan->midi_pressure = ev->data.control.value; + if (ops->control) + ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan); + break; + case SNDRV_SEQ_EVENT_CONTROL14: + /* Best guess is that this is any of the 14 bit controller values */ + if (ev->data.control.param < 32) { + /* set low part first */ + chan->control[ev->data.control.param + 32] = + ev->data.control.value & 0x7f; + do_control(ops, drv, chanset, chan, + ev->data.control.param, + ((ev->data.control.value>>7) & 0x7f)); + } else + do_control(ops, drv, chanset, chan, + ev->data.control.param, + ev->data.control.value); + break; + case SNDRV_SEQ_EVENT_NONREGPARAM: + /* Break it back into its controler values */ + chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; + chan->control[MIDI_CTL_MSB_DATA_ENTRY] + = (ev->data.control.value >> 7) & 0x7f; + chan->control[MIDI_CTL_LSB_DATA_ENTRY] + = ev->data.control.value & 0x7f; + chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] + = (ev->data.control.param >> 7) & 0x7f; + chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] + = ev->data.control.param & 0x7f; + nrpn(ops, drv, chan, chanset); + break; + case SNDRV_SEQ_EVENT_REGPARAM: + /* Break it back into its controler values */ + chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; + chan->control[MIDI_CTL_MSB_DATA_ENTRY] + = (ev->data.control.value >> 7) & 0x7f; + chan->control[MIDI_CTL_LSB_DATA_ENTRY] + = ev->data.control.value & 0x7f; + chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] + = (ev->data.control.param >> 7) & 0x7f; + chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB] + = ev->data.control.param & 0x7f; + rpn(ops, drv, chan, chanset); + break; + case SNDRV_SEQ_EVENT_SYSEX: + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { + unsigned char sysexbuf[64]; + int len; + len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0); + if (len > 0) + sysex(ops, drv, sysexbuf, len, chanset); + } + break; + case SNDRV_SEQ_EVENT_SONGPOS: + case SNDRV_SEQ_EVENT_SONGSEL: + case SNDRV_SEQ_EVENT_CLOCK: + case SNDRV_SEQ_EVENT_START: + case SNDRV_SEQ_EVENT_CONTINUE: + case SNDRV_SEQ_EVENT_STOP: + case SNDRV_SEQ_EVENT_QFRAME: + case SNDRV_SEQ_EVENT_TEMPO: + case SNDRV_SEQ_EVENT_TIMESIGN: + case SNDRV_SEQ_EVENT_KEYSIGN: + goto not_yet; + case SNDRV_SEQ_EVENT_SENSING: + break; + case SNDRV_SEQ_EVENT_CLIENT_START: + case SNDRV_SEQ_EVENT_CLIENT_EXIT: + case SNDRV_SEQ_EVENT_CLIENT_CHANGE: + case SNDRV_SEQ_EVENT_PORT_START: + case SNDRV_SEQ_EVENT_PORT_EXIT: + case SNDRV_SEQ_EVENT_PORT_CHANGE: + case SNDRV_SEQ_EVENT_SAMPLE: + case SNDRV_SEQ_EVENT_SAMPLE_START: + case SNDRV_SEQ_EVENT_SAMPLE_STOP: + case SNDRV_SEQ_EVENT_SAMPLE_FREQ: + case SNDRV_SEQ_EVENT_SAMPLE_VOLUME: + case SNDRV_SEQ_EVENT_SAMPLE_LOOP: + case SNDRV_SEQ_EVENT_SAMPLE_POSITION: + case SNDRV_SEQ_EVENT_ECHO: + not_yet: + default: + /*snd_printd("Unimplemented event %d\n", ev->type);*/ + break; + } +} + + +/* + * release note + */ +static void +note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel) +{ + if (chan->gm_hold) { + /* Hold this note until pedal is turned off */ + chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED; + } else if (chan->note[note] & SNDRV_MIDI_NOTE_SUSTENUTO) { + /* Mark this note as release; it will be turned off when sustenuto + * is turned off */ + chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED; + } else { + chan->note[note] = 0; + if (ops->note_off) + ops->note_off(drv, note, vel, chan); + } +} + +/* + * Do all driver independent operations for this controler and pass + * events that need to take place immediately to the driver. + */ +static void +do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset, + snd_midi_channel_t *chan, int control, int value) +{ + int i; + + /* Switches */ + if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) { + /* These are all switches; either off or on so set to 0 or 127 */ + value = (value >= 64)? 127: 0; + } + chan->control[control] = value; + + switch (control) { + case MIDI_CTL_SUSTAIN: + if (value == 0) { + /* Sustain has been released, turn off held notes */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) { + chan->note[i] = SNDRV_MIDI_NOTE_OFF; + if (ops->note_off) + ops->note_off(drv, i, 0, chan); + } + } + } + break; + case MIDI_CTL_PORTAMENTO: + break; + case MIDI_CTL_SUSTENUTO: + if (value) { + /* Mark each note that is currently held down */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_ON) + chan->note[i] |= SNDRV_MIDI_NOTE_SUSTENUTO; + } + } else { + /* release all notes that were held */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_SUSTENUTO) { + chan->note[i] &= ~SNDRV_MIDI_NOTE_SUSTENUTO; + if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) { + chan->note[i] = SNDRV_MIDI_NOTE_OFF; + if (ops->note_off) + ops->note_off(drv, i, 0, chan); + } + } + } + } + break; + case MIDI_CTL_MSB_DATA_ENTRY: + chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0; + /* go through here */ + case MIDI_CTL_LSB_DATA_ENTRY: + if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED) + rpn(ops, drv, chan, chset); + else + nrpn(ops, drv, chan, chset); + break; + case MIDI_CTL_REGIST_PARM_NUM_LSB: + case MIDI_CTL_REGIST_PARM_NUM_MSB: + chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; + break; + case MIDI_CTL_NONREG_PARM_NUM_LSB: + case MIDI_CTL_NONREG_PARM_NUM_MSB: + chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; + break; + + case MIDI_CTL_ALL_SOUNDS_OFF: + all_sounds_off(ops, drv, chan); + break; + + case MIDI_CTL_ALL_NOTES_OFF: + all_notes_off(ops, drv, chan); + break; + + case MIDI_CTL_MSB_BANK: + if (chset->midi_mode == SNDRV_MIDI_MODE_XG) { + if (value == 127) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } + break; + case MIDI_CTL_LSB_BANK: + break; + + case MIDI_CTL_RESET_CONTROLLERS: + snd_midi_reset_controllers(chan); + break; + + case MIDI_CTL_SOFT_PEDAL: + case MIDI_CTL_LEGATO_FOOTSWITCH: + case MIDI_CTL_HOLD2: + case MIDI_CTL_SC1_SOUND_VARIATION: + case MIDI_CTL_SC2_TIMBRE: + case MIDI_CTL_SC3_RELEASE_TIME: + case MIDI_CTL_SC4_ATTACK_TIME: + case MIDI_CTL_SC5_BRIGHTNESS: + case MIDI_CTL_E1_REVERB_DEPTH: + case MIDI_CTL_E2_TREMOLO_DEPTH: + case MIDI_CTL_E3_CHORUS_DEPTH: + case MIDI_CTL_E4_DETUNE_DEPTH: + case MIDI_CTL_E5_PHASER_DEPTH: + goto notyet; + notyet: + default: + if (ops->control) + ops->control(drv, control, chan); + break; + } +} + + +/* + * initialize the MIDI status + */ +void +snd_midi_channel_set_clear(snd_midi_channel_set_t *chset) +{ + int i; + + chset->midi_mode = SNDRV_MIDI_MODE_GM; + chset->gs_master_volume = 127; + + for (i = 0; i < chset->max_channels; i++) { + snd_midi_channel_t *chan = chset->channels + i; + memset(chan->note, 0, sizeof(chan->note)); + + chan->midi_aftertouch = 0; + chan->midi_pressure = 0; + chan->midi_program = 0; + chan->midi_pitchbend = 0; + snd_midi_reset_controllers(chan); + chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + chan->gm_rpn_fine_tuning = 0; + chan->gm_rpn_coarse_tuning = 0; + + if (i == 9) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } +} + +/* + * Process a rpn message. + */ +static void +rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + int type; + int val; + + if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) { + type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) | + chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]; + val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | + chan->control[MIDI_CTL_LSB_DATA_ENTRY]; + + switch (type) { + case 0x0000: /* Pitch bend sensitivity */ + /* MSB only / 1 semitone per 128 */ + chan->gm_rpn_pitch_bend_range = val; + break; + + case 0x0001: /* fine tuning: */ + /* MSB/LSB, 8192=center, 100/8192 cent step */ + chan->gm_rpn_fine_tuning = val - 8192; + break; + + case 0x0002: /* coarse tuning */ + /* MSB only / 8192=center, 1 semitone per 128 */ + chan->gm_rpn_coarse_tuning = val - 8192; + break; + + case 0x7F7F: /* "lock-in" RPN */ + /* ignored */ + break; + } + } + /* should call nrpn or rpn callback here.. */ +} + +/* + * Process an nrpn message. + */ +static void +nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + /* parse XG NRPNs here if possible */ + if (ops->nrpn) + ops->nrpn(drv, chan, chset); +} + + +/* + * convert channel parameter in GS sysex + */ +static int +get_channel(unsigned char cmd) +{ + int p = cmd & 0x0f; + if (p == 0) + p = 9; + else if (p < 10) + p--; + return p; +} + + +/* + * Process a sysex message. + */ +static void +sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset) +{ + /* GM on */ + static unsigned char gm_on_macro[] = { + 0x7e,0x7f,0x09,0x01, + }; + /* XG on */ + static unsigned char xg_on_macro[] = { + 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, + }; + /* GS prefix + * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off + * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 + * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 + * master vol: XX=0x00, YY=0x04, ZZ=0-127 + */ + static unsigned char gs_pfx_macro[] = { + 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ + }; + + int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED; + + if (len <= 0 || buf[0] != 0xf0) + return; + /* skip first byte */ + buf++; + len--; + + /* GM on */ + if (len >= (int)sizeof(gm_on_macro) && + memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { + if (chset->midi_mode != SNDRV_MIDI_MODE_GS && + chset->midi_mode != SNDRV_MIDI_MODE_XG) { + chset->midi_mode = SNDRV_MIDI_MODE_GM; + reset_all_channels(chset); + parsed = SNDRV_MIDI_SYSEX_GM_ON; + } + } + + /* GS macros */ + else if (len >= 8 && + memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { + if (chset->midi_mode != SNDRV_MIDI_MODE_GS && + chset->midi_mode != SNDRV_MIDI_MODE_XG) + chset->midi_mode = SNDRV_MIDI_MODE_GS; + + if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) { + /* GS reset */ + parsed = SNDRV_MIDI_SYSEX_GS_RESET; + reset_all_channels(chset); + } + + else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) { + /* drum pattern */ + int p = get_channel(buf[5]); + if (p < chset->max_channels) { + parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL; + if (buf[7]) + chset->channels[p].drum_channel = 1; + else + chset->channels[p].drum_channel = 0; + } + + } else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) { + /* program */ + int p = get_channel(buf[5]); + if (p < chset->max_channels && + ! chset->channels[p].drum_channel) { + parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL; + chset->channels[p].midi_program = buf[7]; + } + + } else if (buf[5] == 0x01 && buf[6] == 0x30) { + /* reverb mode */ + parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE; + chset->gs_reverb_mode = buf[7]; + + } else if (buf[5] == 0x01 && buf[6] == 0x38) { + /* chorus mode */ + parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE; + chset->gs_chorus_mode = buf[7]; + + } else if (buf[5] == 0x00 && buf[6] == 0x04) { + /* master volume */ + parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE; + chset->gs_master_volume = buf[7]; + + } + } + + /* XG on */ + else if (len >= (int)sizeof(xg_on_macro) && + memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { + int i; + chset->midi_mode = SNDRV_MIDI_MODE_XG; + parsed = SNDRV_MIDI_SYSEX_XG_ON; + /* reset CC#0 for drums */ + for (i = 0; i < chset->max_channels; i++) { + if (chset->channels[i].drum_channel) + chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127; + else + chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0; + } + } + + if (ops->sysex) + ops->sysex(private, buf - 1, len + 1, parsed, chset); +} + +/* + * all sound off + */ +static void +all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan) +{ + int n; + + if (! ops->note_terminate) + return; + for (n = 0; n < 128; n++) { + if (chan->note[n]) { + ops->note_terminate(drv, n, chan); + chan->note[n] = 0; + } + } +} + +/* + * all notes off + */ +static void +all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan) +{ + int n; + + if (! ops->note_off) + return; + for (n = 0; n < 128; n++) { + if (chan->note[n] == SNDRV_MIDI_NOTE_ON) + note_off(ops, drv, chan, n, 0); + } +} + +/* + * Initialise a single midi channel control block. + */ +void snd_midi_channel_init(snd_midi_channel_t *p, int n) +{ + if (p == NULL) + return; + + memset(p, 0, sizeof(snd_midi_channel_t)); + p->private = NULL; + p->number = n; + + snd_midi_reset_controllers(p); + p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + p->gm_rpn_fine_tuning = 0; + p->gm_rpn_coarse_tuning = 0; + + if (n == 9) + p->drum_channel = 1; /* Default ch 10 as drums */ +} + +/* + * Allocate and initialise a set of midi channel control blocks. + */ +snd_midi_channel_t *snd_midi_channel_init_set(int n) +{ + snd_midi_channel_t *chan; + int i; + + chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL); + if (chan) { + for (i = 0; i < n; i++) + snd_midi_channel_init(chan+i, i); + } + + return chan; +} + +/* + * reset all midi channels + */ +static void +reset_all_channels(snd_midi_channel_set_t *chset) +{ + int ch; + for (ch = 0; ch < chset->max_channels; ch++) { + snd_midi_channel_t *chan = chset->channels + ch; + snd_midi_reset_controllers(chan); + chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + chan->gm_rpn_fine_tuning = 0; + chan->gm_rpn_coarse_tuning = 0; + + if (ch == 9) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } +} + + +/* + * Allocate and initialise a midi channel set. + */ +snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n) +{ + snd_midi_channel_set_t *chset; + + chset = kmalloc(sizeof(*chset), GFP_KERNEL); + if (chset) { + chset->channels = snd_midi_channel_init_set(n); + chset->private_data = NULL; + chset->max_channels = n; + } + return chset; +} + +/* + * Reset the midi controllers on a particular channel to default values. + */ +void snd_midi_reset_controllers(snd_midi_channel_t *chan) +{ + memset(chan->control, 0, sizeof(chan->control)); + chan->gm_volume = 127; + chan->gm_expression = 127; + chan->gm_pan = 64; +} + + +/* + * Free a midi channel set. + */ +void snd_midi_channel_free_set(snd_midi_channel_set_t *chset) +{ + if (chset == NULL) + return; + if (chset->channels != NULL) + kfree(chset->channels); + kfree(chset); +} + +static int __init alsa_seq_midi_emul_init(void) +{ + return 0; +} + +static void __exit alsa_seq_midi_emul_exit(void) +{ +} + +module_init(alsa_seq_midi_emul_init) +module_exit(alsa_seq_midi_emul_exit) + +EXPORT_SYMBOL(snd_midi_process_event); +EXPORT_SYMBOL(snd_midi_channel_set_clear); +EXPORT_SYMBOL(snd_midi_channel_init); +EXPORT_SYMBOL(snd_midi_channel_init_set); +EXPORT_SYMBOL(snd_midi_channel_alloc_set); +EXPORT_SYMBOL(snd_midi_channel_free_set); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_midi_event.c linux/sound/core/seq/seq_midi_event.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_midi_event.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_midi_event.c 2003-01-31 08:19:34.000000000 -0700 @@ -0,0 +1,490 @@ +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99 Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai , Jaroslav Kysela "); +MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder"); +MODULE_LICENSE("GPL"); + +/* queue type */ +/* from 0 to 7 are normal commands (note off, on, etc.) */ +#define ST_NOTEOFF 0 +#define ST_NOTEON 1 +#define ST_SPECIAL 8 +#define ST_SYSEX ST_SPECIAL +/* from 8 to 15 are events for 0xf0-0xf7 */ + + +/* status event types */ +typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev); +typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf); + +/* + * prototypes + */ +static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void note_decode(snd_seq_event_t *ev, unsigned char *buf); +static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf); +static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf); +static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf); +static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf); + +/* + * event list + */ +static struct status_event_list_t { + int event; + int qlen; + event_encode_t encode; + event_decode_t decode; +} status_event[] = { + /* 0x80 - 0xf0 */ + {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, + {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode}, + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */ + /* 0xf0 - 0xff */ + {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ + {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ + {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ + {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */ + {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */ + {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */ + {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */ + {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */ + {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */ + {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ + {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ +}; + +static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); + +static struct extra_event_list_t { + int event; + int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); +} extra_event[] = { + {SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14}, + /*{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_nrpn},*/ + /*{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_rpn},*/ +}; + +/* + * new/delete record + */ + +int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev) +{ + snd_midi_event_t *dev; + + *rdev = NULL; + dev = (snd_midi_event_t *)snd_kcalloc(sizeof(snd_midi_event_t), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + if (bufsize > 0) { + dev->buf = kmalloc(bufsize, GFP_KERNEL); + if (dev->buf == NULL) { + kfree(dev); + return -ENOMEM; + } + } + dev->bufsize = bufsize; + dev->lastcmd = 0xff; + spin_lock_init(&dev->lock); + *rdev = dev; + return 0; +} + +void snd_midi_event_free(snd_midi_event_t *dev) +{ + if (dev != NULL) { + if (dev->buf) + kfree(dev->buf); + kfree(dev); + } +} + +/* + * initialize record + */ +inline static void reset_encode(snd_midi_event_t *dev) +{ + dev->read = 0; + dev->qlen = 0; + dev->type = 0; +} + +void snd_midi_event_reset_encode(snd_midi_event_t *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + reset_encode(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +void snd_midi_event_reset_decode(snd_midi_event_t *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dev->lastcmd = 0xff; + spin_unlock_irqrestore(&dev->lock, flags); +} + +void snd_midi_event_init(snd_midi_event_t *dev) +{ + snd_midi_event_reset_encode(dev); + snd_midi_event_reset_decode(dev); +} + +void snd_midi_event_no_status(snd_midi_event_t *dev, int on) +{ + dev->nostat = on ? 1 : 0; +} + +/* + * resize buffer + */ +int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize) +{ + unsigned char *new_buf, *old_buf; + unsigned long flags; + + if (bufsize == dev->bufsize) + return 0; + new_buf = kmalloc(bufsize, GFP_KERNEL); + if (new_buf == NULL) + return -ENOMEM; + spin_lock_irqsave(&dev->lock, flags); + old_buf = dev->buf; + dev->buf = new_buf; + dev->bufsize = bufsize; + reset_encode(dev); + spin_unlock_irqrestore(&dev->lock, flags); + if (old_buf) + kfree(old_buf); + return 0; +} + +/* + * read bytes and encode to sequencer event if finished + * return the size of encoded bytes + */ +long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev) +{ + long result = 0; + int rc; + + ev->type = SNDRV_SEQ_EVENT_NONE; + + while (count-- > 0) { + rc = snd_midi_event_encode_byte(dev, *buf++, ev); + result++; + if (rc < 0) + return rc; + else if (rc > 0) + return result; + } + + return result; +} + +/* + * read one byte and encode to sequencer event: + * return 1 if MIDI bytes are encoded to an event + * 0 data is not finished + * negative for error + */ +int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev) +{ + int rc = 0; + unsigned long flags; + + c &= 0xff; + + if (c >= MIDI_CMD_COMMON_CLOCK) { + /* real-time event */ + ev->type = status_event[ST_SPECIAL + c - 0xf0].event; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + return 1; + } + + spin_lock_irqsave(&dev->lock, flags); + if (dev->qlen > 0) { + /* rest of command */ + dev->buf[dev->read++] = c; + if (dev->type != ST_SYSEX) + dev->qlen--; + } else { + /* new command */ + dev->read = 1; + if (c & 0x80) { + dev->buf[0] = c; + if ((c & 0xf0) == 0xf0) /* special events */ + dev->type = (c & 0x0f) + ST_SPECIAL; + else + dev->type = (c >> 4) & 0x07; + dev->qlen = status_event[dev->type].qlen; + } else { + /* process this byte as argument */ + dev->buf[dev->read++] = c; + dev->qlen = status_event[dev->type].qlen - 1; + } + } + if (dev->qlen == 0) { + ev->type = status_event[dev->type].event; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + if (status_event[dev->type].encode) /* set data values */ + status_event[dev->type].encode(dev, ev); + rc = 1; + } else if (dev->type == ST_SYSEX) { + if (c == MIDI_CMD_COMMON_SYSEX_END || + dev->read >= dev->bufsize) { + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->data.ext.len = dev->read; + ev->data.ext.ptr = dev->buf; + if (c != MIDI_CMD_COMMON_SYSEX_END) + dev->read = 0; /* continue to parse */ + else + reset_encode(dev); /* all parsed */ + rc = 1; + } + } + + spin_unlock_irqrestore(&dev->lock, flags); + return rc; +} + +/* encode note event */ +static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.note.channel = dev->buf[0] & 0x0f; + ev->data.note.note = dev->buf[1]; + ev->data.note.velocity = dev->buf[2]; +} + +/* encode one parameter controls */ +static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.value = dev->buf[1]; +} + +/* encode pitch wheel change */ +static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192; +} + +/* encode midi control change */ +static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.param = dev->buf[1]; + ev->data.control.value = dev->buf[2]; +} + +/* encode one parameter value*/ +static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.value = dev->buf[1]; +} + +/* encode song position */ +static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1]; +} + +/* + * decode from a sequencer event to midi bytes + * return the size of decoded midi events + */ +long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev) +{ + unsigned int cmd, type; + + if (ev->type == SNDRV_SEQ_EVENT_NONE) + return -ENOENT; + + for (type = 0; type < ARRAY_SIZE(status_event); type++) { + if (ev->type == status_event[type].event) + goto __found; + } + for (type = 0; type < ARRAY_SIZE(extra_event); type++) { + if (ev->type == extra_event[type].event) + return extra_event[type].decode(dev, buf, count, ev); + } + return -ENOENT; + + __found: + if (type >= ST_SPECIAL) + cmd = 0xf0 + (type - ST_SPECIAL); + else + /* data.note.channel and data.control.channel is identical */ + cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f); + + + if (cmd == MIDI_CMD_COMMON_SYSEX) { + snd_midi_event_reset_decode(dev); + return snd_seq_expand_var_event(ev, count, buf, 1, 0); + } else { + int qlen; + unsigned char xbuf[4]; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) { + dev->lastcmd = cmd; + spin_unlock_irqrestore(&dev->lock, flags); + xbuf[0] = cmd; + if (status_event[type].decode) + status_event[type].decode(ev, xbuf + 1); + qlen = status_event[type].qlen + 1; + } else { + spin_unlock_irqrestore(&dev->lock, flags); + if (status_event[type].decode) + status_event[type].decode(ev, xbuf + 0); + qlen = status_event[type].qlen; + } + if (count < qlen) + return -ENOMEM; + memcpy(buf, xbuf, qlen); + return qlen; + } +} + + +/* decode note event */ +static void note_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.note.note & 0x7f; + buf[1] = ev->data.note.velocity & 0x7f; +} + +/* decode one parameter controls */ +static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.value & 0x7f; +} + +/* decode pitch wheel change */ +static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + int value = ev->data.control.value + 8192; + buf[0] = value & 0x7f; + buf[1] = (value >> 7) & 0x7f; +} + +/* decode midi control change */ +static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.param & 0x7f; + buf[1] = ev->data.control.value & 0x7f; +} + +/* decode song position */ +static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.value & 0x7f; + buf[1] = (ev->data.control.value >> 7) & 0x7f; +} + +/* decode 14bit control */ +static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev) +{ + unsigned char cmd; + int idx = 0; + + if (ev->data.control.param < 32) { + if (count < 4) + return -ENOMEM; + if (dev->nostat && count < 6) + return -ENOMEM; + cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); + if (cmd != dev->lastcmd || dev->nostat) { + if (count < 5) + return -ENOMEM; + buf[idx++] = dev->lastcmd = cmd; + } + buf[idx++] = ev->data.control.param; + buf[idx++] = (ev->data.control.value >> 7) & 0x7f; + if (dev->nostat) + buf[idx++] = cmd; + buf[idx++] = ev->data.control.param + 32; + buf[idx++] = ev->data.control.value & 0x7f; + return idx; + } else { + if (count < 2) + return -ENOMEM; + cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); + if (cmd != dev->lastcmd || dev->nostat) { + if (count < 3) + return -ENOMEM; + buf[idx++] = dev->lastcmd = cmd; + } + buf[idx++] = ev->data.control.param & 0x7f; + buf[idx++] = ev->data.control.value & 0x7f; + return idx; + } +} + +/* + * exports + */ + +EXPORT_SYMBOL(snd_midi_event_new); +EXPORT_SYMBOL(snd_midi_event_free); +EXPORT_SYMBOL(snd_midi_event_resize_buffer); +EXPORT_SYMBOL(snd_midi_event_init); +EXPORT_SYMBOL(snd_midi_event_reset_encode); +EXPORT_SYMBOL(snd_midi_event_reset_decode); +EXPORT_SYMBOL(snd_midi_event_no_status); +EXPORT_SYMBOL(snd_midi_event_encode); +EXPORT_SYMBOL(snd_midi_event_encode_byte); +EXPORT_SYMBOL(snd_midi_event_decode); diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_ports.c linux/sound/core/seq/seq_ports.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_ports.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_ports.c 2003-02-28 08:08:20.000000000 -0700 @@ -0,0 +1,664 @@ +/* + * ALSA sequencer Ports + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "seq_system.h" +#include "seq_ports.h" +#include "seq_clientmgr.h" + +/* + + registration of client ports + + */ + + +/* + +NOTE: the current implementation of the port structure as a linked list is +not optimal for clients that have many ports. For sending messages to all +subscribers of a port we first need to find the address of the port +structure, which means we have to traverse the list. A direct access table +(array) would be better, but big preallocated arrays waste memory. + +Possible actions: + +1) leave it this way, a client does normaly does not have more than a few +ports + +2) replace the linked list of ports by a array of pointers which is +dynamicly kmalloced. When a port is added or deleted we can simply allocate +a new array, copy the corresponding pointers, and delete the old one. We +then only need a pointer to this array, and an integer that tells us how +much elements are in array. + +*/ + +/* return pointer to port structure - port is locked if found */ +client_port_t *snd_seq_port_use_ptr(client_t *client, int num) +{ + struct list_head *p; + client_port_t *port; + + if (client == NULL) + return NULL; + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + port = list_entry(p, client_port_t, list); + if (port->addr.port == num) { + if (port->closing) + break; /* deleting now */ + snd_use_lock_use(&port->use_lock); + read_unlock(&client->ports_lock); + return port; + } + } + read_unlock(&client->ports_lock); + return NULL; /* not found */ +} + + +/* search for the next port - port is locked if found */ +client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo) +{ + int num; + struct list_head *p; + client_port_t *port, *found; + + num = pinfo->addr.port; + found = NULL; + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + port = list_entry(p, client_port_t, list); + if (port->addr.port < num) + continue; + if (port->addr.port == num) { + found = port; + break; + } + if (found == NULL || port->addr.port < found->addr.port) + found = port; + } + if (found) { + if (found->closing) + found = NULL; + else + snd_use_lock_use(&found->use_lock); + } + read_unlock(&client->ports_lock); + return found; +} + + +/* initialize port_subs_info_t */ +static void port_subs_info_init(port_subs_info_t *grp) +{ + INIT_LIST_HEAD(&grp->list_head); + grp->count = 0; + grp->exclusive = 0; + rwlock_init(&grp->list_lock); + init_rwsem(&grp->list_mutex); + grp->open = NULL; + grp->close = NULL; +} + + +/* create a port, port number is returned (-1 on failure) */ +client_port_t *snd_seq_create_port(client_t *client, int port) +{ + unsigned long flags; + client_port_t *new_port; + struct list_head *l; + int num = -1; + + /* sanity check */ + snd_assert(client, return NULL); + + if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) { + snd_printk(KERN_WARNING "too many ports for client %d\n", client->number); + return NULL; + } + + /* create a new port */ + new_port = snd_kcalloc(sizeof(client_port_t), GFP_KERNEL); + if (! new_port) { + snd_printd("malloc failed for registering client port\n"); + return NULL; /* failure, out of memory */ + } + /* init port data */ + new_port->addr.client = client->number; + new_port->addr.port = -1; + new_port->owner = THIS_MODULE; + sprintf(new_port->name, "port-%d", num); + snd_use_lock_init(&new_port->use_lock); + port_subs_info_init(&new_port->c_src); + port_subs_info_init(&new_port->c_dest); + + num = port >= 0 ? port : 0; + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + if (p->addr.port > num) + break; + if (port < 0) /* auto-probe mode */ + num = p->addr.port + 1; + } + /* insert the new port */ + list_add_tail(&new_port->list, l); + client->num_ports++; + new_port->addr.port = num; /* store the port number in the port */ + write_unlock_irqrestore(&client->ports_lock, flags); + up(&client->ports_mutex); + sprintf(new_port->name, "port-%d", num); + + return new_port; +} + +/* */ +enum group_type_t { + SRC_LIST, DEST_LIST +}; + +static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); +static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); + + +static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp) +{ + client_port_t *p; + *cp = snd_seq_client_use_ptr(addr->client); + if (*cp) { + p = snd_seq_port_use_ptr(*cp, addr->port); + if (! p) { + snd_seq_client_unlock(*cp); + *cp = NULL; + } + return p; + } + return NULL; +} + +/* + * remove all subscribers on the list + * this is called from port_delete, for each src and dest list. + */ +static void clear_subscriber_list(client_t *client, client_port_t *port, + port_subs_info_t *grp, int grptype) +{ + struct list_head *p, *n; + + down_write(&grp->list_mutex); + list_for_each_safe(p, n, &grp->list_head) { + subscribers_t *subs; + client_t *c; + client_port_t *aport; + + if (grptype == SRC_LIST) { + subs = list_entry(p, subscribers_t, src_list); + aport = get_client_port(&subs->info.dest, &c); + } else { + subs = list_entry(p, subscribers_t, dest_list); + aport = get_client_port(&subs->info.sender, &c); + } + list_del(p); + unsubscribe_port(client, port, grp, &subs->info, 0); + if (!aport) { + /* looks like the connected port is being deleted. + * we decrease the counter, and when both ports are deleted + * remove the subscriber info + */ + if (atomic_dec_and_test(&subs->ref_count)) + kfree(subs); + } else { + /* ok we got the connected port */ + port_subs_info_t *agrp; + agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; + down_write(&agrp->list_mutex); + if (grptype == SRC_LIST) + list_del(&subs->dest_list); + else + list_del(&subs->src_list); + unsubscribe_port(c, aport, agrp, &subs->info, 1); + kfree(subs); + up_write(&agrp->list_mutex); + snd_seq_port_unlock(aport); + snd_seq_client_unlock(c); + } + } + up_write(&grp->list_mutex); +} + +/* delete port data */ +static int port_delete(client_t *client, client_port_t *port) +{ + /* set closing flag and wait for all port access are gone */ + port->closing = 1; + snd_use_lock_sync(&port->use_lock); + + /* clear subscribers info */ + clear_subscriber_list(client, port, &port->c_src, SRC_LIST); + clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); + + if (port->private_free) + port->private_free(port->private_data); + + snd_assert(port->c_src.count == 0,); + snd_assert(port->c_dest.count == 0,); + + kfree(port); + return 0; +} + + +/* delete a port with the given port id */ +int snd_seq_delete_port(client_t *client, int port) +{ + unsigned long flags; + struct list_head *l; + client_port_t *found = NULL; + + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + if (p->addr.port == port) { + /* ok found. delete from the list at first */ + list_del(l); + client->num_ports--; + found = p; + break; + } + } + write_unlock_irqrestore(&client->ports_lock, flags); + up(&client->ports_mutex); + if (found) + return port_delete(client, found); + else + return -ENOENT; +} + +/* delete the all ports belonging to the given client */ +int snd_seq_delete_all_ports(client_t *client) +{ + unsigned long flags; + struct list_head deleted_list, *p, *n; + + /* move the port list to deleted_list, and + * clear the port list in the client data. + */ + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + if (! list_empty(&client->ports_list_head)) { + __list_add(&deleted_list, + client->ports_list_head.prev, + client->ports_list_head.next); + INIT_LIST_HEAD(&client->ports_list_head); + } else { + INIT_LIST_HEAD(&deleted_list); + } + client->num_ports = 0; + write_unlock_irqrestore(&client->ports_lock, flags); + + /* remove each port in deleted_list */ + list_for_each_safe(p, n, &deleted_list) { + client_port_t *port = list_entry(p, client_port_t, list); + list_del(p); + snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); + port_delete(client, port); + } + up(&client->ports_mutex); + return 0; +} + +/* set port info fields */ +int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info) +{ + snd_assert(port && info, return -EINVAL); + + /* set port name */ + if (info->name[0]) { + strncpy(port->name, info->name, sizeof(port->name)-1); + port->name[sizeof(port->name)-1] = '\0'; + } + + /* set capabilities */ + port->capability = info->capability; + + /* get port type */ + port->type = info->type; + + /* information about supported channels/voices */ + port->midi_channels = info->midi_channels; + port->midi_voices = info->midi_voices; + port->synth_voices = info->synth_voices; + + return 0; +} + +/* get port info fields */ +int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info) +{ + snd_assert(port && info, return -EINVAL); + + /* get port name */ + strncpy(info->name, port->name, sizeof(info->name)); + + /* get capabilities */ + info->capability = port->capability; + + /* get port type */ + info->type = port->type; + + /* information about supported channels/voices */ + info->midi_channels = port->midi_channels; + info->midi_voices = port->midi_voices; + info->synth_voices = port->synth_voices; + + /* get subscriber counts */ + info->read_use = port->c_src.count; + info->write_use = port->c_dest.count; + + return 0; +} + + + +/* + * call callback functions (if any): + * the callbacks are invoked only when the first (for connection) or + * the last subscription (for disconnection) is done. Second or later + * subscription results in increment of counter, but no callback is + * invoked. + * This feature is useful if these callbacks are associated with + * initialization or termination of devices (see seq_midi.c). + * + * If callback_all option is set, the callback function is invoked + * at each connnection/disconnection. + */ + +static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, + snd_seq_port_subscribe_t *info, int send_ack) +{ + int err = 0; + + if (!try_module_get(port->owner)) + return -EFAULT; + grp->count++; + if (grp->open && (port->callback_all || grp->count == 1)) { + err = grp->open(port->private_data, info); + if (err < 0) { + module_put(port->owner); + grp->count--; + } + } + if (err >= 0 && send_ack && client->type == USER_CLIENT) + snd_seq_client_notify_subscription(port->addr.client, port->addr.port, + info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + + return err; +} + +static int unsubscribe_port(client_t *client, client_port_t *port, + port_subs_info_t *grp, + snd_seq_port_subscribe_t *info, int send_ack) +{ + int err = 0; + + if (! grp->count) + return -EINVAL; + grp->count--; + if (grp->close && (port->callback_all || grp->count == 0)) + err = grp->close(port->private_data, info); + if (send_ack && client->type == USER_CLIENT) + snd_seq_client_notify_subscription(port->addr.client, port->addr.port, + info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + module_put(port->owner); + return err; +} + + + +/* check if both addresses are identical */ +static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s) +{ + return (r->client == s->client) && (r->port == s->port); +} + +/* check the two subscribe info match */ +/* if flags is zero, checks only sender and destination addresses */ +static int match_subs_info(snd_seq_port_subscribe_t *r, + snd_seq_port_subscribe_t *s) +{ + if (addr_match(&r->sender, &s->sender) && + addr_match(&r->dest, &s->dest)) { + if (r->flags && r->flags == s->flags) + return r->queue == s->queue; + else if (! r->flags) + return 1; + } + return 0; +} + + +/* connect two ports */ +int snd_seq_port_connect(client_t *connector, + client_t *src_client, client_port_t *src_port, + client_t *dest_client, client_port_t *dest_port, + snd_seq_port_subscribe_t *info) +{ + port_subs_info_t *src = &src_port->c_src; + port_subs_info_t *dest = &dest_port->c_dest; + subscribers_t *subs; + struct list_head *p; + int err, src_called = 0; + unsigned long flags; + int exclusive; + + subs = snd_kcalloc(sizeof(*subs), GFP_KERNEL); + if (! subs) + return -ENOMEM; + + subs->info = *info; + atomic_set(&subs->ref_count, 2); + + down_write(&src->list_mutex); + down_write(&dest->list_mutex); + + exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; + err = -EBUSY; + if (exclusive) { + if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) + goto __error; + } else { + if (src->exclusive || dest->exclusive) + goto __error; + /* check whether already exists */ + list_for_each(p, &src->list_head) { + subscribers_t *s = list_entry(p, subscribers_t, src_list); + if (match_subs_info(info, &s->info)) + goto __error; + } + list_for_each(p, &dest->list_head) { + subscribers_t *s = list_entry(p, subscribers_t, dest_list); + if (match_subs_info(info, &s->info)) + goto __error; + } + } + + if ((err = subscribe_port(src_client, src_port, src, info, + connector->number != src_client->number)) < 0) + goto __error; + src_called = 1; + + if ((err = subscribe_port(dest_client, dest_port, dest, info, + connector->number != dest_client->number)) < 0) + goto __error; + + /* add to list */ + write_lock_irqsave(&src->list_lock, flags); + // write_lock(&dest->list_lock); // no other lock yet + list_add_tail(&subs->src_list, &src->list_head); + list_add_tail(&subs->dest_list, &dest->list_head); + // write_unlock(&dest->list_lock); // no other lock yet + write_unlock_irqrestore(&src->list_lock, flags); + + src->exclusive = dest->exclusive = exclusive; + + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return 0; + + __error: + if (src_called) + unsubscribe_port(src_client, src_port, src, info, + connector->number != src_client->number); + kfree(subs); + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return err; +} + + +/* remove the connection */ +int snd_seq_port_disconnect(client_t *connector, + client_t *src_client, client_port_t *src_port, + client_t *dest_client, client_port_t *dest_port, + snd_seq_port_subscribe_t *info) +{ + port_subs_info_t *src = &src_port->c_src; + port_subs_info_t *dest = &dest_port->c_dest; + subscribers_t *subs; + struct list_head *p; + int err = -ENOENT; + unsigned long flags; + + down_write(&src->list_mutex); + down_write(&dest->list_mutex); + + /* look for the connection */ + list_for_each(p, &src->list_head) { + subs = list_entry(p, subscribers_t, src_list); + if (match_subs_info(info, &subs->info)) { + write_lock_irqsave(&src->list_lock, flags); + // write_lock(&dest->list_lock); // no lock yet + list_del(&subs->src_list); + list_del(&subs->dest_list); + // write_unlock(&dest->list_lock); + write_unlock_irqrestore(&src->list_lock, flags); + src->exclusive = dest->exclusive = 0; + unsubscribe_port(src_client, src_port, src, info, + connector->number != src_client->number); + unsubscribe_port(dest_client, dest_port, dest, info, + connector->number != dest_client->number); + kfree(subs); + err = 0; + break; + } + } + + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return err; +} + + +/* get matched subscriber */ +subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, + snd_seq_addr_t *dest_addr) +{ + struct list_head *p; + subscribers_t *s, *found = NULL; + + down_read(&src_grp->list_mutex); + list_for_each(p, &src_grp->list_head) { + s = list_entry(p, subscribers_t, src_list); + if (addr_match(dest_addr, &s->info.dest)) { + found = s; + break; + } + } + up_read(&src_grp->list_mutex); + return found; +} + +/* + * Attach a device driver that wants to receive events from the + * sequencer. Returns the new port number on success. + * A driver that wants to receive the events converted to midi, will + * use snd_seq_midisynth_register_port(). + */ +/* exported */ +int snd_seq_event_port_attach(int client, + snd_seq_port_callback_t *pcbp, + int cap, int type, int midi_channels, + int midi_voices, char *portname) +{ + snd_seq_port_info_t portinfo; + int ret; + + /* Set up the port */ + memset(&portinfo, 0, sizeof(portinfo)); + portinfo.addr.client = client; + if (portname) + strncpy(portinfo.name, portname, sizeof(portinfo.name)); + else + sprintf(portinfo.name, "Unamed port"); + + portinfo.capability = cap; + portinfo.type = type; + portinfo.kernel = pcbp; + portinfo.midi_channels = midi_channels; + portinfo.midi_voices = midi_voices; + + /* Create it */ + ret = snd_seq_kernel_client_ctl(client, + SNDRV_SEQ_IOCTL_CREATE_PORT, + &portinfo); + + if (ret >= 0) + ret = portinfo.addr.port; + + return ret; +} + + +/* + * Detach the driver from a port. + */ +/* exported */ +int snd_seq_event_port_detach(int client, int port) +{ + snd_seq_port_info_t portinfo; + int err; + + memset(&portinfo, 0, sizeof(portinfo)); + portinfo.addr.client = client; + portinfo.addr.port = port; + err = snd_seq_kernel_client_ctl(client, + SNDRV_SEQ_IOCTL_DELETE_PORT, + &portinfo); + + return err; +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_ports.h linux/sound/core/seq/seq_ports.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_ports.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_ports.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,125 @@ +/* + * ALSA sequencer Ports + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_PORTS_H +#define __SND_SEQ_PORTS_H + +#include +#include "seq_lock.h" + +/* list of 'exported' ports */ + +/* Client ports that are not exported are still accessible, but are + anonymous ports. + + If a port supports SUBSCRIPTION, that port can send events to all + subscribersto a special address, with address + (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all + recipients that are registered in the subscription list. A typical + application for these SUBSCRIPTION events is handling of incoming MIDI + data. The port doesn't 'know' what other clients are interested in this + message. If for instance a MIDI recording application would like to receive + the events from that port, it will first have to subscribe with that port. + +*/ + +typedef struct subscribers_t { + snd_seq_port_subscribe_t info; /* additional info */ + struct list_head src_list; /* link of sources */ + struct list_head dest_list; /* link of destinations */ + atomic_t ref_count; +} subscribers_t; + +typedef struct port_subs_info_t { + struct list_head list_head; /* list of subscribed ports */ + unsigned int count; /* count of subscribers */ + unsigned int exclusive: 1; /* exclusive mode */ + struct rw_semaphore list_mutex; + rwlock_t list_lock; + snd_seq_kernel_port_open_t *open; + snd_seq_kernel_port_close_t *close; +} port_subs_info_t; + +typedef struct client_port_t { + + snd_seq_addr_t addr; /* client/port number */ + struct module *owner; /* owner of this port */ + char name[64]; /* port name */ + struct list_head list; /* port list */ + snd_use_lock_t use_lock; + + /* subscribers */ + port_subs_info_t c_src; /* read (sender) list */ + port_subs_info_t c_dest; /* write (dest) list */ + + snd_seq_kernel_port_input_t *event_input; + snd_seq_kernel_port_private_free_t *private_free; + void *private_data; + unsigned int callback_all : 1; + unsigned int closing : 1; + + /* capability, inport, output, sync */ + unsigned int capability; /* port capability bits */ + unsigned int type; /* port type bits */ + + /* supported channels */ + int midi_channels; + int midi_voices; + int synth_voices; + +} client_port_t; + +/* return pointer to port structure and lock port */ +client_port_t *snd_seq_port_use_ptr(client_t *client, int num); + +/* search for next port - port is locked if found */ +client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo); + +/* unlock the port */ +#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock) + +/* create a port, port number is returned (-1 on failure) */ +client_port_t *snd_seq_create_port(client_t *client, int port_index); + +/* delete a port */ +int snd_seq_delete_port(client_t *client, int port); + +/* delete all ports */ +int snd_seq_delete_all_ports(client_t *client); + +/* set port info fields */ +int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info); + +/* get port info fields */ +int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info); + +/* add subscriber to subscription list */ +int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info); + +/* remove subscriber from subscription list */ +int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info); + +/* subscribe port */ +int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info); + +/* get matched subscriber */ +subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_prioq.c linux/sound/core/seq/seq_prioq.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_prioq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_prioq.c 2002-11-04 02:11:39.000000000 -0700 @@ -0,0 +1,449 @@ +/* + * ALSA sequencer Priority Queue + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "seq_timer.h" +#include "seq_prioq.h" + + +/* Implementation is a simple linked list for now... + + This priority queue orders the events on timestamp. For events with an + equeal timestamp the queue behaves as a FIFO. + + * + * +-------+ + * Head --> | first | + * +-------+ + * |next + * +-----v-+ + * | | + * +-------+ + * | + * +-----v-+ + * | | + * +-------+ + * | + * +-----v-+ + * Tail --> | last | + * +-------+ + * + + */ + + + +/* create new prioq (constructor) */ +prioq_t *snd_seq_prioq_new(void) +{ + prioq_t *f; + + f = snd_kcalloc(sizeof(prioq_t), GFP_KERNEL); + if (f == NULL) { + snd_printd("oops: malloc failed for snd_seq_prioq_new()\n"); + return NULL; + } + + spin_lock_init(&f->lock); + f->head = NULL; + f->tail = NULL; + f->cells = 0; + + return f; +} + +/* delete prioq (destructor) */ +void snd_seq_prioq_delete(prioq_t **fifo) +{ + prioq_t *f = *fifo; + *fifo = NULL; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n"); + return; + } + + /* release resources...*/ + /*....................*/ + + if (f->cells > 0) { + /* drain prioQ */ + while (f->cells > 0) + snd_seq_cell_free(snd_seq_prioq_cell_out(f)); + } + + kfree(f); +} + + + + +/* compare timestamp between events */ +/* return 1 if a >= b; 0 */ +static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b) +{ + if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) { + /* compare ticks */ + return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick)); + } else { + /* compare real time */ + return (snd_seq_compare_real_time(&a->time.time, &b->time.time)); + } +} + +/* compare timestamp between events */ +/* return negative if a < b; + * zero if a = b; + * positive if a > b; + */ +static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b) +{ + if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) { + /* compare ticks */ + if (a->time.tick > b->time.tick) + return 1; + else if (a->time.tick == b->time.tick) + return 0; + else + return -1; + } else { + /* compare real time */ + if (a->time.time.tv_sec > b->time.time.tv_sec) + return 1; + else if (a->time.time.tv_sec == b->time.time.tv_sec) { + if (a->time.time.tv_nsec > b->time.time.tv_nsec) + return 1; + else if (a->time.time.tv_nsec == b->time.time.tv_nsec) + return 0; + else + return -1; + } else + return -1; + } +} + +/* enqueue cell to prioq */ +int snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell) +{ + snd_seq_event_cell_t *cur, *prev; + unsigned long flags; + int count; + int prior; + + snd_assert(f, return -EINVAL); + snd_assert(cell, return -EINVAL); + + /* check flags */ + prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK); + + spin_lock_irqsave(&f->lock, flags); + + /* check if this element needs to inserted at the end (ie. ordered + data is inserted) This will be very likeley if a sequencer + application or midi file player is feeding us (sequential) data */ + if (f->tail && !prior) { + if (compare_timestamp(&cell->event, &f->tail->event)) { + /* add new cell to tail of the fifo */ + f->tail->next = cell; + f->tail = cell; + cell->next = NULL; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + return 0; + } + } + /* traverse list of elements to find the place where the new cell is + to be inserted... Note that this is a order n process ! */ + + prev = NULL; /* previous cell */ + cur = f->head; /* cursor */ + + count = 10000; /* FIXME: enough big, isn't it? */ + while (cur != NULL) { + /* compare timestamps */ + int rel = compare_timestamp_rel(&cell->event, &cur->event); + if (rel < 0) + /* new cell has earlier schedule time, */ + break; + else if (rel == 0 && prior) + /* equal schedule time and prior to others */ + break; + /* new cell has equal or larger schedule time, */ + /* move cursor to next cell */ + prev = cur; + cur = cur->next; + if (! --count) { + spin_unlock_irqrestore(&f->lock, flags); + snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n"); + return -EINVAL; + } + } + + /* insert it before cursor */ + if (prev != NULL) + prev->next = cell; + cell->next = cur; + + if (f->head == cur) /* this is the first cell, set head to it */ + f->head = cell; + if (cur == NULL) /* reached end of the list */ + f->tail = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + return 0; +} + +/* dequeue cell from prioq */ +snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return NULL; + } + spin_lock_irqsave(&f->lock, flags); + + cell = f->head; + if (cell) { + f->head = cell->next; + + /* reset tail if this was the last element */ + if (f->tail == cell) + f->tail = NULL; + + cell->next = NULL; + f->cells--; + } + + spin_unlock_irqrestore(&f->lock, flags); + return cell; +} + +/* return number of events available in prioq */ +int snd_seq_prioq_avail(prioq_t * f) +{ + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return 0; + } + return f->cells; +} + + +/* peek at cell at the head of the prioq */ +snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f) +{ + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return NULL; + } + return f->head; +} + + +static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp) +{ + if (cell->event.source.client == client || + cell->event.dest.client == client) + return 1; + if (!timestamp) + return 0; + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + if (cell->event.time.tick) + return 1; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + if (cell->event.time.time.tv_sec || + cell->event.time.time.tv_nsec) + return 1; + break; + } + return 0; +} + +/* remove cells for left client */ +void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp) +{ + register snd_seq_event_cell_t *cell, *next; + unsigned long flags; + snd_seq_event_cell_t *prev = NULL; + snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext; + + /* collect all removed cells */ + spin_lock_irqsave(&f->lock, flags); + cell = f->head; + while (cell) { + next = cell->next; + if (prioq_match(cell, client, timestamp)) { + /* remove cell from prioq */ + if (cell == f->head) { + f->head = cell->next; + } else { + prev->next = cell->next; + } + if (cell == f->tail) + f->tail = cell->next; + f->cells--; + /* add cell to free list */ + cell->next = NULL; + if (freefirst == NULL) { + freefirst = cell; + } else { + freeprev->next = cell; + } + freeprev = cell; + } else { +#if 0 + printk("type = %i, source = %i, dest = %i, client = %i\n", + cell->event.type, + cell->event.source.client, + cell->event.dest.client, + client); +#endif + prev = cell; + } + cell = next; + } + spin_unlock_irqrestore(&f->lock, flags); + + /* remove selected cells */ + while (freefirst) { + freenext = freefirst->next; + snd_seq_cell_free(freefirst); + freefirst = freenext; + } +} + +static int prioq_remove_match(snd_seq_remove_events_t *info, + snd_seq_event_t *ev) +{ + int res; + + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) { + if (ev->dest.client != info->dest.client || + ev->dest.port != info->dest.port) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) { + if (! snd_seq_ev_is_channel_type(ev)) + return 0; + /* data.note.channel and data.control.channel are identical */ + if (ev->data.note.channel != info->channel) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) + res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick); + else + res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); + if (!res) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) + res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick); + else + res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); + if (res) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) { + if (ev->type != info->type) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) { + /* Do not remove off events */ + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEOFF: + /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */ + return 0; + default: + break; + } + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) { + if (info->tag != ev->tag) + return 0; + } + + return 1; +} + +/* remove cells matching remove criteria */ +void snd_seq_prioq_remove_events(prioq_t * f, int client, + snd_seq_remove_events_t *info) +{ + register snd_seq_event_cell_t *cell, *next; + unsigned long flags; + snd_seq_event_cell_t *prev = NULL; + snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext; + + /* collect all removed cells */ + spin_lock_irqsave(&f->lock, flags); + cell = f->head; + + while (cell) { + next = cell->next; + if (cell->event.source.client == client && + prioq_remove_match(info, &cell->event)) { + + /* remove cell from prioq */ + if (cell == f->head) { + f->head = cell->next; + } else { + prev->next = cell->next; + } + + if (cell == f->tail) + f->tail = cell->next; + f->cells--; + + /* add cell to free list */ + cell->next = NULL; + if (freefirst == NULL) { + freefirst = cell; + } else { + freeprev->next = cell; + } + + freeprev = cell; + } else { + prev = cell; + } + cell = next; + } + spin_unlock_irqrestore(&f->lock, flags); + + /* remove selected cells */ + while (freefirst) { + freenext = freefirst->next; + snd_seq_cell_free(freefirst); + freefirst = freenext; + } +} + + diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_prioq.h linux/sound/core/seq/seq_prioq.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_prioq.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_prioq.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,62 @@ +/* + * ALSA sequencer Priority Queue + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_PRIOQ_H +#define __SND_SEQ_PRIOQ_H + +#include "seq_memory.h" + + +/* === PRIOQ === */ + +typedef struct { + snd_seq_event_cell_t* head; /* pointer to head of prioq */ + snd_seq_event_cell_t* tail; /* pointer to tail of prioq */ + int cells; + spinlock_t lock; +} prioq_t; + + +/* create new prioq (constructor) */ +extern prioq_t *snd_seq_prioq_new(void); + +/* delete prioq (destructor) */ +extern void snd_seq_prioq_delete(prioq_t **fifo); + +/* enqueue cell to prioq */ +extern int snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell); + +/* dequeue cell from prioq */ +extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f); + +/* return number of events available in prioq */ +extern int snd_seq_prioq_avail(prioq_t *f); + +/* peek at cell at the head of the prioq */ +extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f); + +/* client left queue */ +extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp); + +/* Remove events */ +void snd_seq_prioq_remove_events(prioq_t * f, int client, + snd_seq_remove_events_t *info); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_queue.c linux/sound/core/seq/seq_queue.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_queue.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_queue.c 2003-02-05 05:49:16.000000000 -0700 @@ -0,0 +1,785 @@ +/* + * ALSA sequencer Timing queue handling + * Copyright (c) 1998-1999 by Frank van de Pol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * MAJOR CHANGES + * Nov. 13, 1999 Takashi Iwai + * - Queues are allocated dynamically via ioctl. + * - When owner client is deleted, all owned queues are deleted, too. + * - Owner of unlocked queue is kept unmodified even if it is + * manipulated by other clients. + * - Owner field in SET_QUEUE_OWNER ioctl must be identical with the + * caller client. i.e. Changing owner to a third client is not + * allowed. + * + * Aug. 30, 2000 Takashi Iwai + * - Queues are managed in static array again, but with better way. + * The API itself is identical. + * - The queue is locked when queue_t pinter is returned via + * queueptr(). This pointer *MUST* be released afterward by + * queuefree(ptr). + * - Addition of experimental sync support. + */ + +#include +#include +#include +#include + +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_clientmgr.h" +#include "seq_fifo.h" +#include "seq_timer.h" +#include "seq_info.h" + +/* list of allocated queues */ +static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES]; +static spinlock_t queue_list_lock = SPIN_LOCK_UNLOCKED; +/* number of queues allocated */ +static int num_queues; + +int snd_seq_queue_get_cur_queues(void) +{ + return num_queues; +} + +/*----------------------------------------------------------------*/ + +/* assign queue id and insert to list */ +static int queue_list_add(queue_t *q) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&queue_list_lock, flags); + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if (! queue_list[i]) { + queue_list[i] = q; + q->queue = i; + num_queues++; + spin_unlock_irqrestore(&queue_list_lock, flags); + return i; + } + } + spin_unlock_irqrestore(&queue_list_lock, flags); + return -1; +} + +static queue_t *queue_list_remove(int id, int client) +{ + queue_t *q; + unsigned long flags; + + spin_lock_irqsave(&queue_list_lock, flags); + q = queue_list[id]; + if (q) { + spin_lock(&q->owner_lock); + if (q->owner == client) { + /* found */ + q->klocked = 1; + spin_unlock(&q->owner_lock); + queue_list[id] = NULL; + num_queues--; + spin_unlock_irqrestore(&queue_list_lock, flags); + return q; + } + spin_unlock(&q->owner_lock); + } + spin_unlock_irqrestore(&queue_list_lock, flags); + return NULL; +} + +/*----------------------------------------------------------------*/ + +/* create new queue (constructor) */ +static queue_t *queue_new(int owner, int locked) +{ + queue_t *q; + + q = snd_kcalloc(sizeof(queue_t), GFP_KERNEL); + if (q == NULL) { + snd_printd("malloc failed for snd_seq_queue_new()\n"); + return NULL; + } + + spin_lock_init(&q->owner_lock); + spin_lock_init(&q->check_lock); + init_MUTEX(&q->timer_mutex); + snd_use_lock_init(&q->use_lock); + q->queue = -1; + + q->tickq = snd_seq_prioq_new(); + q->timeq = snd_seq_prioq_new(); + q->timer = snd_seq_timer_new(); + if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) { + snd_seq_prioq_delete(&q->tickq); + snd_seq_prioq_delete(&q->timeq); + snd_seq_timer_delete(&q->timer); + kfree(q); + return NULL; + } + + q->owner = owner; + q->locked = locked; + q->klocked = 0; + + return q; +} + +/* delete queue (destructor) */ +static void queue_delete(queue_t *q) +{ + /* stop and release the timer */ + snd_seq_timer_stop(q->timer); + snd_seq_timer_close(q); + /* wait until access free */ + snd_use_lock_sync(&q->use_lock); + /* release resources... */ + snd_seq_prioq_delete(&q->tickq); + snd_seq_prioq_delete(&q->timeq); + snd_seq_timer_delete(&q->timer); + + kfree(q); +} + + +/*----------------------------------------------------------------*/ + +/* setup queues */ +int __init snd_seq_queues_init(void) +{ + /* + memset(queue_list, 0, sizeof(queue_list)); + num_queues = 0; + */ + return 0; +} + +/* delete all existing queues */ +void __exit snd_seq_queues_delete(void) +{ + int i; + + /* clear list */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if (queue_list[i]) + queue_delete(queue_list[i]); + } +} + +/* allocate a new queue - + * return queue index value or negative value for error + */ +int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags) +{ + queue_t *q; + + q = queue_new(client, locked); + if (q == NULL) + return -ENOMEM; + q->info_flags = info_flags; + if (queue_list_add(q) < 0) { + queue_delete(q); + return -ENOMEM; + } + snd_seq_queue_use(q->queue, client, 1); /* use this queue */ + return q->queue; +} + +/* delete a queue - queue must be owned by the client */ +int snd_seq_queue_delete(int client, int queueid) +{ + queue_t *q; + + if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) + return -EINVAL; + q = queue_list_remove(queueid, client); + if (q == NULL) + return -EINVAL; + queue_delete(q); + + return 0; +} + + +/* return pointer to queue structure for specified id */ +queue_t *queueptr(int queueid) +{ + queue_t *q; + unsigned long flags; + + if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) + return NULL; + spin_lock_irqsave(&queue_list_lock, flags); + q = queue_list[queueid]; + if (q) + snd_use_lock_use(&q->use_lock); + spin_unlock_irqrestore(&queue_list_lock, flags); + return q; +} + +/* return the (first) queue matching with the specified name */ +queue_t *snd_seq_queue_find_name(char *name) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) != NULL) { + if (strncpy(q->name, name, sizeof(q->name)) == 0) + return q; + queuefree(q); + } + } + return NULL; +} + + +/* -------------------------------------------------------- */ + +void snd_seq_check_queue(queue_t *q, int atomic, int hop) +{ + unsigned long flags; + snd_seq_event_cell_t *cell; + + if (q == NULL) + return; + + /* make this function non-reentrant */ + spin_lock_irqsave(&q->check_lock, flags); + if (q->check_blocked) { + q->check_again = 1; + spin_unlock_irqrestore(&q->check_lock, flags); + return; /* other thread is already checking queues */ + } + q->check_blocked = 1; + spin_unlock_irqrestore(&q->check_lock, flags); + + __again: + /* Process tick queue... */ + while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) { + if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) { + cell = snd_seq_prioq_cell_out(q->tickq); + if (cell) + snd_seq_dispatch_event(cell, atomic, hop); + } else { + /* event remains in the queue */ + break; + } + } + + + /* Process time queue... */ + while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) { + if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) { + cell = snd_seq_prioq_cell_out(q->timeq); + if (cell) + snd_seq_dispatch_event(cell, atomic, hop); + } else { + /* event remains in the queue */ + break; + } + } + + /* free lock */ + spin_lock_irqsave(&q->check_lock, flags); + if (q->check_again) { + q->check_again = 0; + spin_unlock_irqrestore(&q->check_lock, flags); + goto __again; + } + q->check_blocked = 0; + spin_unlock_irqrestore(&q->check_lock, flags); +} + + +/* enqueue a event to singe queue */ +int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop) +{ + int dest, err; + queue_t *q; + + snd_assert(cell != NULL, return -EINVAL); + dest = cell->event.queue; /* destination queue */ + q = queueptr(dest); + if (q == NULL) + return -EINVAL; + /* handle relative time stamps, convert them into absolute */ + if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) { + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + cell->event.time.tick += q->timer->tick.cur_tick; + break; + + case SNDRV_SEQ_TIME_STAMP_REAL: + snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time); + break; + } + cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK; + cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS; + } + /* enqueue event in the real-time or midi queue */ + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + err = snd_seq_prioq_cell_in(q->tickq, cell); + break; + + case SNDRV_SEQ_TIME_STAMP_REAL: + default: + err = snd_seq_prioq_cell_in(q->timeq, cell); + break; + } + + if (err < 0) { + queuefree(q); /* unlock */ + return err; + } + + /* trigger dispatching */ + snd_seq_check_queue(q, atomic, hop); + + queuefree(q); /* unlock */ + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static inline int check_access(queue_t *q, int client) +{ + return (q->owner == client) || (!q->locked && !q->klocked); +} + +/* check if the client has permission to modify queue parameters. + * if it does, lock the queue + */ +static int queue_access_lock(queue_t *q, int client) +{ + unsigned long flags; + int access_ok; + + spin_lock_irqsave(&q->owner_lock, flags); + access_ok = check_access(q, client); + if (access_ok) + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + return access_ok; +} + +/* unlock the queue */ +static inline void queue_access_unlock(queue_t *q) +{ + unsigned long flags; + + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 0; + spin_unlock_irqrestore(&q->owner_lock, flags); +} + +/* exported - only checking permission */ +int snd_seq_queue_check_access(int queueid, int client) +{ + queue_t *q = queueptr(queueid); + int access_ok; + unsigned long flags; + + if (! q) + return 0; + spin_lock_irqsave(&q->owner_lock, flags); + access_ok = check_access(q, client); + spin_unlock_irqrestore(&q->owner_lock, flags); + queuefree(q); + return access_ok; +} + +/*----------------------------------------------------------------*/ + +/* + * change queue's owner and permission + */ +int snd_seq_queue_set_owner(int queueid, int client, int locked) +{ + queue_t *q = queueptr(queueid); + + if (q == NULL) + return -EINVAL; + + if (! queue_access_lock(q, client)) { + queuefree(q); + return -EPERM; + } + + q->locked = locked ? 1 : 0; + q->owner = client; + queue_access_unlock(q); + queuefree(q); + + return 0; +} + + +/*----------------------------------------------------------------*/ + +/* open timer - + * q->use mutex should be down before calling this function to avoid + * confliction with snd_seq_queue_use() + */ +int snd_seq_queue_timer_open(int queueid) +{ + int result = 0; + queue_t *queue; + seq_timer_t *tmr; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + tmr = queue->timer; + if ((result = snd_seq_timer_open(queue)) < 0) { + snd_seq_timer_defaults(tmr); + result = snd_seq_timer_open(queue); + } + queuefree(queue); + return result; +} + +/* close timer - + * q->use mutex should be down before calling this function + */ +int snd_seq_queue_timer_close(int queueid) +{ + queue_t *queue; + seq_timer_t *tmr; + int result = 0; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + tmr = queue->timer; + snd_seq_timer_close(queue); + queuefree(queue); + return result; +} + +/* change queue tempo and ppq */ +int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info) +{ + queue_t *q = queueptr(queueid); + int result; + + if (q == NULL) + return -EINVAL; + if (! queue_access_lock(q, client)) { + queuefree(q); + return -EPERM; + } + + result = snd_seq_timer_set_tempo(q->timer, info->tempo); + if (result >= 0) + result = snd_seq_timer_set_ppq(q->timer, info->ppq); + if (result >= 0 && info->skew_base > 0) + result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base); + queue_access_unlock(q); + queuefree(q); + return result; +} + + +/* use or unuse this queue - + * if it is the first client, starts the timer. + * if it is not longer used by any clients, stop the timer. + */ +int snd_seq_queue_use(int queueid, int client, int use) +{ + queue_t *queue; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + down(&queue->timer_mutex); + if (use) { + if (!test_and_set_bit(client, queue->clients_bitmap)) + queue->clients++; + } else { + if (test_and_clear_bit(client, queue->clients_bitmap)) + queue->clients--; + } + if (queue->clients) { + if (use && queue->clients == 1) + snd_seq_timer_defaults(queue->timer); + snd_seq_timer_open(queue); + } else { + snd_seq_timer_close(queue); + } + up(&queue->timer_mutex); + queuefree(queue); + return 0; +} + +/* + * check if queue is used by the client + * return negative value if the queue is invalid. + * return 0 if not used, 1 if used. + */ +int snd_seq_queue_is_used(int queueid, int client) +{ + queue_t *q; + int result; + + q = queueptr(queueid); + if (q == NULL) + return -EINVAL; /* invalid queue */ + result = test_bit(client, q->clients_bitmap) ? 1 : 0; + queuefree(q); + return result; +} + + +/*----------------------------------------------------------------*/ + +/* notification that client has left the system - + * stop the timer on all queues owned by this client + */ +void snd_seq_queue_client_termination(int client) +{ + unsigned long flags; + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + spin_lock_irqsave(&q->owner_lock, flags); + if (q->owner == client) + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + if (q->owner == client) { + if (q->timer->running) + snd_seq_timer_stop(q->timer); + snd_seq_timer_reset(q->timer); + } + queuefree(q); + } +} + +/* final stage notification - + * remove cells for no longer exist client (for non-owned queue) + * or delete this queue (for owned queue) + */ +void snd_seq_queue_client_leave(int client) +{ + int i; + queue_t *q; + + /* delete own queues from queue list */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queue_list_remove(i, client)) != NULL) + queue_delete(q); + } + + /* remove cells from existing queues - + * they are not owned by this client + */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + if (test_bit(client, q->clients_bitmap)) { + snd_seq_prioq_leave(q->tickq, client, 0); + snd_seq_prioq_leave(q->timeq, client, 0); + snd_seq_queue_use(q->queue, client, 0); + } + queuefree(q); + } +} + + + +/*----------------------------------------------------------------*/ + +/* remove cells from all queues */ +void snd_seq_queue_client_leave_cells(int client) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + snd_seq_prioq_leave(q->tickq, client, 0); + snd_seq_prioq_leave(q->timeq, client, 0); + queuefree(q); + } +} + +/* remove cells based on flush criteria */ +void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + if (test_bit(client, q->clients_bitmap) && + (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) || + q->queue == info->queue)) { + snd_seq_prioq_remove_events(q->tickq, client, info); + snd_seq_prioq_remove_events(q->timeq, client, info); + } + queuefree(q); + } +} + +/*----------------------------------------------------------------*/ + +/* + * send events to all subscribed ports + */ +static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop) +{ + snd_seq_event_t sev; + + sev = *ev; + + sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS; + sev.time.tick = q->timer->tick.cur_tick; + sev.queue = q->queue; + sev.data.queue.queue = q->queue; + + if (from_timer_port) { + /* broadcast events from Timer port */ + sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; + sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop); + } +} + +/* + * process a received queue-control event. + * this function is exported for seq_sync.c. + */ +void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop) +{ + switch (ev->type) { + case SNDRV_SEQ_EVENT_START: + snd_seq_prioq_leave(q->tickq, ev->source.client, 1); + snd_seq_prioq_leave(q->timeq, ev->source.client, 1); + if (! snd_seq_timer_start(q->timer)) + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_CONTINUE: + if (! snd_seq_timer_continue(q->timer)) + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_STOP: + snd_seq_timer_stop(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_TEMPO: + snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_SETPOS_TICK: + if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) { + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + + case SNDRV_SEQ_EVENT_SETPOS_TIME: + if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) { + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + case SNDRV_SEQ_EVENT_QUEUE_SKEW: + if (snd_seq_timer_set_skew(q->timer, + ev->data.queue.param.skew.value, + ev->data.queue.param.skew.base) == 0) { + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + } +} + + +/* + * Queue control via timer control port: + * this function is exported as a callback of timer port. + */ +int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop) +{ + queue_t *q; + + snd_assert(ev != NULL, return -EINVAL); + q = queueptr(ev->data.queue.queue); + + if (q == NULL) + return -EINVAL; + + if (! queue_access_lock(q, ev->source.client)) { + queuefree(q); + return -EPERM; + } + + snd_seq_queue_process_event(q, ev, 1, atomic, hop); + + queue_access_unlock(q); + queuefree(q); + return 0; +} + + +/*----------------------------------------------------------------*/ + +/* exported to seq_info.c */ +void snd_seq_info_queues_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + int i, bpm; + queue_t *q; + seq_timer_t *tmr; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + + tmr = q->timer; + if (tmr->tempo) + bpm = 60000000 / tmr->tempo; + else + bpm = 0; + + snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name); + snd_iprintf(buffer, "owned by client : %d\n", q->owner); + snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free"); + snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq)); + snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq)); + snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped"); + snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq); + snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo); + snd_iprintf(buffer, "current BPM : %d\n", bpm); + snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec); + snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick); + snd_iprintf(buffer, "\n"); + queuefree(q); + } +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_queue.h linux/sound/core/seq/seq_queue.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_queue.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_queue.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,140 @@ +/* + * ALSA sequencer Queue handling + * Copyright (c) 1998-1999 by Frank van de Pol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_QUEUE_H +#define __SND_SEQ_QUEUE_H + +#include "seq_memory.h" +#include "seq_prioq.h" +#include "seq_timer.h" +#include "seq_lock.h" +#include +#include +#include + +#define SEQ_QUEUE_NO_OWNER (-1) + +struct _snd_seq_queue { + int queue; /* queue number */ + + char name[64]; /* name of this queue */ + + prioq_t *tickq; /* midi tick event queue */ + prioq_t *timeq; /* real-time event queue */ + + seq_timer_t *timer; /* time keeper for this queue */ + int owner; /* client that 'owns' the timer */ + unsigned int locked:1, /* timer is only accesibble by owner if set */ + klocked:1, /* kernel lock (after START) */ + check_again:1, + check_blocked:1; + + unsigned int flags; /* status flags */ + unsigned int info_flags; /* info for sync */ + + spinlock_t owner_lock; + spinlock_t check_lock; + + /* clients which uses this queue (bitmap) */ + DECLARE_BITMAP(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS); + unsigned int clients; /* users of this queue */ + struct semaphore timer_mutex; + + snd_use_lock_t use_lock; +}; + + +/* get the number of current queues */ +int snd_seq_queue_get_cur_queues(void); + +/* init queues structure */ +int snd_seq_queues_init(void); + +/* delete queues */ +void snd_seq_queues_delete(void); + + +/* create new queue (constructor) */ +int snd_seq_queue_alloc(int client, int locked, unsigned int flags); + +/* delete queue (destructor) */ +int snd_seq_queue_delete(int client, int queueid); + +/* notification that client has left the system */ +void snd_seq_queue_client_termination(int client); + +/* final stage */ +void snd_seq_queue_client_leave(int client); + +/* enqueue a event received from one the clients */ +int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop); + +/* Remove events */ +void snd_seq_queue_client_leave_cells(int client); +void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info); + +/* return pointer to queue structure for specified id */ +queue_t *queueptr(int queueid); +/* unlock */ +#define queuefree(q) snd_use_lock_free(&(q)->use_lock) + +/* return the (first) queue matching with the specified name */ +queue_t *snd_seq_queue_find_name(char *name); + +/* check single queue and dispatch events */ +void snd_seq_check_queue(queue_t *q, int atomic, int hop); + +/* access to queue's parameters */ +int snd_seq_queue_check_access(int queueid, int client); +int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info); +int snd_seq_queue_set_owner(int queueid, int client, int locked); +int snd_seq_queue_set_locked(int queueid, int client, int locked); +int snd_seq_queue_timer_open(int queueid); +int snd_seq_queue_timer_close(int queueid); +int snd_seq_queue_use(int queueid, int client, int use); +int snd_seq_queue_is_used(int queueid, int client); + +int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop); +void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop); + +/* + * 64bit division - for sync stuff.. + */ +#if defined(i386) || defined(i486) + +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("divl %4" \ + : "=a" ((u32)(q)), \ + "=d" ((u32)(r)) \ + : "0" ((u32)(n0)), \ + "1" ((u32)(n1)), \ + "rm" ((u32)(d))) + +#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0) +#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0) +#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y) + +#else +#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y))) +#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y))) +#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r)) +#endif + + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_system.c linux/sound/core/seq/seq_system.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_system.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_system.c 2002-08-12 02:43:45.000000000 -0600 @@ -0,0 +1,182 @@ +/* + * ALSA sequencer System services Client + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "seq_system.h" +#include "seq_timer.h" +#include "seq_queue.h" + +/* internal client that provide system services, access to timer etc. */ + +/* + * Port "Timer" + * - send tempo /start/stop etc. events to this port to manipulate the + * queue's timer. The queue address is specified in + * data.queue.queue. + * - this port supports subscription. The received timer events are + * broadcasted to all subscribed clients. The modified tempo + * value is stored on data.queue.value. + * The modifier client/port is not send. + * + * Port "Announce" + * - does not receive message + * - supports supscription. For each client or port attaching to or + * detaching from the system an announcement is send to the subscribed + * clients. + * + * Idea: the subscription mechanism might also work handy for distributing + * synchronisation and timing information. In this case we would ideally have + * a list of subscribers for each type of sync (time, tick), for each timing + * queue. + * + * NOTE: the queue to be started, stopped, etc. must be specified + * in data.queue.addr.queue field. queue is used only for + * scheduling, and no longer referred as affected queue. + * They are used only for timer broadcast (see above). + * -- iwai + */ + + +/* client id of our system client */ +static int sysclient = -1; + +/* port id numbers for this client */ +static int announce_port = -1; + + + +/* fill standard header data, source port & channel are filled in */ +static int setheader(snd_seq_event_t * ev, int client, int port) +{ + if (announce_port < 0) + return -ENODEV; + + memset(ev, 0, sizeof(snd_seq_event_t)); + + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + + ev->source.client = sysclient; + ev->source.port = announce_port; + ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + + /* fill data */ + /*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/ + ev->data.addr.client = client; + ev->data.addr.port = port; + + return 0; +} + + +/* entry points for broadcasting system events */ +void snd_seq_system_broadcast(int client, int port, int type) +{ + snd_seq_event_t ev; + + if (setheader(&ev, client, port) < 0) + return; + ev.type = type; + snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0); +} + +/* entry points for broadcasting system events */ +int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev) +{ + ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + ev->source.client = sysclient; + ev->source.port = announce_port; + ev->dest.client = client; + ev->dest.port = port; + return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0); +} + +/* call-back handler for timer events */ +static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + return snd_seq_control_queue(ev, atomic, hop); +} + +/* register our internal client */ +int __init snd_seq_system_client_init(void) +{ + + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t inf; + snd_seq_port_info_t port; + + memset(&callbacks, 0, sizeof(callbacks)); + memset(&pcallbacks, 0, sizeof(pcallbacks)); + memset(&inf, 0, sizeof(inf)); + memset(&port, 0, sizeof(port)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.event_input = event_input_timer; + + /* register client */ + callbacks.allow_input = callbacks.allow_output = 1; + sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks); + + /* set our name */ + inf.client = 0; + inf.type = KERNEL_CLIENT; + strcpy(inf.name, "System"); + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf); + + /* register timer */ + strcpy(port.name, "Timer"); + port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */ + port.capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */ + port.kernel = &pcallbacks; + port.type = 0; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.addr.client = sysclient; + port.addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + + /* register announcement port */ + strcpy(port.name, "Announce"); + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ + port.kernel = NULL; + port.type = 0; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.addr.client = sysclient; + port.addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + announce_port = port.addr.port; + + return 0; +} + + +/* unregister our internal client */ +void __exit snd_seq_system_client_done(void) +{ + int oldsysclient = sysclient; + + if (oldsysclient >= 0) { + sysclient = -1; + announce_port = -1; + snd_seq_delete_kernel_client(oldsysclient); + } +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_system.h linux/sound/core/seq/seq_system.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_system.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_system.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,46 @@ +/* + * ALSA sequencer System Client + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_SYSTEM_H +#define __SND_SEQ_SYSTEM_H + +#include + + +/* entry points for broadcasting system events */ +void snd_seq_system_broadcast(int client, int port, int type); + +#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) +#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) +#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) +#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START) +#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) +#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) + +int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev); + +/* register our internal client */ +int snd_seq_system_client_init(void); + +/* unregister our internal client */ +void snd_seq_system_client_done(void); + + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_timer.c linux/sound/core/seq/seq_timer.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_timer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_timer.c 2003-03-11 08:02:26.000000000 -0700 @@ -0,0 +1,435 @@ +/* + * ALSA sequencer Timer + * Copyright (c) 1998-1999 by Frank van de Pol + * Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "seq_timer.h" +#include "seq_queue.h" +#include "seq_info.h" + +extern int seq_default_timer_class; +extern int seq_default_timer_sclass; +extern int seq_default_timer_card; +extern int seq_default_timer_device; +extern int seq_default_timer_subdevice; +extern int seq_default_timer_resolution; + +#define SKEW_BASE 0x10000 /* 16bit shift */ + +void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks) +{ + if (tempo < 1000000) + tick->resolution = (tempo * 1000) / ppq; + else { + /* might overflow.. */ + unsigned int s; + s = tempo % ppq; + s = (s * 1000) / ppq; + tick->resolution = (tempo / ppq) * 1000; + tick->resolution += s; + } + if (tick->resolution <= 0) + tick->resolution = 1; + tick->resolution *= nticks; + snd_seq_timer_update_tick(tick, 0); +} + +/* create new timer (constructor) */ +seq_timer_t *snd_seq_timer_new(void) +{ + seq_timer_t *tmr; + + tmr = snd_kcalloc(sizeof(seq_timer_t), GFP_KERNEL); + if (tmr == NULL) { + snd_printd("malloc failed for snd_seq_timer_new() \n"); + return NULL; + } + spin_lock_init(&tmr->lock); + + /* reset setup to defaults */ + snd_seq_timer_defaults(tmr); + + /* reset time */ + snd_seq_timer_reset(tmr); + + return tmr; +} + +/* delete timer (destructor) */ +void snd_seq_timer_delete(seq_timer_t **tmr) +{ + seq_timer_t *t = *tmr; + *tmr = NULL; + + if (t == NULL) { + snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n"); + return; + } + t->running = 0; + + /* reset time */ + snd_seq_timer_stop(t); + snd_seq_timer_reset(t); + + kfree(t); +} + +void snd_seq_timer_defaults(seq_timer_t * tmr) +{ + /* setup defaults */ + tmr->ppq = 96; /* 96 PPQ */ + tmr->tempo = 500000; /* 120 BPM */ + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + tmr->running = 0; + + tmr->type = SNDRV_SEQ_TIMER_ALSA; + tmr->alsa_id.dev_class = seq_default_timer_class; + tmr->alsa_id.dev_sclass = seq_default_timer_sclass; + tmr->alsa_id.card = seq_default_timer_card; + tmr->alsa_id.device = seq_default_timer_device; + tmr->alsa_id.subdevice = seq_default_timer_subdevice; + tmr->preferred_resolution = seq_default_timer_resolution; + + tmr->skew = tmr->skew_base = SKEW_BASE; +} + +void snd_seq_timer_reset(seq_timer_t * tmr) +{ + unsigned long flags; + + spin_lock_irqsave(&tmr->lock, flags); + + /* reset time & songposition */ + tmr->cur_time.tv_sec = 0; + tmr->cur_time.tv_nsec = 0; + + tmr->tick.cur_tick = 0; + tmr->tick.fraction = 0; + + spin_unlock_irqrestore(&tmr->lock, flags); +} + + +/* called by timer interrupt routine. the period time since previous invocation is passed */ +static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks) +{ + unsigned long flags; + queue_t *q = (queue_t *)timeri->callback_data; + seq_timer_t *tmr; + + if (q == NULL) + return; + tmr = q->timer; + if (tmr == NULL) + return; + if (!tmr->running) + return; + + resolution *= ticks; + if (tmr->skew != tmr->skew_base) { + /* FIXME: assuming skew_base = 0x10000 */ + resolution = (resolution >> 16) * tmr->skew + + (((resolution & 0xffff) * tmr->skew) >> 16); + } + + spin_lock_irqsave(&tmr->lock, flags); + + /* update timer */ + snd_seq_inc_time_nsec(&tmr->cur_time, resolution); + + /* calculate current tick */ + snd_seq_timer_update_tick(&tmr->tick, resolution); + + /* register actual time of this timer update */ + do_gettimeofday(&tmr->last_update); + + spin_unlock_irqrestore(&tmr->lock, flags); + + /* check queues and dispatch events */ + snd_seq_check_queue(q, 1, 0); +} + +/* set current tempo */ +int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + if (tempo <= 0) + return -EINVAL; + spin_lock_irqsave(&tmr->lock, flags); + if ((unsigned int)tempo != tmr->tempo) { + tmr->tempo = tempo; + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + } + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current ppq */ +int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + if (ppq <= 0) + return -EINVAL; + spin_lock_irqsave(&tmr->lock, flags); + if (tmr->running && (ppq != tmr->ppq)) { + /* refuse to change ppq on running timers */ + /* because it will upset the song position (ticks) */ + spin_unlock_irqrestore(&tmr->lock, flags); + snd_printd("seq: cannot change ppq of a running timer\n"); + return -EBUSY; + } + + tmr->ppq = ppq; + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current tick position */ +int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + spin_lock_irqsave(&tmr->lock, flags); + tmr->tick.cur_tick = position; + tmr->tick.fraction = 0; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current real-time position */ +int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + snd_seq_sanity_real_time(&position); + spin_lock_irqsave(&tmr->lock, flags); + tmr->cur_time = position; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set timer skew */ +int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + /* FIXME */ + if (base != SKEW_BASE) { + snd_printd("invalid skew base 0x%x\n", base); + return -EINVAL; + } + spin_lock_irqsave(&tmr->lock, flags); + tmr->skew = skew; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +int snd_seq_timer_open(queue_t *q) +{ + snd_timer_instance_t *t; + seq_timer_t *tmr; + char str[32]; + int err; + + tmr = q->timer; + snd_assert(tmr != NULL, return -EINVAL); + if (tmr->timeri) + return -EBUSY; + sprintf(str, "sequencer queue %i", q->queue); + if (tmr->type != SNDRV_SEQ_TIMER_ALSA) /* standard ALSA timer */ + return -EINVAL; + if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) + tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; + err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue); + if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) { + if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL || + tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) { + snd_timer_id_t tid; + memset(&tid, 0, sizeof(tid)); + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; + tid.card = -1; + tid.device = SNDRV_TIMER_GLOBAL_SYSTEM; + err = snd_timer_open(&t, str, &tid, q->queue); + } + if (err < 0) { + snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err); + return err; + } + } + t->callback = snd_seq_timer_interrupt; + t->callback_data = q; + t->flags |= SNDRV_TIMER_IFLG_AUTO; + tmr->timeri = t; + return 0; +} + +int snd_seq_timer_close(queue_t *q) +{ + seq_timer_t *tmr; + + tmr = q->timer; + snd_assert(tmr != NULL, return -EINVAL); + if (tmr->timeri) { + snd_timer_stop(tmr->timeri); + snd_timer_close(tmr->timeri); + tmr->timeri = NULL; + } + return 0; +} + +int snd_seq_timer_stop(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return -EINVAL; + if (!tmr->running) + return 0; + tmr->running = 0; + snd_timer_pause(tmr->timeri); + return 0; +} + +static int initialize_timer(seq_timer_t *tmr) +{ + snd_timer_t *t; + t = tmr->timeri->timer; + snd_assert(t, return -EINVAL); + + tmr->ticks = 1; + if (tmr->preferred_resolution && + ! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { + unsigned long r = t->hw.resolution; + if (! r && t->hw.c_resolution) + r = t->hw.c_resolution(t); + if (r) { + tmr->ticks = (unsigned int)(tmr->preferred_resolution / r); + if (! tmr->ticks) + tmr->ticks = 1; + } + } + tmr->initialized = 1; + return 0; +} + +int snd_seq_timer_start(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return -EINVAL; + if (tmr->running) + snd_seq_timer_stop(tmr); + snd_seq_timer_reset(tmr); + if (initialize_timer(tmr) < 0) + return -EINVAL; + snd_timer_start(tmr->timeri, tmr->ticks); + tmr->running = 1; + do_gettimeofday(&tmr->last_update); + return 0; +} + +int snd_seq_timer_continue(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return -EINVAL; + if (tmr->running) + return -EBUSY; + if (! tmr->initialized) { + snd_seq_timer_reset(tmr); + if (initialize_timer(tmr) < 0) + return -EINVAL; + } + snd_timer_start(tmr->timeri, tmr->ticks); + tmr->running = 1; + do_gettimeofday(&tmr->last_update); + return 0; +} + +/* return current 'real' time. use timeofday() to get better granularity. */ +snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr) +{ + snd_seq_real_time_t cur_time; + + cur_time = tmr->cur_time; + if (tmr->running) { + struct timeval tm; + int usec; + do_gettimeofday(&tm); + usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); + if (usec < 0) { + cur_time.tv_nsec += (1000000 + usec) * 1000; + cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; + } else { + cur_time.tv_nsec += usec * 1000; + cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; + } + snd_seq_sanity_real_time(&cur_time); + } + + return cur_time; +} + +/* TODO: use interpolation on tick queue (will only be useful for very + high PPQ values) */ +snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr) +{ + return tmr->tick.cur_tick; +} + + +/* exported to seq_info.c */ +void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + queue_t *q; + seq_timer_t *tmr; + snd_timer_instance_t *ti; + unsigned long resolution; + + for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) { + q = queueptr(idx); + if (q == NULL) + continue; + if ((tmr = q->timer) == NULL || + (ti = tmr->timeri) == NULL) { + queuefree(q); + continue; + } + snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); + resolution = snd_timer_resolution(ti) * tmr->ticks; + snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); + snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); + queuefree(q); + } +} diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_timer.h linux/sound/core/seq/seq_timer.h --- linux-2.4.21-rc1.orig/sound/core/seq/seq_timer.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_timer.h 2003-04-29 05:19:00.000000000 -0600 @@ -0,0 +1,141 @@ +/* + * ALSA sequencer Timer + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_TIMER_H +#define __SND_SEQ_TIMER_H + +#include +#include + +typedef struct { + snd_seq_tick_time_t cur_tick; /* current tick */ + unsigned long resolution; /* time per tick in nsec */ + unsigned long fraction; /* current time per tick in nsec */ +} seq_timer_tick_t; + +typedef struct { + /* ... tempo / offset / running state */ + + unsigned int running:1, /* running state of queue */ + initialized:1; /* timer is initialized */ + + unsigned int tempo; /* current tempo, us/tick */ + int ppq; /* time resolution, ticks/quarter */ + + snd_seq_real_time_t cur_time; /* current time */ + seq_timer_tick_t tick; /* current tick */ + int tick_updated; + + int type; /* timer type */ + snd_timer_id_t alsa_id; /* ALSA's timer ID */ + snd_timer_instance_t *timeri; /* timer instance */ + unsigned int ticks; + unsigned long preferred_resolution; /* timer resolution */ + + unsigned int skew; + unsigned int skew_base; + + struct timeval last_update; /* time of last clock update, used for interpolation */ + + spinlock_t lock; +} seq_timer_t; + + +/* create new timer (constructor) */ +extern seq_timer_t *snd_seq_timer_new(void); + +/* delete timer (destructor) */ +extern void snd_seq_timer_delete(seq_timer_t **tmr); + +void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks); + +/* */ +static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution) +{ + if (tick->resolution > 0) { + tick->fraction += resolution; + tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution); + tick->fraction %= tick->resolution; + } +} + + +/* compare timestamp between events */ +/* return 1 if a >= b; otherwise return 0 */ +static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b) +{ + /* compare ticks */ + return (*a >= *b); +} + +static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b) +{ + /* compare real time */ + if (a->tv_sec > b->tv_sec) + return 1; + if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec)) + return 1; + return 0; +} + + +static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm) +{ + while (tm->tv_nsec >= 1000000000) { + /* roll-over */ + tm->tv_nsec -= 1000000000; + tm->tv_sec++; + } +} + + +/* increment timestamp */ +static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc) +{ + tm->tv_sec += inc->tv_sec; + tm->tv_nsec += inc->tv_nsec; + snd_seq_sanity_real_time(tm); +} + +static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec) +{ + tm->tv_nsec += nsec; + snd_seq_sanity_real_time(tm); +} + +/* called by timer isr */ +int snd_seq_timer_open(queue_t *q); +int snd_seq_timer_close(queue_t *q); +int snd_seq_timer_midi_open(queue_t *q); +int snd_seq_timer_midi_close(queue_t *q); +void snd_seq_timer_defaults(seq_timer_t *tmr); +void snd_seq_timer_reset(seq_timer_t *tmr); +int snd_seq_timer_stop(seq_timer_t *tmr); +int snd_seq_timer_start(seq_timer_t *tmr); +int snd_seq_timer_continue(seq_timer_t *tmr); +int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo); +int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq); +int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position); +int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position); +int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base); +snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr); +snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr); + +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/seq/seq_virmidi.c linux/sound/core/seq/seq_virmidi.c --- linux-2.4.21-rc1.orig/sound/core/seq/seq_virmidi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/seq/seq_virmidi.c 2002-12-19 08:59:17.000000000 -0700 @@ -0,0 +1,537 @@ +/* + * Virtual Raw MIDI client on Sequencer + * + * Copyright (c) 2000 by Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Virtual Raw MIDI client + * + * The virtual rawmidi client is a sequencer client which associate + * a rawmidi device file. The created rawmidi device file can be + * accessed as a normal raw midi, but its MIDI source and destination + * are arbitrary. For example, a user-client software synth connected + * to this port can be used as a normal midi device as well. + * + * The virtual rawmidi device accepts also multiple opens. Each file + * has its own input buffer, so that no conflict would occur. The drain + * of input/output buffer acts only to the local buffer. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer"); +MODULE_LICENSE("GPL"); + +/* + * initialize an event record + */ +static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->source.port = vmidi->port; + switch (vmidi->seq_mode) { + case SNDRV_VIRMIDI_SEQ_DISPATCH: + ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + break; + case SNDRV_VIRMIDI_SEQ_ATTACH: + /* FIXME: source and destination are same - not good.. */ + ev->dest.client = vmidi->client; + ev->dest.port = vmidi->port; + break; + } + ev->type = SNDRV_SEQ_EVENT_NONE; +} + +/* + * decode input event and put to read buffer of each opened file + */ +static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev) +{ + snd_virmidi_t *vmidi; + struct list_head *list; + unsigned char msg[4]; + int len; + + read_lock(&rdev->filelist_lock); + list_for_each(list, &rdev->filelist) { + vmidi = list_entry(list, snd_virmidi_t, list); + if (!vmidi->trigger) + continue; + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + continue; + snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream); + } else { + len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev); + if (len > 0) + snd_rawmidi_receive(vmidi->substream, msg, len); + } + } + read_unlock(&rdev->filelist_lock); + + return 0; +} + +/* + * receive an event from the remote virmidi port + * + * for rawmidi inputs, you can call this function from the event + * handler of a remote port which is attached to the virmidi via + * SNDRV_VIRMIDI_SEQ_ATTACH. + */ +/* exported */ +int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -EINVAL); + return snd_virmidi_dev_receive_event(rdev, ev); +} + +/* + * event handler of virmidi port + */ +static int snd_virmidi_event_input(snd_seq_event_t *ev, int direct, + void *private_data, int atomic, int hop) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!(rdev->flags & SNDRV_VIRMIDI_USE)) + return 0; /* ignored */ + return snd_virmidi_dev_receive_event(rdev, ev); +} + +/* + * trigger rawmidi stream for input + */ +static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + + if (up) { + vmidi->trigger = 1; + } else { + vmidi->trigger = 0; + } +} + +/* + * trigger rawmidi stream for output + */ +static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + int count, res; + unsigned char buf[32], *pbuf; + + if (up) { + vmidi->trigger = 1; + if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH && + !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { + snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail); + return; /* ignored */ + } + if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { + if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0) + return; + vmidi->event.type = SNDRV_SEQ_EVENT_NONE; + } + while (1) { + count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); + if (count <= 0) + break; + pbuf = buf; + while (count > 0) { + res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event); + if (res < 0) { + snd_midi_event_reset_encode(vmidi->parser); + continue; + } + snd_rawmidi_transmit_ack(substream, res); + pbuf += res; + count -= res; + if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { + if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0) + return; + vmidi->event.type = SNDRV_SEQ_EVENT_NONE; + } + } + } + } else { + vmidi->trigger = 0; + } +} + +/* + * open rawmidi handle for input + */ +static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_rawmidi_runtime_t *runtime = substream->runtime; + snd_virmidi_t *vmidi; + unsigned long flags; + + vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + if (vmidi == NULL) + return -ENOMEM; + vmidi->substream = substream; + if (snd_midi_event_new(0, &vmidi->parser) < 0) { + snd_magic_kfree(vmidi); + return -ENOMEM; + } + vmidi->seq_mode = rdev->seq_mode; + vmidi->client = rdev->client; + vmidi->port = rdev->port; + runtime->private_data = vmidi; + write_lock_irqsave(&rdev->filelist_lock, flags); + list_add_tail(&vmidi->list, &rdev->filelist); + write_unlock_irqrestore(&rdev->filelist_lock, flags); + vmidi->rdev = rdev; + return 0; +} + +/* + * open rawmidi handle for output + */ +static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_rawmidi_runtime_t *runtime = substream->runtime; + snd_virmidi_t *vmidi; + + vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + if (vmidi == NULL) + return -ENOMEM; + vmidi->substream = substream; + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) { + snd_magic_kfree(vmidi); + return -ENOMEM; + } + vmidi->seq_mode = rdev->seq_mode; + vmidi->client = rdev->client; + vmidi->port = rdev->port; + snd_virmidi_init_event(vmidi, &vmidi->event); + vmidi->rdev = rdev; + runtime->private_data = vmidi; + return 0; +} + +/* + * close rawmidi handle for input + */ +static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_midi_event_free(vmidi->parser); + list_del(&vmidi->list); + substream->runtime->private_data = NULL; + snd_magic_kfree(vmidi); + return 0; +} + +/* + * close rawmidi handle for output + */ +static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_midi_event_free(vmidi->parser); + substream->runtime->private_data = NULL; + snd_magic_kfree(vmidi); + return 0; +} + +/* + * subscribe callback - allow output to rawmidi device + */ +static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!try_module_get(rdev->card->module)) + return -EFAULT; + rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE; + return 0; +} + +/* + * unsubscribe callback - disallow output to rawmidi device + */ +static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE; + module_put(rdev->card->module); + return 0; +} + + +/* + * use callback - allow input to rawmidi device + */ +static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!try_module_get(rdev->card->module)) + return -EFAULT; + rdev->flags |= SNDRV_VIRMIDI_USE; + return 0; +} + +/* + * unuse callback - disallow input to rawmidi device + */ +static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev->flags &= ~SNDRV_VIRMIDI_USE; + module_put(rdev->card->module); + return 0; +} + + +/* + * Register functions + */ + +static snd_rawmidi_ops_t snd_virmidi_input_ops = { + .open = snd_virmidi_input_open, + .close = snd_virmidi_input_close, + .trigger = snd_virmidi_input_trigger, +}; + +static snd_rawmidi_ops_t snd_virmidi_output_ops = { + .open = snd_virmidi_output_open, + .close = snd_virmidi_output_close, + .trigger = snd_virmidi_output_trigger, +}; + +/* + * create a sequencer client and a port + */ +static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev) +{ + int client; + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t info; + snd_seq_port_info_t pinfo; + int err; + + if (rdev->client >= 0) + return 0; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = rdev; + callbacks.allow_input = 1; + callbacks.allow_output = 1; + client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks); + if (client < 0) + return client; + rdev->client = client; + + /* set client name */ + memset(&info, 0, sizeof(info)); + info.client = client; + info.type = KERNEL_CLIENT; + sprintf(info.name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info); + + /* create a port */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr.client = client; + sprintf(pinfo.name, "VirMIDI %d-%d", rdev->card->number, rdev->device); + /* set all capabilities */ + pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + pinfo.midi_channels = 16; + memset(&pcallbacks, 0, sizeof(pcallbacks)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.private_data = rdev; + pcallbacks.subscribe = snd_virmidi_subscribe; + pcallbacks.unsubscribe = snd_virmidi_unsubscribe; + pcallbacks.use = snd_virmidi_use; + pcallbacks.unuse = snd_virmidi_unuse; + pcallbacks.event_input = snd_virmidi_event_input; + pinfo.kernel = &pcallbacks; + err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo); + if (err < 0) { + snd_seq_delete_kernel_client(client); + rdev->client = -1; + return err; + } + + rdev->port = pinfo.addr.port; + return 0; /* success */ +} + + +/* + * release the sequencer client + */ +static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev) +{ + if (rdev->client >= 0) { + snd_seq_delete_kernel_client(rdev->client); + rdev->client = -1; + } +} + +/* + * register the device + */ +static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + int err; + + switch (rdev->seq_mode) { + case SNDRV_VIRMIDI_SEQ_DISPATCH: + err = snd_virmidi_dev_attach_seq(rdev); + if (err < 0) + return err; + break; + case SNDRV_VIRMIDI_SEQ_ATTACH: + if (rdev->client == 0) + return -EINVAL; + /* should check presence of port more strictly.. */ + break; + default: + snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode); + return -EINVAL; + } + return 0; +} + + +/* + * unregister the device + */ +static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + + if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH) + snd_virmidi_dev_detach_seq(rdev); + return 0; +} + +/* + * + */ +static snd_rawmidi_global_ops_t snd_virmidi_global_ops = { + .dev_register = snd_virmidi_dev_register, + .dev_unregister = snd_virmidi_dev_unregister, +}; + +/* + * free device + */ +static void snd_virmidi_free(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return); + snd_magic_kfree(rdev); +} + +/* + * create a new device + * + */ +/* exported */ +int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi) +{ + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + int err; + + *rrmidi = NULL; + if ((err = snd_rawmidi_new(card, "VirMidi", device, + 16, /* may be configurable */ + 16, /* may be configurable */ + &rmidi)) < 0) + return err; + strcpy(rmidi->name, rmidi->id); + rdev = snd_magic_kcalloc(snd_virmidi_dev_t, 0, GFP_KERNEL); + if (rdev == NULL) { + snd_device_free(card, rmidi); + return -ENOMEM; + } + rdev->card = card; + rdev->rmidi = rmidi; + rdev->device = device; + rdev->client = -1; + rwlock_init(&rdev->filelist_lock); + INIT_LIST_HEAD(&rdev->filelist); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; + rmidi->private_data = rdev; + rmidi->private_free = snd_virmidi_free; + rmidi->ops = &snd_virmidi_global_ops; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + *rrmidi = rmidi; + return 0; +} + +/* + * ENTRY functions + */ + +static int __init alsa_virmidi_init(void) +{ + return 0; +} + +static void __exit alsa_virmidi_exit(void) +{ +} + +module_init(alsa_virmidi_init) +module_exit(alsa_virmidi_exit) + +EXPORT_SYMBOL(snd_virmidi_new); +EXPORT_SYMBOL(snd_virmidi_receive); diff -urN linux-2.4.21-rc1.orig/sound/core/sgbuf.c linux/sound/core/sgbuf.c --- linux-2.4.21-rc1.orig/sound/core/sgbuf.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/sgbuf.c 2003-04-29 23:43:44.000000000 -0600 @@ -0,0 +1,290 @@ +/* + * Scatter-Gather buffer + * + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* First the 2.5.x kernel version of things, as provided + * by alsa-kernel/core/sgbuf.c */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) + + +/* table entries are align to 32 */ +#define SGBUF_TBL_ALIGN 32 +#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN) + + +/** + * snd_malloc_sgbuf_pages - allocate the pages for the PCI SG buffer + * @pci: the pci device pointer + * @size: the requested buffer size in bytes + * @dmab: the buffer record to store + * + * Initializes the SG-buffer table and allocates the buffer pages + * for the given size. + * The pages are mapped to the virtually continuous memory. + * + * This function is usually called from the middle-level functions such as + * snd_pcm_lib_malloc_pages(). + * + * Returns the mapped virtual address of the buffer if allocation was + * successful, or NULL at error. + */ +void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab) +{ + struct snd_sg_buf *sgbuf; + unsigned int i, pages; + + dmab->area = NULL; + dmab->addr = 0; + dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL); + if (! sgbuf) + return NULL; + memset(sgbuf, 0, sizeof(*sgbuf)); + sgbuf->pci = pci; + pages = snd_sgbuf_aligned_pages(size); + sgbuf->tblsize = sgbuf_align_table(pages); + sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); + if (! sgbuf->table) + goto _failed; + memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize); + sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL); + if (! sgbuf->page_table) + goto _failed; + memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize); + + /* allocate each page */ + for (i = 0; i < pages; i++) { + void *ptr; + dma_addr_t addr; + ptr = snd_malloc_pci_page(sgbuf->pci, &addr); + if (! ptr) + goto _failed; + sgbuf->table[i].buf = ptr; + sgbuf->table[i].addr = addr; + sgbuf->page_table[i] = virt_to_page(ptr); + sgbuf->pages++; + } + + sgbuf->size = size; + dmab->area = vmap(sgbuf->page_table, sgbuf->pages); + if (! dmab->area) + goto _failed; + return dmab->area; + + _failed: + snd_free_sgbuf_pages(dmab); /* free the table */ + return NULL; +} + +/** + * snd_free_sgbuf_pages - free the sg buffer + * @dmab: buffer record + * + * Releases the pages and the SG-buffer table. + * + * This function is called usually from the middle-level function + * such as snd_pcm_lib_free_pages(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) +{ + struct snd_sg_buf *sgbuf = dmab->private_data; + int i; + + if (! sgbuf) + return -EINVAL; + + for (i = 0; i < sgbuf->pages; i++) + snd_free_pci_page(sgbuf->pci, sgbuf->table[i].buf, sgbuf->table[i].addr); + if (dmab->area) + vunmap(dmab->area); + dmab->area = NULL; + + if (sgbuf->table) + kfree(sgbuf->table); + if (sgbuf->page_table) + kfree(sgbuf->page_table); + kfree(sgbuf); + dmab->private_data = NULL; + + return 0; +} + +#else + +/* + * we don't have vmap/vunmap, so use vmalloc_32 and vmalloc_dma instead + * So use the stuff provided by acore/sgbuf.c + */ + +/* table entries are align to 32 */ +#define SGBUF_TBL_ALIGN 32 +#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) +/* get the virtual address of the given vmalloc'ed pointer */ +static void *get_vmalloc_addr(void *pageptr) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long lpage; + + lpage = VMALLOC_VMADDR(pageptr); + pgd = pgd_offset_k(lpage); + pmd = pmd_offset(pgd, lpage); + pte = pte_offset(pmd, lpage); + return (void *)pte_page(*pte); +} +#endif + +/* set up the page table from the given vmalloc'ed buffer pointer. + * return a negative error if the page is out of the pci address mask. + */ +static int store_page_tables(struct snd_sg_buf *sgbuf, void *vmaddr, unsigned int pages) +{ + unsigned int i; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) + unsigned long rmask; + if (sgbuf->pci) + rmask = ~((unsigned long)sgbuf->pci->dma_mask); + else + rmask = ~0xffffffUL; +#endif + + sgbuf->pages = 0; + for (i = 0; i < pages; i++) { + struct page *page; + void *ptr; + dma_addr_t addr; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) + page = vmalloc_to_page(vmaddr + (i << PAGE_SHIFT)); + ptr = page_address(page); + addr = virt_to_bus(ptr); + if (addr & rmask) + return -EINVAL; +#else + ptr = get_vmalloc_addr(vmaddr + (i << PAGE_SHIFT)); + addr = virt_to_bus(ptr); + page = virt_to_page(ptr); +#endif + sgbuf->table[i].buf = ptr; + sgbuf->table[i].addr = addr; + sgbuf->page_table[i] = page; + SetPageReserved(page); + sgbuf->pages++; + } + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18) +#define vmalloc_32(x) vmalloc(x) +#endif + +/* remove all vmalloced pages */ +static void release_vm_buffer(struct snd_sg_buf *sgbuf, void *vmaddr) +{ + int i; + + for (i = 0; i < sgbuf->pages; i++) + if (sgbuf->page_table[i]) { + ClearPageReserved(sgbuf->page_table[i]); + sgbuf->page_table[i] = NULL; + } + sgbuf->pages = 0; + if (vmaddr) + vfree(vmaddr); /* don't use wrapper */ +} + +void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab) +{ + struct snd_sg_buf *sgbuf; + unsigned int pages; + + dmab->area = NULL; + dmab->addr = 0; + dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL); + if (! sgbuf) + return NULL; + memset(sgbuf, 0, sizeof(*sgbuf)); + sgbuf->pci = pci; + pages = snd_sgbuf_aligned_pages(size); + sgbuf->tblsize = sgbuf_align_table(pages); + sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); + if (! sgbuf->table) + goto _failed; + memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize); + sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL); + if (! sgbuf->page_table) + goto _failed; + memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize); + + sgbuf->size = size; + dmab->area = vmalloc_32(pages << PAGE_SHIFT); + if (! dmab->area || store_page_tables(sgbuf, dmab->area, pages) < 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) + /* reallocate with DMA flag */ + release_vm_buffer(sgbuf, dmab->area); + dmab->area = vmalloc_dma(pages << PAGE_SHIFT); + if (! dmab->area || store_page_tables(sgbuf, dmab->area, pages) < 0) + goto _failed; + +#else + goto _failed; +#endif + } + + memset(dmab->area, 0, size); + return dmab->area; + + _failed: + snd_free_sgbuf_pages(dmab); /* free the table */ + return NULL; +} + +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) +{ + struct snd_sg_buf *sgbuf = dmab->private_data; + + if (dmab->area) + release_vm_buffer(sgbuf, dmab->area); + dmab->area = NULL; + if (sgbuf->table) + kfree(sgbuf->table); + sgbuf->table = NULL; + if (sgbuf->page_table) + kfree(sgbuf->page_table); + kfree(sgbuf); + dmab->private_data = NULL; + + return 0; +} + +#endif /* < 2.5.0 */ diff -urN linux-2.4.21-rc1.orig/sound/core/sound.c linux/sound/core/sound.c --- linux-2.4.21-rc1.orig/sound/core/sound.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/sound.c 2003-02-28 07:29:21.000000000 -0700 @@ -0,0 +1,513 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SNDRV_OS_MINORS 256 + +int major = CONFIG_SND_MAJOR; +static int cards_limit = SNDRV_CARDS; +int device_mode = S_IFCHR | S_IRUGO | S_IWUGO; +int device_gid = 0; +int device_uid = 0; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +MODULE_PARM(major, "i"); +MODULE_PARM_DESC(major, "Major # for sound driver."); +MODULE_PARM_SYNTAX(major, "default:116,skill:devel"); +MODULE_PARM(cards_limit, "i"); +MODULE_PARM_DESC(cards_limit, "Count of soundcards installed in the system."); +MODULE_PARM_SYNTAX(cards_limit, "default:8,skill:advanced"); +MODULE_PARM(device_mode, "i"); +MODULE_PARM_DESC(device_mode, "Device file permission mask for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(device_mode, "default:0666,base:8"); +MODULE_PARM(device_gid, "i"); +MODULE_PARM_DESC(device_gid, "Device file GID for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(device_gid, "default:0"); +MODULE_PARM(device_uid, "i"); +MODULE_PARM_DESC(device_uid, "Device file UID for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(device_uid, "default:0"); + +int snd_ecards_limit; + +static struct list_head snd_minors_hash[SNDRV_CARDS]; + +static DECLARE_MUTEX(sound_mutex); + +#ifdef CONFIG_DEVFS_FS +static devfs_handle_t devfs_handle = NULL; +#endif + +#ifdef CONFIG_KMOD + +/** + * snd_request_card - try to load the card module + * @card: the card number + * + * Tries to load the module "snd-card-X" for the given card number + * via KMOD. Returns immediately if already loaded. + */ +void snd_request_card(int card) +{ + char str[32]; + int locked; + + read_lock(&snd_card_rwlock); + locked = snd_cards_lock & (1 << card); + read_unlock(&snd_card_rwlock); + if (locked) + return; + if (card < 0 || card >= cards_limit) + return; + sprintf(str, "snd-card-%i", card); + request_module(str); +} + +static void snd_request_other(int minor) +{ + char *str; + + switch (minor) { + case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; + case SNDRV_MINOR_TIMER: str = "snd-timer"; break; + default: return; + } + request_module(str); +} + +#endif /* request_module support */ + +static snd_minor_t *snd_minor_search(int minor) +{ + struct list_head *list; + snd_minor_t *mptr; + + list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) { + mptr = list_entry(list, snd_minor_t, list); + if (mptr->number == minor) + return mptr; + } + return NULL; +} + +static int snd_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + int card = SNDRV_MINOR_CARD(minor); + int dev = SNDRV_MINOR_DEVICE(minor); + snd_minor_t *mptr = NULL; + struct file_operations *old_fops; + int err = 0; + + if (dev != SNDRV_MINOR_SEQUENCER && dev != SNDRV_MINOR_TIMER) { + if (snd_cards[card] == NULL) { +#ifdef CONFIG_KMOD + snd_request_card(card); + if (snd_cards[card] == NULL) +#endif + return -ENODEV; + } + } else { +#ifdef CONFIG_KMOD + if ((mptr = snd_minor_search(minor)) == NULL) + snd_request_other(minor); +#endif + } + if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL) + return -ENODEV; + old_fops = file->f_op; + file->f_op = fops_get(mptr->f_ops); + if (file->f_op->open) + err = file->f_op->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; +} + +struct file_operations snd_fops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .open = snd_open +}; + +static int snd_kernel_minor(int type, snd_card_t * card, int dev) +{ + int minor; + + switch (type) { + case SNDRV_DEVICE_TYPE_SEQUENCER: + case SNDRV_DEVICE_TYPE_TIMER: + minor = type; + break; + case SNDRV_DEVICE_TYPE_CONTROL: + snd_assert(card != NULL, return -EINVAL); + minor = SNDRV_MINOR(card->number, type); + break; + case SNDRV_DEVICE_TYPE_HWDEP: + case SNDRV_DEVICE_TYPE_RAWMIDI: + case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: + case SNDRV_DEVICE_TYPE_PCM_CAPTURE: + snd_assert(card != NULL, return -EINVAL); + minor = SNDRV_MINOR(card->number, type + dev); + break; + default: + return -EINVAL; + } + snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + return minor; +} + +/** + * snd_register_device - Register the ALSA device file for the card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * @reg: the snd_minor_t record + * @name: the device file name + * + * Registers an ALSA device file for the given card. + * The operators have to be set in reg parameter. + * + * Retrurns zero if successful, or a negative error code on failure. + */ +int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) +{ + int minor = snd_kernel_minor(type, card, dev); + snd_minor_t *preg; + + if (minor < 0) + return minor; + preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); + if (preg == NULL) + return -ENOMEM; + *preg = *reg; + preg->number = minor; + preg->device = dev; + preg->dev = NULL; + down(&sound_mutex); + if (snd_minor_search(minor)) { + up(&sound_mutex); + kfree(preg); + return -EBUSY; + } + if (name) + preg->dev = snd_info_create_device(name, minor, 0); + list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]); + up(&sound_mutex); + return 0; +} + +/** + * snd_unregister_device - unregister the device on the given card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * + * Unregisters the device file already registered via + * snd_register_device(). + * + * Returns zero if sucecessful, or a negative error code on failure + */ +int snd_unregister_device(int type, snd_card_t * card, int dev) +{ + int minor = snd_kernel_minor(type, card, dev); + snd_minor_t *mptr; + + if (minor < 0) + return minor; + down(&sound_mutex); + if ((mptr = snd_minor_search(minor)) == NULL) { + up(&sound_mutex); + return -EINVAL; + } + if (mptr->dev) + snd_info_free_device(mptr->dev); + list_del(&mptr->list); + up(&sound_mutex); + kfree(mptr); + return 0; +} + +/* + * INFO PART + */ + +static snd_info_entry_t *snd_minor_info_entry = NULL; + +static void snd_minor_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int card, device; + struct list_head *list; + snd_minor_t *mptr; + + down(&sound_mutex); + for (card = 0; card < SNDRV_CARDS; card++) { + list_for_each(list, &snd_minors_hash[card]) { + mptr = list_entry(list, snd_minor_t, list); + if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) { + if ((device = mptr->device) >= 0) + snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment); + else + snd_iprintf(buffer, "%3i: [%i] : %s\n", mptr->number, card, mptr->comment); + } else { + snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); + } + } + } + up(&sound_mutex); +} + +int __init snd_minor_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_minor_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_minor_info_entry = entry; + return 0; +} + +int __exit snd_minor_info_done(void) +{ + if (snd_minor_info_entry) + snd_info_unregister(snd_minor_info_entry); + return 0; +} + +/* + * INIT PART + */ + +static int __init alsa_sound_init(void) +{ +#ifdef CONFIG_DEVFS_FS + short controlnum; + char controlname[24]; +#endif +#ifdef CONFIG_SND_OSSEMUL + int err; +#endif + int card; + + snd_ecards_limit = cards_limit; + for (card = 0; card < SNDRV_CARDS; card++) + INIT_LIST_HEAD(&snd_minors_hash[card]); +#ifdef CONFIG_SND_OSSEMUL + if ((err = snd_oss_init_module()) < 0) + return err; +#endif +#ifdef CONFIG_DEVFS_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + devfs_handle = devfs_mk_dir(NULL, "snd", 3, NULL); +#else + devfs_handle = devfs_mk_dir(NULL, "snd", NULL); +#endif +#endif + if (register_chrdev(major, "alsa", &snd_fops)) { + snd_printk(KERN_ERR "unable to register native major device number %d\n", major); + return -EIO; + } +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_init(); +#endif + if (snd_info_init() < 0) { +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_done(); +#endif + return -ENOMEM; + } +#ifdef CONFIG_SND_OSSEMUL + snd_info_minor_register(); +#endif +#ifdef CONFIG_DEVFS_FS + for (controlnum = 0; controlnum < cards_limit; controlnum++) { + sprintf(controlname, "snd/controlC%d", controlnum); + devfs_register(NULL, controlname, DEVFS_FL_DEFAULT, + major, controlnum<<5, device_mode | S_IFCHR, + &snd_fops, NULL); + } +#endif +#ifndef MODULE + printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + pm_init(); +#endif + return 0; +} + +static void __exit alsa_sound_exit(void) +{ + short controlnum; + + for (controlnum = 0; controlnum < cards_limit; controlnum++) + devfs_remove("snd/controlC%d", controlnum); + +#ifdef CONFIG_SND_OSSEMUL + snd_info_minor_unregister(); +#endif + snd_info_done(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + pm_done(); +#endif +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_done(); +#endif + if (unregister_chrdev(major, "alsa") != 0) + snd_printk(KERN_ERR "unable to unregister major device number %d\n", major); +#ifdef CONFIG_DEVFS_FS + devfs_unregister(devfs_handle); +#endif +} + +module_init(alsa_sound_init) +module_exit(alsa_sound_exit) + + /* sound.c */ +EXPORT_SYMBOL(snd_ecards_limit); +#if defined(CONFIG_KMOD) +EXPORT_SYMBOL(snd_request_card); +#endif +EXPORT_SYMBOL(snd_register_device); +EXPORT_SYMBOL(snd_unregister_device); +#if defined(CONFIG_SND_OSSEMUL) +EXPORT_SYMBOL(snd_register_oss_device); +EXPORT_SYMBOL(snd_unregister_oss_device); +#endif + /* memory.c */ +#ifdef CONFIG_SND_DEBUG_MEMORY +EXPORT_SYMBOL(snd_hidden_kmalloc); +EXPORT_SYMBOL(snd_hidden_kfree); +EXPORT_SYMBOL(snd_hidden_vmalloc); +EXPORT_SYMBOL(snd_hidden_vfree); +EXPORT_SYMBOL(_snd_magic_kmalloc); +EXPORT_SYMBOL(_snd_magic_kcalloc); +EXPORT_SYMBOL(snd_magic_kfree); +#endif +EXPORT_SYMBOL(snd_kcalloc); +EXPORT_SYMBOL(snd_kmalloc_strdup); +EXPORT_SYMBOL(copy_to_user_fromio); +EXPORT_SYMBOL(copy_from_user_toio); + /* init.c */ +EXPORT_SYMBOL(snd_cards_count); +EXPORT_SYMBOL(snd_cards); +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +EXPORT_SYMBOL(snd_mixer_oss_notify_callback); +#endif +EXPORT_SYMBOL(snd_card_new); +EXPORT_SYMBOL(snd_card_disconnect); +EXPORT_SYMBOL(snd_card_free); +EXPORT_SYMBOL(snd_card_free_in_thread); +EXPORT_SYMBOL(snd_card_register); +EXPORT_SYMBOL(snd_component_add); +EXPORT_SYMBOL(snd_card_file_add); +EXPORT_SYMBOL(snd_card_file_remove); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_power_wait); +#endif + /* device.c */ +EXPORT_SYMBOL(snd_device_new); +EXPORT_SYMBOL(snd_device_register); +EXPORT_SYMBOL(snd_device_free); +EXPORT_SYMBOL(snd_device_free_all); + /* isadma.c */ +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_dma_program); +EXPORT_SYMBOL(snd_dma_disable); +EXPORT_SYMBOL(snd_dma_pointer); +#endif + /* info.c */ +#ifdef CONFIG_PROC_FS +EXPORT_SYMBOL(snd_seq_root); +EXPORT_SYMBOL(snd_create_proc_entry); +EXPORT_SYMBOL(snd_remove_proc_entry); +EXPORT_SYMBOL(snd_iprintf); +EXPORT_SYMBOL(snd_info_get_line); +EXPORT_SYMBOL(snd_info_get_str); +EXPORT_SYMBOL(snd_info_create_module_entry); +EXPORT_SYMBOL(snd_info_create_card_entry); +EXPORT_SYMBOL(snd_info_free_entry); +EXPORT_SYMBOL(snd_info_create_device); +EXPORT_SYMBOL(snd_info_free_device); +EXPORT_SYMBOL(snd_info_register); +EXPORT_SYMBOL(snd_info_unregister); +EXPORT_SYMBOL(snd_card_proc_new); +#endif + /* info_oss.c */ +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) +EXPORT_SYMBOL(snd_oss_info_register); +#endif + /* control.c */ +EXPORT_SYMBOL(snd_ctl_new); +EXPORT_SYMBOL(snd_ctl_new1); +EXPORT_SYMBOL(snd_ctl_free_one); +EXPORT_SYMBOL(snd_ctl_add); +EXPORT_SYMBOL(snd_ctl_remove); +EXPORT_SYMBOL(snd_ctl_remove_id); +EXPORT_SYMBOL(snd_ctl_rename_id); +EXPORT_SYMBOL(snd_ctl_find_numid); +EXPORT_SYMBOL(snd_ctl_find_id); +EXPORT_SYMBOL(snd_ctl_notify); +EXPORT_SYMBOL(snd_ctl_register_ioctl); +EXPORT_SYMBOL(snd_ctl_unregister_ioctl); + /* misc.c */ +EXPORT_SYMBOL(snd_task_name); +#ifdef CONFIG_SND_VERBOSE_PRINTK +EXPORT_SYMBOL(snd_verbose_printk); +#endif +#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) +EXPORT_SYMBOL(snd_verbose_printd); +#endif + /* wrappers */ +#ifdef CONFIG_SND_DEBUG_MEMORY +EXPORT_SYMBOL(snd_wrapper_kmalloc); +EXPORT_SYMBOL(snd_wrapper_kfree); +EXPORT_SYMBOL(snd_wrapper_vmalloc); +EXPORT_SYMBOL(snd_wrapper_vfree); +#endif diff -urN linux-2.4.21-rc1.orig/sound/core/sound_oss.c linux/sound/core/sound_oss.c --- linux-2.4.21-rc1.orig/sound/core/sound_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/sound_oss.c 2002-10-13 07:16:39.000000000 -0600 @@ -0,0 +1,252 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#ifdef CONFIG_SND_OSSEMUL + +#if !defined(CONFIG_SOUND) && !defined(CONFIG_SOUND_MODULE) +#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel." +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define SNDRV_OS_MINORS 256 + +static struct list_head snd_oss_minors_hash[SNDRV_CARDS]; + +static DECLARE_MUTEX(sound_oss_mutex); + +static snd_minor_t *snd_oss_minor_search(int minor) +{ + struct list_head *list; + snd_minor_t *mptr; + + list_for_each(list, &snd_oss_minors_hash[SNDRV_MINOR_OSS_CARD(minor)]) { + mptr = list_entry(list, snd_minor_t, list); + if (mptr->number == minor) + return mptr; + } + return NULL; +} + +static int snd_oss_kernel_minor(int type, snd_card_t * card, int dev) +{ + int minor; + + switch (type) { + case SNDRV_OSS_DEVICE_TYPE_MIXER: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER)); + break; + case SNDRV_OSS_DEVICE_TYPE_SEQUENCER: + minor = SNDRV_MINOR_OSS_SEQUENCER; + break; + case SNDRV_OSS_DEVICE_TYPE_MUSIC: + minor = SNDRV_MINOR_OSS_MUSIC; + break; + case SNDRV_OSS_DEVICE_TYPE_PCM: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM)); + break; + case SNDRV_OSS_DEVICE_TYPE_MIDI: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI)); + break; + case SNDRV_OSS_DEVICE_TYPE_DMFM: + minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM); + break; + case SNDRV_OSS_DEVICE_TYPE_SNDSTAT: + minor = SNDRV_MINOR_OSS_SNDSTAT; + break; + default: + return -EINVAL; + } + snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + return minor; +} + +int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) +{ + int minor = snd_oss_kernel_minor(type, card, dev); + int minor_unit; + snd_minor_t *preg; + int cidx = SNDRV_MINOR_OSS_CARD(minor); + int track2 = -1; + int register1 = -1, register2 = -1; + + if (minor < 0) + return minor; + preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); + if (preg == NULL) + return -ENOMEM; + *preg = *reg; + preg->number = minor; + preg->device = dev; + preg->dev = NULL; + down(&sound_oss_mutex); + list_add_tail(&preg->list, &snd_oss_minors_hash[cidx]); + minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); + switch (minor_unit) { + case SNDRV_MINOR_OSS_PCM: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); + break; + case SNDRV_MINOR_OSS_MIDI: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); + break; + case SNDRV_MINOR_OSS_MIDI1: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); + break; + } + register1 = register_sound_special(reg->f_ops, minor); + if (register1 != minor) + goto __end; + if (track2 >= 0) { + register2 = register_sound_special(reg->f_ops, track2); + if (register2 != track2) + goto __end; + } + up(&sound_oss_mutex); + return 0; + + __end: + if (register2 >= 0) + unregister_sound_special(register2); + if (register1 >= 0) + unregister_sound_special(register1); + list_del(&preg->list); + up(&sound_oss_mutex); + kfree(preg); + return -EBUSY; +} + +int snd_unregister_oss_device(int type, snd_card_t * card, int dev) +{ + int minor = snd_oss_kernel_minor(type, card, dev); + int cidx = SNDRV_MINOR_OSS_CARD(minor); + int track2 = -1; + snd_minor_t *mptr; + + if (minor < 0) + return minor; + down(&sound_oss_mutex); + mptr = snd_oss_minor_search(minor); + if (mptr == NULL) { + up(&sound_oss_mutex); + return -ENOENT; + } + unregister_sound_special(minor); + switch (SNDRV_MINOR_OSS_DEVICE(minor)) { + case SNDRV_MINOR_OSS_PCM: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); + break; + case SNDRV_MINOR_OSS_MIDI: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); + break; + case SNDRV_MINOR_OSS_MIDI1: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); + break; + } + if (track2 >= 0) + unregister_sound_special(track2); + list_del(&mptr->list); + up(&sound_oss_mutex); + kfree(mptr); + return 0; +} + +/* + * INFO PART + */ + +#ifdef CONFIG_PROC_FS + +static snd_info_entry_t *snd_minor_info_oss_entry = NULL; + +static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int card, dev; + struct list_head *list; + snd_minor_t *mptr; + + down(&sound_oss_mutex); + for (card = 0; card < SNDRV_CARDS; card++) { + list_for_each(list, &snd_oss_minors_hash[card]) { + mptr = list_entry(list, snd_minor_t, list); + dev = SNDRV_MINOR_OSS_DEVICE(mptr->number); + if (dev != SNDRV_MINOR_OSS_SNDSTAT && + dev != SNDRV_MINOR_OSS_SEQUENCER && + dev != SNDRV_MINOR_OSS_MUSIC) + snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, dev, mptr->comment); + else + snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); + } + } + up(&sound_oss_mutex); +} + +#endif /* CONFIG_PROC_FS */ + +int __init snd_minor_info_oss_init(void) +{ +#ifdef CONFIG_PROC_FS + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_minor_info_oss_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_minor_info_oss_entry = entry; +#endif + return 0; +} + +int __exit snd_minor_info_oss_done(void) +{ +#ifdef CONFIG_PROC_FS + if (snd_minor_info_oss_entry) + snd_info_unregister(snd_minor_info_oss_entry); +#endif + return 0; +} + +int __init snd_oss_init_module(void) +{ + int card; + + for (card = 0; card < SNDRV_CARDS; card++) + INIT_LIST_HEAD(&snd_oss_minors_hash[card]); + return 0; +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -urN linux-2.4.21-rc1.orig/sound/core/timer.c linux/sound/core/timer.c --- linux-2.4.21-rc1.orig/sound/core/timer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/timer.c 2003-03-11 07:43:39.000000000 -0700 @@ -0,0 +1,1821 @@ +/* + * Timers abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#if !defined(CONFIG_SND_RTCTIMER) && !defined(CONFIG_SND_RTCTIMER_MODULE) +#define DEFAULT_TIMER_LIMIT 1 +#else +#define DEFAULT_TIMER_LIMIT 2 +#endif + +int timer_limit = DEFAULT_TIMER_LIMIT; +MODULE_AUTHOR("Jaroslav Kysela , Takashi Iwai "); +MODULE_DESCRIPTION("ALSA timer interface"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_PARM(timer_limit, "i"); +MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); + +typedef struct { + snd_timer_instance_t *timeri; + int tread; /* enhanced read with timestamps and events */ + unsigned long ticks; + unsigned long overrun; + int qhead; + int qtail; + int qused; + int queue_size; + snd_timer_read_t *queue; + snd_timer_tread_t *tqueue; + spinlock_t qlock; + unsigned long last_resolution; + unsigned int filter; + struct timespec tstamp; /* trigger tstamp */ + wait_queue_head_t qchange_sleep; + struct fasync_struct *fasync; +} snd_timer_user_t; + +/* list of timers */ +static LIST_HEAD(snd_timer_list); + +/* list of slave instances */ +static LIST_HEAD(snd_timer_slave_list); + +/* lock for slave active lists */ +static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED; + +static DECLARE_MUTEX(register_mutex); + +static int snd_timer_free(snd_timer_t *timer); +static int snd_timer_dev_free(snd_device_t *device); +static int snd_timer_dev_register(snd_device_t *device); +static int snd_timer_dev_unregister(snd_device_t *device); + +static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left); + +/* + * create a timer instance with the given owner string. + * when timer is not NULL, increments the module counter + */ +static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer) +{ + snd_timer_instance_t *timeri; + timeri = snd_kcalloc(sizeof(snd_timer_instance_t), GFP_KERNEL); + if (timeri == NULL) + return NULL; + timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); + if (! timeri->owner) { + kfree(timeri); + return NULL; + } + INIT_LIST_HEAD(&timeri->open_list); + INIT_LIST_HEAD(&timeri->active_list); + INIT_LIST_HEAD(&timeri->ack_list); + INIT_LIST_HEAD(&timeri->slave_list_head); + INIT_LIST_HEAD(&timeri->slave_active_head); + + timeri->timer = timer; + if (timer && timer->card && !try_module_get(timer->card->module)) { + kfree(timeri->owner); + kfree(timeri); + return NULL; + } + + return timeri; +} + +/* + * find a timer instance from the given timer id + */ +static snd_timer_t *snd_timer_find(snd_timer_id_t *tid) +{ + snd_timer_t *timer = NULL; + struct list_head *p; + + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + + if (timer->tmr_class != tid->dev_class) + continue; + if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || + timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && + (timer->card == NULL || + timer->card->number != tid->card)) + continue; + if (timer->tmr_device != tid->device) + continue; + if (timer->tmr_subdevice != tid->subdevice) + continue; + return timer; + } + return NULL; +} + +#ifdef CONFIG_KMOD + +static void snd_timer_request(snd_timer_id_t *tid) +{ + char str[32]; + + switch (tid->dev_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + if (tid->device >= timer_limit) + return; + sprintf(str, "snd-timer-%i", tid->device); + break; + case SNDRV_TIMER_CLASS_CARD: + case SNDRV_TIMER_CLASS_PCM: + if (tid->card >= snd_ecards_limit) + return; + sprintf(str, "snd-card-%i", tid->card); + break; + default: + return; + } + request_module(str); +} + +#endif + +/* + * look for a master instance matching with the slave id of the given slave. + * when found, relink the open_link of the slave. + * + * call this with register_mutex down. + */ +static void snd_timer_check_slave(snd_timer_instance_t *slave) +{ + snd_timer_t *timer; + snd_timer_instance_t *master; + struct list_head *p, *q; + + /* FIXME: it's really dumb to look up all entries.. */ + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + list_for_each(q, &timer->open_list_head) { + master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); + if (slave->slave_class == master->slave_class && + slave->slave_id == master->slave_id) { + list_del(&slave->open_list); + list_add_tail(&slave->open_list, &master->slave_list_head); + spin_lock_irq(&slave_active_lock); + slave->master = master; + slave->timer = master->timer; + spin_unlock_irq(&slave_active_lock); + return; + } + } + } +} + +/* + * look for slave instances matching with the slave id of the given master. + * when found, relink the open_link of slaves. + * + * call this with register_mutex down. + */ +static void snd_timer_check_master(snd_timer_instance_t *master) +{ + snd_timer_instance_t *slave; + struct list_head *p, *n; + + /* check all pending slaves */ + list_for_each_safe(p, n, &snd_timer_slave_list) { + slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + if (slave->slave_class == master->slave_class && + slave->slave_id == master->slave_id) { + list_del(p); + list_add_tail(p, &master->slave_list_head); + spin_lock_irq(&slave_active_lock); + slave->master = master; + slave->timer = master->timer; + if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) + list_add_tail(&slave->active_list, &master->slave_active_head); + spin_unlock_irq(&slave_active_lock); + } + } +} + +/* + * open a timer instance + * when opening a master, the slave id must be here given. + */ +int snd_timer_open(snd_timer_instance_t **ti, + char *owner, snd_timer_id_t *tid, + unsigned int slave_id) +{ + snd_timer_t *timer; + snd_timer_instance_t *timeri = NULL; + + if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { + /* open a slave instance */ + if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || + tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { + snd_printd("invalid slave class %i\n", tid->dev_sclass); + return -EINVAL; + } + down(®ister_mutex); + timeri = snd_timer_instance_new(owner, NULL); + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = tid->device; + timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; + list_add_tail(&timeri->open_list, &snd_timer_slave_list); + snd_timer_check_slave(timeri); + up(®ister_mutex); + *ti = timeri; + return 0; + } + + /* open a master instance */ + down(®ister_mutex); + timer = snd_timer_find(tid); +#ifdef CONFIG_KMOD + if (timer == NULL) { + up(®ister_mutex); + snd_timer_request(tid); + down(®ister_mutex); + timer = snd_timer_find(tid); + } +#endif + if (timer) { + if (!list_empty(&timer->open_list_head)) { + timeri = (snd_timer_instance_t *)list_entry(timer->open_list_head.next, snd_timer_instance_t, open_list); + if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { + up(®ister_mutex); + return -EBUSY; + } + } + timeri = snd_timer_instance_new(owner, timer); + if (timeri) { + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = slave_id; + if (list_empty(&timer->open_list_head) && timer->hw.open) + timer->hw.open(timer); + list_add_tail(&timeri->open_list, &timer->open_list_head); + snd_timer_check_master(timeri); + } + } else { + up(®ister_mutex); + return -ENODEV; + } + up(®ister_mutex); + *ti = timeri; + return 0; +} + +static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event); + +/* + * close a timer instance + */ +int snd_timer_close(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer = NULL; + struct list_head *p, *n; + snd_timer_instance_t *slave; + + snd_assert(timeri != NULL, return -ENXIO); + + snd_timer_stop(timeri); /* force to stop the timer */ + + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { + down(®ister_mutex); + list_del(&timeri->open_list); + up(®ister_mutex); + } else { + timer = timeri->timer; + down(®ister_mutex); + list_del(&timeri->open_list); + if (timer && list_empty(&timer->open_list_head) && timer->hw.close) + timer->hw.close(timer); + /* remove slave links */ + list_for_each_safe(p, n, &timeri->slave_list_head) { + slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + spin_lock_irq(&slave_active_lock); + _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION); + list_del(p); + list_add_tail(p, &snd_timer_slave_list); + slave->master = NULL; + slave->timer = NULL; + spin_unlock_irq(&slave_active_lock); + } + up(®ister_mutex); + } + if (timeri->private_free) + timeri->private_free(timeri); + if (timeri->owner) + kfree(timeri->owner); + kfree(timeri); + if (timer && timer->card) + module_put(timer->card->module); + return 0; +} + +unsigned long snd_timer_resolution(snd_timer_instance_t * timeri) +{ + snd_timer_t * timer; + + if (timeri == NULL) + return 0; + if ((timer = timeri->timer) != NULL) { + if (timer->hw.c_resolution) + return timer->hw.c_resolution(timer); + return timer->hw.resolution; + } + return 0; +} + +static void snd_timer_notify1(snd_timer_instance_t *ti, enum sndrv_timer_event event) +{ + snd_timer_t *timer; + unsigned long flags; + unsigned long resolution = 0; + snd_timer_instance_t *ts; + struct list_head *n; + struct timespec tstamp; + + snd_timestamp_now(&tstamp, 1); + snd_assert(event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE, return); + if (event == SNDRV_TIMER_EVENT_START || event == SNDRV_TIMER_EVENT_CONTINUE) + resolution = snd_timer_resolution(ti); + if (ti->ccallback) + ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution); + if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) + return; + timer = ti->timer; + if (timer == NULL) + return; + if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) + return; + spin_lock_irqsave(&timer->lock, flags); + list_for_each(n, &ti->slave_active_head) { + ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); + if (ts->ccallback) + ts->ccallback(ti, event + 100, &tstamp, resolution); + } + spin_unlock_irqrestore(&timer->lock, flags); +} + +static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks) +{ + list_del(&timeri->active_list); + list_add_tail(&timeri->active_list, &timer->active_list_head); + if (timer->running) { + timer->flags |= SNDRV_TIMER_FLG_RESCHED; + timeri->flags |= SNDRV_TIMER_IFLG_START; + return 1; /* delayed start */ + } else { + timer->sticks = sticks; + timer->hw.start(timer); + timer->running++; + timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + return 0; + } +} + +static int snd_timer_start_slave(snd_timer_instance_t *timeri) +{ + unsigned long flags; + + spin_lock_irqsave(&slave_active_lock, flags); + timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + if (timeri->master) + list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); + spin_unlock_irqrestore(&slave_active_lock, flags); + return 1; /* delayed start */ +} + +/* + * start the timer instance + */ +int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks) +{ + snd_timer_t *timer; + int result = -EINVAL; + unsigned long flags; + + if (timeri == NULL || ticks < 1) + return -EINVAL; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { + result = snd_timer_start_slave(timeri); + snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); + return result; + } + timer = timeri->timer; + if (timer == NULL) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + timeri->ticks = timeri->cticks = ticks; + timeri->pticks = 0; + result = snd_timer_start1(timer, timeri, ticks); + spin_unlock_irqrestore(&timer->lock, flags); + snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); + return result; +} + +static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event) +{ + snd_timer_t *timer; + unsigned long flags; + + snd_assert(timeri != NULL, return -ENXIO); + + timer = timeri->timer; + if (! timer) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + list_del_init(&timeri->ack_list); + /* wait until the callback is finished */ + while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { + spin_unlock_irqrestore(&timer->lock, flags); + udelay(10); + spin_lock_irqsave(&timer->lock, flags); + } + list_del_init(&timeri->active_list); + if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && + !(timeri->flags & SNDRV_TIMER_IFLG_SLAVE) && + !(--timer->running)) { + timer->hw.stop(timer); + if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { + timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; + snd_timer_reschedule(timer, 0); + if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { + timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + timer->hw.start(timer); + } + } + } + if (!keep_flag) + timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING|SNDRV_TIMER_IFLG_START); + spin_unlock_irqrestore(&timer->lock, flags); + if (event != SNDRV_TIMER_EVENT_RESOLUTION) + snd_timer_notify1(timeri, event); + return 0; +} + +/* + * stop the timer instance. + * + * do not call this from the timer callback! + */ +int snd_timer_stop(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer; + unsigned long flags; + int err; + + err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP); + if (err < 0) + return err; + timer = timeri->timer; + spin_lock_irqsave(&timer->lock, flags); + timeri->cticks = timeri->ticks; + timeri->pticks = 0; + spin_unlock_irqrestore(&timer->lock, flags); + return 0; +} + +/* + * start again.. the tick is kept. + */ +int snd_timer_continue(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer; + int result = -EINVAL; + unsigned long flags; + + if (timeri == NULL) + return result; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri); + timer = timeri->timer; + if (! timer) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + if (!timeri->cticks) + timeri->cticks = 1; + timeri->pticks = 0; + result = snd_timer_start1(timer, timeri, timer->sticks); + spin_unlock_irqrestore(&timer->lock, flags); + snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); + return result; +} + +/* + * pause.. remember the ticks left + */ +int snd_timer_pause(snd_timer_instance_t * timeri) +{ + return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE); +} + +/* + * reschedule the timer + * + * start pending instances and check the scheduling ticks. + * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. + */ +static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left) +{ + snd_timer_instance_t *ti; + unsigned long ticks = ~0UL; + struct list_head *p; + + list_for_each(p, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (ti->flags & SNDRV_TIMER_IFLG_START) { + ti->flags &= ~SNDRV_TIMER_IFLG_START; + ti->flags |= SNDRV_TIMER_IFLG_RUNNING; + timer->running++; + } + if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { + if (ticks > ti->cticks) + ticks = ti->cticks; + } + } + if (ticks == ~0UL) { + timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; + return; + } + if (ticks > timer->hw.ticks) + ticks = timer->hw.ticks; + if (ticks_left != ticks) + timer->flags |= SNDRV_TIMER_FLG_CHANGE; + timer->sticks = ticks; +} + +/* + * timer tasklet + * + */ +static void snd_timer_tasklet(unsigned long arg) +{ + snd_timer_t *timer = (snd_timer_t *) arg; + snd_timer_instance_t *ti; + struct list_head *p; + unsigned long resolution, ticks; + + spin_lock(&timer->lock); + /* now process all callbacks */ + while (!list_empty(&timer->sack_list_head)) { + p = timer->sack_list_head.next; /* get first item */ + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list); + + /* remove from ack_list and make empty */ + list_del_init(p); + + ticks = ti->pticks; + ti->pticks = 0; + resolution = ti->resolution; + + ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; + spin_unlock(&timer->lock); + if (ti->callback) + ti->callback(ti, resolution, ticks); + spin_lock(&timer->lock); + ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; + } + spin_unlock(&timer->lock); +} + +/* + * timer interrupt + * + * ticks_left is usually equal to timer->sticks. + * + */ +void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left) +{ + snd_timer_instance_t *ti, *ts; + unsigned long resolution, ticks; + struct list_head *p, *q, *n; + int use_tasklet = 0; + + if (timer == NULL) + return; + + spin_lock(&timer->lock); + + /* remember the current resolution */ + if (timer->hw.c_resolution) + resolution = timer->hw.c_resolution(timer); + else + resolution = timer->hw.resolution; + + /* loop for all active instances + * here we cannot use list_for_each because the active_list of a processed + * instance is relinked to done_list_head before callback is called. + */ + list_for_each_safe(p, n, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) + continue; + ti->pticks += ticks_left; + ti->resolution = resolution; + if (ti->cticks < ticks_left) + ti->cticks = 0; + else + ti->cticks -= ticks_left; + if (ti->cticks) /* not expired */ + continue; + if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { + ti->cticks = ti->ticks; + } else { + ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + if (--timer->running) + list_del(p); + } + if (list_empty(&ti->ack_list)) { + if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || + (ti->flags & SNDRV_TIMER_IFLG_FAST)) { + list_add_tail(&ti->ack_list, &timer->ack_list_head); + } else { + list_add_tail(&ti->ack_list, &timer->sack_list_head); + } + } + list_for_each(q, &ti->slave_active_head) { + ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list); + ts->pticks = ti->pticks; + ts->resolution = resolution; + if (list_empty(&ts->ack_list)) { + if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || + (ti->flags & SNDRV_TIMER_IFLG_FAST)) { + list_add_tail(&ts->ack_list, &timer->ack_list_head); + } else { + list_add_tail(&ts->ack_list, &timer->sack_list_head); + } + } + } + } + if (timer->flags & SNDRV_TIMER_FLG_RESCHED) + snd_timer_reschedule(timer, ticks_left); + if (timer->running) { + if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { + timer->hw.stop(timer); + timer->flags |= SNDRV_TIMER_FLG_CHANGE; + } + if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || + (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { + /* restart timer */ + timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + timer->hw.start(timer); + } + } else { + timer->hw.stop(timer); + } + + /* now process all fast callbacks */ + while (!list_empty(&timer->ack_list_head)) { + p = timer->ack_list_head.next; /* get first item */ + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list); + + /* remove from ack_list and make empty */ + list_del_init(p); + + ticks = ti->pticks; + ti->pticks = 0; + + ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; + spin_unlock(&timer->lock); + if (ti->callback) + ti->callback(ti, resolution, ticks); + spin_lock(&timer->lock); + ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; + } + + /* do we have any slow callbacks? */ + use_tasklet = !list_empty(&timer->sack_list_head); + spin_unlock(&timer->lock); + + if (use_tasklet) + tasklet_hi_schedule(&timer->task_queue); +} + +/* + + */ + +int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer) +{ + snd_timer_t *timer; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_timer_dev_free, + .dev_register = snd_timer_dev_register, + .dev_unregister = snd_timer_dev_unregister + }; + + snd_assert(tid != NULL, return -EINVAL); + snd_assert(rtimer != NULL, return -EINVAL); + *rtimer = NULL; + timer = snd_magic_kcalloc(snd_timer_t, 0, GFP_KERNEL); + if (timer == NULL) + return -ENOMEM; + timer->tmr_class = tid->dev_class; + timer->card = card; + timer->tmr_device = tid->device; + timer->tmr_subdevice = tid->subdevice; + if (id) + strncpy(timer->id, id, sizeof(timer->id) - 1); + INIT_LIST_HEAD(&timer->device_list); + INIT_LIST_HEAD(&timer->open_list_head); + INIT_LIST_HEAD(&timer->active_list_head); + INIT_LIST_HEAD(&timer->ack_list_head); + INIT_LIST_HEAD(&timer->sack_list_head); + spin_lock_init(&timer->lock); + tasklet_init(&timer->task_queue, snd_timer_tasklet, (unsigned long)timer); + if (card != NULL) { + if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) { + snd_timer_free(timer); + return err; + } + } + *rtimer = timer; + return 0; +} + +static int snd_timer_free(snd_timer_t *timer) +{ + snd_assert(timer != NULL, return -ENXIO); + if (timer->private_free) + timer->private_free(timer); + snd_magic_kfree(timer); + return 0; +} + +int snd_timer_dev_free(snd_device_t *device) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + return snd_timer_free(timer); +} + +int snd_timer_dev_register(snd_device_t *dev) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, dev->device_data, return -ENXIO); + snd_timer_t *timer1; + struct list_head *p; + + snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO); + if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && + !timer->hw.resolution && timer->hw.c_resolution == NULL) + return -EINVAL; + + down(®ister_mutex); + list_for_each(p, &snd_timer_list) { + timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer1->tmr_class > timer->tmr_class) + break; + if (timer1->tmr_class < timer->tmr_class) + continue; + if (timer1->card && timer->card) { + if (timer1->card->number > timer->card->number) + break; + if (timer1->card->number < timer->card->number) + continue; + } + if (timer1->tmr_device > timer->tmr_device) + break; + if (timer1->tmr_device < timer->tmr_device) + continue; + if (timer1->tmr_subdevice > timer->tmr_subdevice) + break; + if (timer1->tmr_subdevice < timer->tmr_subdevice) + continue; + /* conflicts.. */ + up(®ister_mutex); + return -EBUSY; + } + list_add_tail(&timer->device_list, p); + up(®ister_mutex); + return 0; +} + +int snd_timer_unregister(snd_timer_t *timer) +{ + struct list_head *p, *n; + snd_timer_instance_t *ti; + + snd_assert(timer != NULL, return -ENXIO); + down(®ister_mutex); + if (! list_empty(&timer->open_list_head)) { + snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer); + list_for_each_safe(p, n, &timer->open_list_head) { + list_del_init(p); + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + ti->timer = NULL; + } + } + list_del(&timer->device_list); + up(®ister_mutex); + return snd_timer_free(timer); +} + +static int snd_timer_dev_unregister(snd_device_t *device) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + return snd_timer_unregister(timer); +} + +void snd_timer_notify(snd_timer_t *timer, enum sndrv_timer_event event, struct timespec *tstamp) +{ + unsigned long flags; + unsigned long resolution = 0; + snd_timer_instance_t *ti, *ts; + struct list_head *p, *n; + + snd_runtime_check(timer->hw.flags & SNDRV_TIMER_HW_SLAVE, return); + snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && event <= SNDRV_TIMER_EVENT_MPAUSE, return); + spin_lock_irqsave(&timer->lock, flags); + if (event == SNDRV_TIMER_EVENT_MSTART || event == SNDRV_TIMER_EVENT_MCONTINUE) { + if (timer->hw.c_resolution) + resolution = timer->hw.c_resolution(timer); + else + resolution = timer->hw.resolution; + } + list_for_each(p, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (ti->ccallback) + ti->ccallback(ti, event, tstamp, resolution); + list_for_each(n, &ti->slave_active_head) { + ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); + if (ts->ccallback) + ts->ccallback(ts, event, tstamp, resolution); + } + } + spin_unlock_irqrestore(&timer->lock, flags); +} + +/* + * exported functions for global timers + */ +int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer) +{ + snd_timer_id_t tid; + + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = -1; + tid.device = device; + tid.subdevice = 0; + return snd_timer_new(NULL, id, &tid, rtimer); +} + +int snd_timer_global_free(snd_timer_t *timer) +{ + return snd_timer_free(timer); +} + +int snd_timer_global_register(snd_timer_t *timer) +{ + snd_device_t dev; + + memset(&dev, 0, sizeof(dev)); + dev.device_data = timer; + return snd_timer_dev_register(&dev); +} + +int snd_timer_global_unregister(snd_timer_t *timer) +{ + return snd_timer_unregister(timer); +} + +/* + * System timer + */ + +unsigned int snd_timer_system_resolution(void) +{ + return 1000000000L / HZ; +} + +static void snd_timer_s_function(unsigned long data) +{ + snd_timer_t *timer = (snd_timer_t *)data; + snd_timer_interrupt(timer, timer->sticks); +} + +static int snd_timer_s_start(snd_timer_t * timer) +{ + struct timer_list *tlist; + + tlist = (struct timer_list *) timer->private_data; + tlist->expires = jiffies + timer->sticks; + add_timer(tlist); + return 0; +} + +static int snd_timer_s_stop(snd_timer_t * timer) +{ + struct timer_list *tlist; + + tlist = (struct timer_list *) timer->private_data; + del_timer(tlist); + timer->sticks = tlist->expires - jiffies; + return 0; +} + +static struct _snd_timer_hardware snd_timer_system = +{ + .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, + .resolution = 1000000000L / HZ, + .ticks = 10000000L, + .start = snd_timer_s_start, + .stop = snd_timer_s_stop +}; + +static void snd_timer_free_system(snd_timer_t *timer) +{ + if (timer->private_data) + kfree(timer->private_data); +} + +static int snd_timer_register_system(void) +{ + snd_timer_t *timer; + struct timer_list *tlist; + int err; + + if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0) + return err; + strcpy(timer->name, "system timer"); + timer->hw = snd_timer_system; + tlist = (struct timer_list *) snd_kcalloc(sizeof(struct timer_list), GFP_KERNEL); + if (tlist == NULL) { + snd_timer_free(timer); + return -ENOMEM; + } + init_timer(tlist); + tlist->function = snd_timer_s_function; + tlist->data = (unsigned long) timer; + timer->private_data = tlist; + timer->private_free = snd_timer_free_system; + return snd_timer_global_register(timer); +} + +/* + * Info interface + */ + +static void snd_timer_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + unsigned long flags; + snd_timer_t *timer; + snd_timer_instance_t *ti; + struct list_head *p, *q; + + down(®ister_mutex); + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + switch (timer->tmr_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + snd_iprintf(buffer, "G%i: ", timer->tmr_device); + break; + case SNDRV_TIMER_CLASS_CARD: + snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device); + break; + case SNDRV_TIMER_CLASS_PCM: + snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice); + break; + default: + snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice); + } + snd_iprintf(buffer, "%s :", timer->name); + if (timer->hw.resolution) + snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks); + if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) + snd_iprintf(buffer, " SLAVE"); + snd_iprintf(buffer, "\n"); + spin_lock_irqsave(&timer->lock, flags); + list_for_each(q, &timer->open_list_head) { + ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); + snd_iprintf(buffer, " Client %s : %s : lost interrupts %li\n", + ti->owner ? ti->owner : "unknown", + ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped", + ti->lost); + } + spin_unlock_irqrestore(&timer->lock, flags); + } + up(®ister_mutex); +} + +/* + * USER SPACE interface + */ + +static void snd_timer_user_interrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks) +{ + snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, timeri->callback_data, return); + snd_timer_read_t *r; + int prev; + + spin_lock(&tu->qlock); + if (tu->qused > 0) { + prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; + r = &tu->queue[prev]; + if (r->resolution == resolution) { + r->ticks += ticks; + goto __wake; + } + } + if (tu->qused >= tu->queue_size) { + tu->overrun++; + } else { + r = &tu->queue[tu->qtail++]; + tu->qtail %= tu->queue_size; + r->resolution = resolution; + r->ticks = ticks; + tu->qused++; + } + __wake: + spin_unlock(&tu->qlock); + kill_fasync(&tu->fasync, SIGIO, POLL_IN); + wake_up(&tu->qchange_sleep); +} + +static void snd_timer_user_append_to_tqueue(snd_timer_user_t *tu, snd_timer_tread_t *tread) +{ + if (tu->qused >= tu->queue_size) { + tu->overrun++; + } else { + memcpy(&tu->queue[tu->qtail++], tread, sizeof(*tread)); + tu->qused++; + } +} + +static void snd_timer_user_ccallback(snd_timer_instance_t *timeri, + enum sndrv_timer_event event, + struct timespec *tstamp, + unsigned long resolution) +{ + snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, timeri->callback_data, return); + snd_timer_tread_t r1; + + if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) + tu->tstamp = *tstamp; + if ((tu->filter & (1 << event)) == 0 || !tu->tread) + return; + r1.event = event; + r1.tstamp = *tstamp; + r1.val = resolution; + spin_lock(&tu->qlock); + snd_timer_user_append_to_tqueue(tu, &r1); + spin_unlock(&tu->qlock); +} + +static void snd_timer_user_tinterrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks) +{ + snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, timeri->callback_data, return); + snd_timer_tread_t *r, r1; + struct timespec tstamp; + int prev, append = 0; + + snd_timestamp_zero(&tstamp); + spin_lock(&tu->qlock); + if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION)|(1 << SNDRV_TIMER_EVENT_TICK))) == 0) { + spin_unlock(&tu->qlock); + return; + } + if (tu->last_resolution != resolution || ticks > 0) + snd_timestamp_now(&tstamp, 1); + if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { + r1.event = SNDRV_TIMER_EVENT_RESOLUTION; + r1.tstamp = tstamp; + r1.val = resolution; + snd_timer_user_append_to_tqueue(tu, &r1); + tu->last_resolution = resolution; + append++; + } + if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) + goto __wake; + if (ticks == 0) + goto __wake; + if (tu->qused > 0) { + prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; + r = &tu->tqueue[prev]; + if (r->event == SNDRV_TIMER_EVENT_TICK) { + r->tstamp = tstamp; + r->val += ticks; + append++; + goto __wake; + } + } + r1.event = SNDRV_TIMER_EVENT_TICK; + r1.tstamp = tstamp; + r1.val = ticks; + snd_timer_user_append_to_tqueue(tu, &r1); + append++; + __wake: + spin_unlock(&tu->qlock); + if (append == 0) + return; + kill_fasync(&tu->fasync, SIGIO, POLL_IN); + wake_up(&tu->qchange_sleep); +} + +static int snd_timer_user_open(struct inode *inode, struct file *file) +{ + snd_timer_user_t *tu; + + tu = snd_magic_kcalloc(snd_timer_user_t, 0, GFP_KERNEL); + if (tu == NULL) + return -ENOMEM; + spin_lock_init(&tu->qlock); + init_waitqueue_head(&tu->qchange_sleep); + tu->ticks = 1; + tu->queue_size = 128; + tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tu->queue == NULL) { + snd_magic_kfree(tu); + return -ENOMEM; + } + file->private_data = tu; + return 0; +} + +static int snd_timer_user_release(struct inode *inode, struct file *file) +{ + snd_timer_user_t *tu; + + if (file->private_data) { + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + file->private_data = NULL; + fasync_helper(-1, file, 0, &tu->fasync); + if (tu->timeri) + snd_timer_close(tu->timeri); + if (tu->queue) + kfree(tu->queue); + if (tu->tqueue) + kfree(tu->tqueue); + snd_magic_kfree(tu); + } + return 0; +} + +static void snd_timer_user_zero_id(snd_timer_id_t *id) +{ + id->dev_class = SNDRV_TIMER_CLASS_NONE; + id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; + id->card = -1; + id->device = -1; + id->subdevice = -1; +} + +static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer) +{ + id->dev_class = timer->tmr_class; + id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; + id->card = timer->card ? timer->card->number : -1; + id->device = timer->tmr_device; + id->subdevice = timer->tmr_subdevice; +} + +static int snd_timer_user_next_device(snd_timer_id_t *_tid) +{ + snd_timer_id_t id; + snd_timer_t *timer; + struct list_head *p; + + if (copy_from_user(&id, _tid, sizeof(id))) + return -EFAULT; + down(®ister_mutex); + if (id.dev_class < 0) { /* first item */ + if (list_empty(&snd_timer_list)) + snd_timer_user_zero_id(&id); + else { + timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list); + snd_timer_user_copy_id(&id, timer); + } + } else { + switch (id.dev_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + id.device = id.device < 0 ? 0 : id.device + 1; + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_device >= id.device) { + snd_timer_user_copy_id(&id, timer); + break; + } + } + if (p == &snd_timer_list) + snd_timer_user_zero_id(&id); + break; + case SNDRV_TIMER_CLASS_CARD: + case SNDRV_TIMER_CLASS_PCM: + if (id.card < 0) { + id.card = 0; + } else { + if (id.card < 0) { + id.card = 0; + } else { + if (id.device < 0) { + id.device = 0; + } else { + id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1; + } + } + } + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer->tmr_class > id.dev_class) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_class < id.dev_class) + continue; + if (timer->card->number > id.card) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->card->number < id.card) + continue; + if (timer->tmr_device > id.device) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_device < id.device) + continue; + if (timer->tmr_subdevice > id.subdevice) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_subdevice < id.subdevice) + continue; + snd_timer_user_copy_id(&id, timer); + break; + } + if (p == &snd_timer_list) + snd_timer_user_zero_id(&id); + break; + default: + snd_timer_user_zero_id(&id); + } + } + up(®ister_mutex); + if (copy_to_user(_tid, &id, sizeof(*_tid))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_ginfo(struct file *file, snd_timer_ginfo_t *_ginfo) +{ + snd_timer_ginfo_t ginfo; + snd_timer_id_t tid; + snd_timer_t *t; + struct list_head *p; + int err = 0; + + if (copy_from_user(&ginfo, _ginfo, sizeof(ginfo))) + return -EFAULT; + tid = ginfo.tid; + memset(&ginfo, 0, sizeof(ginfo)); + ginfo.tid = tid; + down(®ister_mutex); + t = snd_timer_find(&tid); + if (t != NULL) { + ginfo.card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + ginfo.flags |= SNDRV_TIMER_FLG_SLAVE; + strncpy(ginfo.id, t->id, sizeof(ginfo.id)-1); + strncpy(ginfo.name, t->name, sizeof(ginfo.name)-1); + ginfo.resolution = t->hw.resolution; + if (t->hw.resolution_min > 0) { + ginfo.resolution_min = t->hw.resolution_min; + ginfo.resolution_max = t->hw.resolution_max; + } + list_for_each(p, &t->open_list_head) { + ginfo.clients++; + } + } else { + err = -ENODEV; + } + up(®ister_mutex); + if (err >= 0 && copy_to_user(_ginfo, &ginfo, sizeof(ginfo))) + err = -EFAULT; + return err; +} + +static int snd_timer_user_gparams(struct file *file, snd_timer_gparams_t *_gparams) +{ + snd_timer_gparams_t gparams; + snd_timer_t *t; + int err; + + if (copy_from_user(&gparams, _gparams, sizeof(gparams))) + return -EFAULT; + down(®ister_mutex); + t = snd_timer_find(&gparams.tid); + if (t != NULL) { + if (list_empty(&t->open_list_head)) { + if (t->hw.set_period) + err = t->hw.set_period(t, gparams.period_num, gparams.period_den); + else + err = -ENOSYS; + } else { + err = -EBUSY; + } + } else { + err = -ENODEV; + } + up(®ister_mutex); + return err; +} + +static int snd_timer_user_gstatus(struct file *file, snd_timer_gstatus_t *_gstatus) +{ + snd_timer_gstatus_t gstatus; + snd_timer_id_t tid; + snd_timer_t *t; + int err = 0; + + if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) + return -EFAULT; + tid = gstatus.tid; + memset(&gstatus, 0, sizeof(gstatus)); + gstatus.tid = tid; + down(®ister_mutex); + t = snd_timer_find(&tid); + if (t != NULL) { + if (t->hw.c_resolution) + gstatus.resolution = t->hw.c_resolution(t); + else + gstatus.resolution = t->hw.resolution; + if (t->hw.precise_resolution) { + t->hw.precise_resolution(t, &gstatus.resolution_num, &gstatus.resolution_den); + } else { + gstatus.resolution_num = 1; + gstatus.resolution_den = gstatus.resolution; + } + } else { + err = -ENODEV; + } + up(®ister_mutex); + if (err >= 0 && copy_from_user(_gstatus, &gstatus, sizeof(gstatus))) + err = -EFAULT; + return err; +} + +static int snd_timer_user_tselect(struct file *file, snd_timer_select_t *_tselect) +{ + snd_timer_user_t *tu; + snd_timer_select_t tselect; + char str[32]; + int err; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + if (tu->timeri) + snd_timer_close(tu->timeri); + if (copy_from_user(&tselect, _tselect, sizeof(tselect))) + return -EFAULT; + sprintf(str, "application %i", current->pid); + if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) + tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; + if ((err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid)) < 0) + return err; + + if (tu->queue) { + kfree(tu->queue); + tu->queue = NULL; + } + if (tu->tqueue) { + kfree(tu->tqueue); + tu->tqueue = NULL; + } + if (tu->tread) { + tu->tqueue = (snd_timer_tread_t *)kmalloc(tu->queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL); + if (tu->tqueue == NULL) { + snd_timer_close(tu->timeri); + return -ENOMEM; + } + } else { + tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tu->queue == NULL) { + snd_timer_close(tu->timeri); + return -ENOMEM; + } + } + + tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; + tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; + tu->timeri->ccallback = snd_timer_user_ccallback; + tu->timeri->callback_data = (void *)tu; + return 0; +} + +static int snd_timer_user_info(struct file *file, snd_timer_info_t *_info) +{ + snd_timer_user_t *tu; + snd_timer_info_t info; + snd_timer_t *t; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + memset(&info, 0, sizeof(info)); + info.card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + info.flags |= SNDRV_TIMER_FLG_SLAVE; + strncpy(info.id, t->id, sizeof(info.id)-1); + strncpy(info.name, t->name, sizeof(info.name)-1); + info.resolution = t->hw.resolution; + if (copy_to_user(_info, &info, sizeof(*_info))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_params(struct file *file, snd_timer_params_t *_params) +{ + unsigned long flags; + snd_timer_user_t *tu; + snd_timer_params_t params; + snd_timer_t *t; + snd_timer_read_t *tr; + snd_timer_tread_t *ttr; + int err; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { + err = -EINVAL; + goto _end; + } + if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { + err = -EINVAL; + goto _end; + } + if (params.filter & ~((1<timeri); + spin_lock_irqsave(&t->lock, flags); + if (params.flags & SNDRV_TIMER_PSFLG_AUTO) { + tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; + } else { + tu->timeri->flags &= ~SNDRV_TIMER_IFLG_AUTO; + } + if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) { + tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; + } else { + tu->timeri->flags &= ~SNDRV_TIMER_IFLG_EXCLUSIVE; + } + spin_unlock_irqrestore(&t->lock, flags); + if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { + if (tu->tread) { + ttr = (snd_timer_tread_t *)kmalloc(params.queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL); + if (ttr) { + kfree(tu->tqueue); + tu->queue_size = params.queue_size; + tu->tqueue = ttr; + } + } else { + tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tr) { + kfree(tu->queue); + tu->queue_size = params.queue_size; + tu->queue = tr; + } + } + } + tu->filter = params.filter; + tu->ticks = params.ticks; + err = 0; + _end: + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_timer_user_status(struct file *file, snd_timer_status_t *_status) +{ + unsigned long flags; + snd_timer_user_t *tu; + snd_timer_status_t status; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + memset(&status, 0, sizeof(status)); + status.tstamp = tu->tstamp; + status.resolution = snd_timer_resolution(tu->timeri); + status.lost = tu->timeri->lost; + status.overrun = tu->overrun; + spin_lock_irqsave(&tu->qlock, flags); + status.queue = tu->qused; + spin_unlock_irqrestore(&tu->qlock, flags); + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_start(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + snd_timer_stop(tu->timeri); + tu->timeri->lost = 0; + tu->last_resolution = 0; + return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0; +} + +static int snd_timer_user_stop(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; +} + +static int snd_timer_user_continue(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + tu->timeri->lost = 0; + return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; +} + +static int snd_timer_user_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + switch (cmd) { + case SNDRV_TIMER_IOCTL_PVERSION: + return put_user(SNDRV_TIMER_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_TIMER_IOCTL_NEXT_DEVICE: + return snd_timer_user_next_device((snd_timer_id_t *)arg); + case SNDRV_TIMER_IOCTL_TREAD: + { + int xarg; + + if (tu->timeri) /* too late */ + return -EBUSY; + if (get_user(xarg, (int *) arg)) + return -EFAULT; + tu->tread = xarg ? 1 : 0; + return 0; + } + case SNDRV_TIMER_IOCTL_GINFO: + return snd_timer_user_ginfo(file, (snd_timer_ginfo_t *)arg); + case SNDRV_TIMER_IOCTL_GPARAMS: + return snd_timer_user_gparams(file, (snd_timer_gparams_t *)arg); + case SNDRV_TIMER_IOCTL_GSTATUS: + return snd_timer_user_gstatus(file, (snd_timer_gstatus_t *)arg); + case SNDRV_TIMER_IOCTL_SELECT: + return snd_timer_user_tselect(file, (snd_timer_select_t *)arg); + case SNDRV_TIMER_IOCTL_INFO: + return snd_timer_user_info(file, (snd_timer_info_t *)arg); + case SNDRV_TIMER_IOCTL_PARAMS: + return snd_timer_user_params(file, (snd_timer_params_t *)arg); + case SNDRV_TIMER_IOCTL_STATUS: + return snd_timer_user_status(file, (snd_timer_status_t *)arg); + case SNDRV_TIMER_IOCTL_START: + return snd_timer_user_start(file); + case SNDRV_TIMER_IOCTL_STOP: + return snd_timer_user_stop(file); + case SNDRV_TIMER_IOCTL_CONTINUE: + return snd_timer_user_continue(file); + } + return -ENOTTY; +} + +static int snd_timer_user_fasync(int fd, struct file * file, int on) +{ + snd_timer_user_t *tu; + int err; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + err = fasync_helper(fd, file, on, &tu->fasync); + if (err < 0) + return err; + return 0; +} + +static ssize_t snd_timer_user_read(struct file *file, char *buffer, size_t count, loff_t *offset) +{ + snd_timer_user_t *tu; + long result = 0, unit; + int err = 0; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + unit = tu->tread ? sizeof(snd_timer_tread_t) : sizeof(snd_timer_read_t); + spin_lock_irq(&tu->qlock); + while ((long)count - result >= unit) { + while (!tu->qused) { + wait_queue_t wait; + + if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { + err = -EAGAIN; + break; + } + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + add_wait_queue(&tu->qchange_sleep, &wait); + + spin_unlock_irq(&tu->qlock); + schedule(); + spin_lock_irq(&tu->qlock); + + remove_wait_queue(&tu->qchange_sleep, &wait); + set_current_state(TASK_RUNNING); + + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + spin_unlock_irq(&tu->qlock); + if (err < 0) + break; + + if (tu->tread) { + if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], sizeof(snd_timer_tread_t))) { + err = -EFAULT; + break; + } + } else { + if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) { + err = -EFAULT; + break; + } + } + + tu->qhead %= tu->queue_size; + + result += unit; + buffer += unit; + + spin_lock_irq(&tu->qlock); + tu->qused--; + } + return result > 0 ? result : err; +} + +static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return 0); + + poll_wait(file, &tu->qchange_sleep, wait); + + mask = 0; + if (tu->qused) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static struct file_operations snd_timer_f_ops = +{ +#ifndef LINUX_2_2 + .owner = THIS_MODULE, +#endif + .read = snd_timer_user_read, + .open = snd_timer_user_open, + .release = snd_timer_user_release, + .poll = snd_timer_user_poll, + .ioctl = snd_timer_user_ioctl, + .fasync = snd_timer_user_fasync, +}; + +static snd_minor_t snd_timer_reg = +{ + .comment = "timer", + .f_ops = &snd_timer_f_ops, +}; + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_timer_proc_entry = NULL; + +static int __init alsa_timer_init(void) +{ + int err; + snd_info_entry_t *entry; + +#ifdef SNDRV_OSS_INFO_DEV_TIMERS + snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer"); +#endif + if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128; + entry->c.text.read = snd_timer_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_timer_proc_entry = entry; + if ((err = snd_timer_register_system()) < 0) + snd_printk(KERN_ERR "unable to register system timer (%i)\n", err); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, + NULL, 0, &snd_timer_reg, "timer"))<0) + snd_printk(KERN_ERR "unable to register timer device (%i)\n", err); + return 0; +} + +static void __exit alsa_timer_exit(void) +{ + struct list_head *p, *n; + + snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0); + /* unregister the system timer */ + list_for_each_safe(p, n, &snd_timer_list) { + snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + snd_timer_unregister(timer); + } + if (snd_timer_proc_entry) { + snd_info_unregister(snd_timer_proc_entry); + snd_timer_proc_entry = NULL; + } +#ifdef SNDRV_OSS_INFO_DEV_TIMERS + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); +#endif +} + +module_init(alsa_timer_init) +module_exit(alsa_timer_exit) + +EXPORT_SYMBOL(snd_timer_open); +EXPORT_SYMBOL(snd_timer_close); +EXPORT_SYMBOL(snd_timer_resolution); +EXPORT_SYMBOL(snd_timer_start); +EXPORT_SYMBOL(snd_timer_stop); +EXPORT_SYMBOL(snd_timer_continue); +EXPORT_SYMBOL(snd_timer_pause); +EXPORT_SYMBOL(snd_timer_new); +EXPORT_SYMBOL(snd_timer_notify); +EXPORT_SYMBOL(snd_timer_global_new); +EXPORT_SYMBOL(snd_timer_global_free); +EXPORT_SYMBOL(snd_timer_global_register); +EXPORT_SYMBOL(snd_timer_global_unregister); +EXPORT_SYMBOL(snd_timer_interrupt); +EXPORT_SYMBOL(snd_timer_system_resolution); diff -urN linux-2.4.21-rc1.orig/sound/core/wrappers.c linux/sound/core/wrappers.c --- linux-2.4.21-rc1.orig/sound/core/wrappers.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/core/wrappers.c 2003-02-28 07:29:21.000000000 -0700 @@ -0,0 +1,51 @@ +/* + * Various wrappers + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG_MEMORY +void *snd_wrapper_kmalloc(size_t size, int flags) +{ + return kmalloc(size, flags); +} + +void snd_wrapper_kfree(const void *obj) +{ + kfree(obj); +} + +void *snd_wrapper_vmalloc(unsigned long size) +{ + return vmalloc(size); +} + +void snd_wrapper_vfree(void *obj) +{ + vfree(obj); +} +#endif + diff -urN linux-2.4.21-rc1.orig/sound/drivers/Config.in linux/sound/drivers/Config.in --- linux-2.4.21-rc1.orig/sound/drivers/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/Config.in 2003-04-29 05:02:23.000000000 -0600 @@ -0,0 +1,12 @@ +# ALSA generic drivers + +mainmenu_option next_comment +comment 'Generic devices' + +dep_tristate 'Dummy (/dev/null) soundcard' CONFIG_SND_DUMMY $CONFIG_SND +dep_tristate 'Virtual MIDI soundcard' CONFIG_SND_VIRMIDI $CONFIG_SND $CONFIG_SND_SEQUENCER +dep_tristate 'MOTU MidiTimePiece AV multiport MIDI' CONFIG_SND_MTPAV $CONFIG_SND +dep_tristate 'UART16550 - MIDI only driver' CONFIG_SND_SERIAL_U16550 $CONFIG_SND +dep_tristate 'Generic MPU-401 UART driver' CONFIG_SND_MPU401 $CONFIG_SND + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/drivers/Makefile linux/sound/drivers/Makefile --- linux-2.4.21-rc1.orig/sound/drivers/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,36 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := drivers.o + +subdir-y := opl3 mpu401 +subdir-m := $(subdir-y) + +list-multi := snd-dummy.o snd-mtpav.o snd-serial-u16550.o snd-virmidi.o + +snd-dummy-objs := dummy.o +snd-mtpav-objs := mtpav.o +snd-serial-u16550-objs := serial-u16550.o +snd-virmidi-objs := virmidi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_DUMMY) += snd-dummy.o +obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o +obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o +obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o + +include $(TOPDIR)/Rules.make + +snd-dummy.o: $(snd-dummy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-dummy-objs) + +snd-mtpav.o: $(snd-mtpav-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mtpav-objs) + +snd-serial-u16550.o: $(snd-serial-u16550-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-serial-u16550-objs) + +snd-virmidi.o: $(snd-virmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-virmidi-objs) diff -urN linux-2.4.21-rc1.orig/sound/drivers/dummy.c linux/sound/drivers/dummy.c --- linux-2.4.21-rc1.orig/sound/drivers/dummy.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/dummy.c 2003-03-15 09:20:07.000000000 -0700 @@ -0,0 +1,637 @@ +/* + * Dummy soundcard + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Dummy soundcard (/dev/null)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA,Dummy soundcard}}"); + +#define MAX_PCM_DEVICES 4 +#define MAX_PCM_SUBSTREAMS 16 +#define MAX_MIDI_DEVICES 2 + +#if 0 /* RME9652 emulation */ +#define MAX_BUFFER_SIZE (26 * 64 * 1024) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE +#define USE_CHANNELS_MIN 26 +#define USE_CHANNELS_MAX 26 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 2 +#endif + +#if 0 /* ICE1712 emulation */ +#define MAX_BUFFER_SIZE (256 * 1024) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE +#define USE_CHANNELS_MIN 10 +#define USE_CHANNELS_MAX 10 +#define USE_PERIODS_MIN 1 +#define USE_PERIODS_MAX 1024 +#endif + +#if 0 /* UDA1341 emulation */ +#define MAX_BUFFER_SIZE (16380) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#define USE_CHANNELS_MIN 2 +#define USE_CHANNELS_MAX 2 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 255 +#endif + + +/* defaults */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE (64*1024) +#endif +#ifndef USE_FORMATS +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) +#endif +#ifndef USE_CHANNELS_MIN +#define USE_CHANNELS_MIN 1 +#endif +#ifndef USE_CHANNELS_MAX +#define USE_CHANNELS_MAX 2 +#endif +#ifndef USE_PERIODS_MIN +#define USE_PERIODS_MIN 1 +#endif +#ifndef USE_PERIODS_MAX +#define USE_PERIODS_MAX 1024 +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; +//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for dummy soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for dummy soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable this dummy soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(pcm_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver."); +MODULE_PARM_SYNTAX(pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list"); +MODULE_PARM(pcm_substreams, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); +MODULE_PARM_SYNTAX(pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list"); +//MODULE_PARM(midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); +//MODULE_PARM_SYNTAX(midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list"); + +#define MIXER_ADDR_MASTER 0 +#define MIXER_ADDR_LINE 1 +#define MIXER_ADDR_MIC 2 +#define MIXER_ADDR_SYNTH 3 +#define MIXER_ADDR_CD 4 +#define MIXER_ADDR_LAST 4 + +typedef struct snd_card_dummy { + snd_card_t *card; + spinlock_t mixer_lock; + int mixer_volume[MIXER_ADDR_LAST+1][2]; + int capture_source[MIXER_ADDR_LAST+1][2]; +} snd_card_dummy_t; + +typedef struct snd_card_dummy_pcm { + snd_card_dummy_t *dummy; + spinlock_t lock; + struct timer_list timer; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_bps; /* bytes per second */ + unsigned int pcm_jiffie; /* bytes per one jiffie */ + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int pcm_buf_pos; /* position in buffer */ + snd_pcm_substream_t *substream; +} snd_card_dummy_pcm_t; + +static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + + dpcm->timer.expires = 1 + jiffies; + add_timer(&dpcm->timer); +} + +static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + + del_timer(&dpcm->timer); +} + +static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_card_dummy_pcm_timer_start(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_card_dummy_pcm_timer_stop(substream); + } else { + return -EINVAL; + } + return 0; +} + +static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_card_dummy_pcm_timer_start(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_card_dummy_pcm_timer_stop(substream); + } else { + return -EINVAL; + } + return 0; +} + +static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + unsigned int bps; + + bps = runtime->rate * runtime->channels; + bps *= snd_pcm_format_width(runtime->format); + bps /= 8; + if (bps <= 0) + return -EINVAL; + dpcm->pcm_bps = bps; + dpcm->pcm_jiffie = bps / HZ; + dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream); + dpcm->pcm_count = snd_pcm_lib_period_bytes(substream); + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + return 0; +} + +static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream) +{ + return snd_card_dummy_pcm_prepare(substream); +} + +static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream) +{ + return snd_card_dummy_pcm_prepare(substream); +} + +static void snd_card_dummy_pcm_timer_function(unsigned long data) +{ + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, (void *)data, return); + + dpcm->timer.expires = 1 + jiffies; + add_timer(&dpcm->timer); + spin_lock_irq(&dpcm->lock); + dpcm->pcm_irq_pos += dpcm->pcm_jiffie; + dpcm->pcm_buf_pos += dpcm->pcm_jiffie; + dpcm->pcm_buf_pos %= dpcm->pcm_size; + while (dpcm->pcm_irq_pos >= dpcm->pcm_count) { + dpcm->pcm_irq_pos -= dpcm->pcm_count; + snd_pcm_period_elapsed(dpcm->substream); + } + spin_unlock_irq(&dpcm->lock); +} + +static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + + return bytes_to_frames(runtime, dpcm->pcm_buf_pos); +} + +static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + + return bytes_to_frames(runtime, dpcm->pcm_buf_pos); +} + +static snd_pcm_hardware_t snd_card_dummy_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5500, + .rate_max = 48000, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = 64, + .period_bytes_max = MAX_BUFFER_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_card_dummy_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5500, + .rate_max = 48000, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = 64, + .period_bytes_max = MAX_BUFFER_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; + +static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime) +{ + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + snd_magic_kfree(dpcm); +} + +static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm; + + dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { + snd_magic_kfree(dpcm); + return -ENOMEM; + } + init_timer(&dpcm->timer); + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_playback; + if (substream->pcm->device & 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm; + + dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { + snd_magic_kfree(dpcm); + return -ENOMEM; + } + memset(runtime->dma_area, 0, runtime->dma_bytes); + init_timer(&dpcm->timer); + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_capture; + if (substream->pcm->device == 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return 0; +} + +static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return 0; +} + +static snd_pcm_ops_t snd_card_dummy_playback_ops = { + .open = snd_card_dummy_playback_open, + .close = snd_card_dummy_playback_close, + .ioctl = snd_card_dummy_playback_ioctl, + .prepare = snd_card_dummy_playback_prepare, + .trigger = snd_card_dummy_playback_trigger, + .pointer = snd_card_dummy_playback_pointer, +}; + +static snd_pcm_ops_t snd_card_dummy_capture_ops = { + .open = snd_card_dummy_capture_open, + .close = snd_card_dummy_capture_close, + .ioctl = snd_card_dummy_capture_ioctl, + .prepare = snd_card_dummy_capture_prepare, + .trigger = snd_card_dummy_capture_trigger, + .pointer = snd_card_dummy_capture_pointer, +}; + +static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); + pcm->private_data = dummy; + pcm->info_flags = 0; + strcpy(pcm->name, "Dummy PCM"); + return 0; +} + +#define DUMMY_VOLUME(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_dummy_volume_info, \ + .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \ + .private_value = addr } + +static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value; + + spin_lock_irqsave(&dummy->mixer_lock, flags); + ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return 0; +} + +static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] % 101; + right = ucontrol->value.integer.value[1] % 101; + spin_lock_irqsave(&dummy->mixer_lock, flags); + change = dummy->mixer_volume[addr][0] != left || + dummy->mixer_volume[addr][1] != right; + dummy->mixer_volume[addr][0] = left; + dummy->mixer_volume[addr][1] = right; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return change; +} + +#define DUMMY_CAPSRC(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_dummy_capsrc_info, \ + .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \ + .private_value = addr } + +static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value; + + spin_lock_irqsave(&dummy->mixer_lock, flags); + ucontrol->value.integer.value[0] = dummy->capture_source[addr][0]; + ucontrol->value.integer.value[1] = dummy->capture_source[addr][1]; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return 0; +} + +static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + spin_lock_irqsave(&dummy->mixer_lock, flags); + change = dummy->capture_source[addr][0] != left && + dummy->capture_source[addr][1] != right; + dummy->capture_source[addr][0] = left; + dummy->capture_source[addr][1] = right; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return change; +} + +#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_dummy_controls[] = { +DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), +DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), +DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), +DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), +DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), +DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER) +}; + +int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy) +{ + snd_card_t *card = dummy->card; + unsigned int idx; + int err; + + snd_assert(dummy != NULL, return -EINVAL); + spin_lock_init(&dummy->mixer_lock); + strcpy(card->mixername, "Dummy Mixer"); + + for (idx = 0; idx < DUMMY_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0) + return err; + } + return 0; +} + +static int __init snd_card_dummy_probe(int dev) +{ + snd_card_t *card; + struct snd_card_dummy *dummy; + int idx, err; + + if (!enable[dev]) + return -ENODEV; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_dummy)); + if (card == NULL) + return -ENOMEM; + dummy = (struct snd_card_dummy *)card->private_data; + dummy->card = card; + for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) { + if (pcm_substreams[dev] < 1) + pcm_substreams[dev] = 1; + if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) + pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; + if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0) + goto __nodev; + } + if ((err = snd_card_dummy_new_mixer(dummy)) < 0) + goto __nodev; + strcpy(card->driver, "Dummy"); + strcpy(card->shortname, "Dummy"); + sprintf(card->longname, "Dummy %i", dev + 1); + if ((err = snd_card_register(card)) == 0) { + snd_dummy_cards[dev] = card; + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __init alsa_card_dummy_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (snd_card_dummy_probe(dev) < 0) { +#ifdef MODULE + printk(KERN_ERR "Dummy soundcard #%i not found or device busy\n", dev + 1); +#endif + break; + } + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Dummy soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_dummy_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_dummy_cards[idx]); +} + +module_init(alsa_card_dummy_init) +module_exit(alsa_card_dummy_exit) + +#ifndef MODULE + +/* format is: snd-dummy=enable,index,id, + pcm_devs,pcm_substreams */ + +static int __init alsa_card_dummy_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pcm_devs[nr_dev]) == 2 && + get_option(&str,&pcm_substreams[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-dummy=", alsa_card_dummy_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/drivers/mpu401/Makefile linux/sound/drivers/mpu401/Makefile --- linux-2.4.21-rc1.orig/sound/drivers/mpu401/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/mpu401/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,56 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _mpu401.o + +list-multi := snd-mpu401.o snd-mpu401-uart.o + +export-objs := mpu401_uart.o + +snd-mpu401-objs := mpu401.o +snd-mpu401-uart-objs := mpu401_uart.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_MPU401) += snd-mpu401.o snd-mpu401-uart.o +obj-$(CONFIG_SND_ALS100) += snd-mpu401-uart.o +obj-$(CONFIG_SND_AZT2320) += snd-mpu401-uart.o +obj-$(CONFIG_SND_DT019X) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES18XX) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPL3SA2) += snd-mpu401-uart.o +obj-$(CONFIG_SND_AD1816A) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4231) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4232) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4236) += snd-mpu401-uart.o +obj-$(CONFIG_SND_PC98_CS4232) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1688) += snd-mpu401-uart.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI93X) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SB16) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SBAWE) += snd-mpu401-uart.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ALS4000) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CMIPCI) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1938) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o +obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o +obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o +obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o +obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o +obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-mpu401.o: $(snd-mpu401-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-objs) + +snd-mpu401-uart.o: $(snd-mpu401-uart-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-uart-objs) diff -urN linux-2.4.21-rc1.orig/sound/drivers/mpu401/mpu401.c linux/sound/drivers/mpu401/mpu401.c --- linux-2.4.21-rc1.orig/sound/drivers/mpu401/mpu401.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/mpu401/mpu401.c 2003-01-22 02:19:38.000000000 -0700 @@ -0,0 +1,168 @@ +/* + * Driver for generic MPU-401 boards (UART mode only) + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("MPU-401 UART"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ +#ifdef CONFIG_X86_PC9800 +static int pc98ii[SNDRV_CARDS]; /* PC98-II dauther board */ +#endif + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for MPU-401 device."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for MPU-401 device."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable MPU-401 device."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for MPU-401 device."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +#ifdef CONFIG_X86_PC9800 +MODULE_PARM(pc98ii, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pc98ii, "Roland MPU-PC98II support."); +MODULE_PARM_SYNTAX(pc98ii, SNDRV_BOOLEAN_FALSE_DESC); +#endif + +static snd_card_t *snd_mpu401_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static int __init snd_card_mpu401_probe(int dev) +{ + snd_card_t *card; + int err; + + if (port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify port\n"); + return -EINVAL; + } + if (irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify or disable IRQ port\n"); + return -EINVAL; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if (snd_mpu401_uart_new(card, 0, +#ifdef CONFIG_X86_PC9800 + pc98ii[dev] ? MPU401_HW_PC98II : +#endif + MPU401_HW_MPU401, + port[dev], 0, + irq[dev], irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) { + printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]); + snd_card_free(card); + return -ENODEV; + } + strcpy(card->driver, "MPU-401 UART"); + strcpy(card->shortname, card->driver); + sprintf(card->longname, "%s at 0x%lx, ", card->shortname, port[dev]); + if (irq[dev] >= 0) { + sprintf(card->longname + strlen(card->longname), "IRQ %d", irq[dev]); + } else { + strcat(card->longname, "polled"); + } + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_mpu401_cards[dev] = card; + return 0; +} + +static int __init alsa_card_mpu401_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + if (snd_card_mpu401_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "MPU-401 device not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_mpu401_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_mpu401_cards[idx]); +} + +module_init(alsa_card_mpu401_init) +module_exit(alsa_card_mpu401_exit) + +#ifndef MODULE + +/* format is: snd-mpu401=enable,index,id,port,irq */ + +static int __init alsa_card_mpu401_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && +#ifdef CONFIG_X86_PC9800 + get_option(&str,&pc98ii[nr_dev]) == 2 && +#endif + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-mpu401=", alsa_card_mpu401_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/drivers/mpu401/mpu401_uart.c linux/sound/drivers/mpu401/mpu401_uart.c --- linux-2.4.21-rc1.orig/sound/drivers/mpu401/mpu401_uart.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/mpu401/mpu401_uart.c 2003-03-15 09:23:33.000000000 -0700 @@ -0,0 +1,546 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of MPU-401 in UART mode + * + * MPU-401 supports UART mode which is not capable generate transmit + * interrupts thus output is done via polling. Also, if irq < 0, then + * input is done also via polling. Do not expect good performance. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 13-03-2003: + * Added support for different kind of hardware I/O. Build in choices + * are port and mmio. For other kind of I/O, set mpu->read and + * mpu->write to your own I/O functions. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); +MODULE_LICENSE("GPL"); + +static void snd_mpu401_uart_input_read(mpu401_t * mpu); +static void snd_mpu401_uart_output_write(mpu401_t * mpu); + +/* + + */ + +#define snd_mpu401_input_avail(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x80)) +#define snd_mpu401_output_ready(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x40)) + +#define MPU401_RESET 0xff +#define MPU401_ENTER_UART 0x3f +#define MPU401_ACK 0xfe + +/* Build in lowlevel io */ +static void mpu401_write_port(mpu401_t *mpu, unsigned char data, unsigned long addr) +{ + outb(data, addr); +} + +static unsigned char mpu401_read_port(mpu401_t *mpu, unsigned long addr) +{ + return inb(addr); +} + +static void mpu401_write_mmio(mpu401_t *mpu, unsigned char data, unsigned long addr) +{ + writeb(data, (unsigned long*)addr); +} + +static unsigned char mpu401_read_mmio(mpu401_t *mpu, unsigned long addr) +{ + return readb((unsigned long*)addr); +} +/* */ + +static void snd_mpu401_uart_clear_rx(mpu401_t *mpu) +{ + int timeout = 100000; + for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) + mpu->read(mpu, MPU401D(mpu)); +#ifdef CONFIG_SND_DEBUG + if (timeout <= 0) + snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); +#endif +} + +static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) +{ + if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { + if (! test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) { + spin_lock(&mpu->input_lock); + snd_mpu401_uart_input_read(mpu); + spin_unlock(&mpu->input_lock); + clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); + } + } else + snd_mpu401_uart_clear_rx(mpu); + /* ok. for better Tx performance try do some output when input is done */ + if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && + test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { + spin_lock(&mpu->output_lock); + snd_mpu401_uart_output_write(mpu); + spin_unlock(&mpu->output_lock); + } +} + +/** + * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler + * @irq: the irq number + * @dev_id: mpu401 instance + * @regs: the reigster + * + * Processes the interrupt for MPU401-UART i/o. + */ +void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return); + + if (mpu == NULL) + return; + _snd_mpu401_uart_interrupt(mpu); +} + +/* + * timer callback + * reprogram the timer and call the interrupt job + */ +static void snd_mpu401_uart_timer(unsigned long data) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, (void *)data, return); + + spin_lock(&mpu->timer_lock); + /*mpu->mode |= MPU401_MODE_TIMER;*/ + mpu->timer.expires = 1 + jiffies; + add_timer(&mpu->timer); + spin_unlock(&mpu->timer_lock); + if (mpu->rmidi) + _snd_mpu401_uart_interrupt(mpu); +} + +/* + * initialize the timer callback if not programmed yet + */ +static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input) +{ + unsigned long flags; + + spin_lock_irqsave (&mpu->timer_lock, flags); + if (mpu->timer_invoked == 0) { + init_timer(&mpu->timer); + mpu->timer.data = (unsigned long)mpu; + mpu->timer.function = snd_mpu401_uart_timer; + mpu->timer.expires = 1 + jiffies; + add_timer(&mpu->timer); + } + mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER; + spin_unlock_irqrestore (&mpu->timer_lock, flags); +} + +/* + * remove the timer callback if still active + */ +static void snd_mpu401_uart_remove_timer (mpu401_t *mpu, int input) +{ + unsigned long flags; + + spin_lock_irqsave (&mpu->timer_lock, flags); + if (mpu->timer_invoked) { + mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER; + if (! mpu->timer_invoked) + del_timer(&mpu->timer); + } + spin_unlock_irqrestore (&mpu->timer_lock, flags); +} + +/* + + */ + +static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack) +{ + unsigned long flags; + int timeout, ok; + + spin_lock_irqsave(&mpu->input_lock, flags); + if (mpu->hardware != MPU401_HW_TRID4DWAVE) { + mpu->write(mpu, 0x00, MPU401D(mpu)); + /*snd_mpu401_uart_clear_rx(mpu);*/ + } + /* ok. standard MPU-401 initialization */ + if (mpu->hardware != MPU401_HW_SB) { + for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--) + udelay(10); +#ifdef CONFIG_SND_DEBUG + if (!timeout) + snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); +#endif + } + mpu->write(mpu, cmd, MPU401C(mpu)); + if (ack) { + ok = 0; + timeout = 10000; + while (!ok && timeout-- > 0) { + if (snd_mpu401_input_avail(mpu)) { + if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) + ok = 1; + } + } + if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) + ok = 1; + } else { + ok = 1; + } + spin_unlock_irqrestore(&mpu->input_lock, flags); + if (! ok) + snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); + // snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); +} + +/* + * input/output open/close - protected by open_mutex in rawmidi.c + */ +static int snd_mpu401_uart_input_open(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + int err; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) + return err; + if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); + snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_input = substream; + set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + return 0; +} + +static int snd_mpu401_uart_output_open(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + int err; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) + return err; + if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); + snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_output = substream; + set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); + return 0; +} + +static int snd_mpu401_uart_input_close(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + mpu->substream_input = NULL; + if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + if (mpu->close_input) + mpu->close_input(mpu); + return 0; +} + +static int snd_mpu401_uart_output_close(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); + mpu->substream_output = NULL; + if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + if (mpu->close_output) + mpu->close_output(mpu); + return 0; +} + +/* + * trigger input callback + */ +static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mpu401_t *mpu; + int max = 64; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + if (up) { + if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { + /* first time - flush FIFO */ + while (max-- > 0) + mpu->read(mpu, MPU401D(mpu)); + if (mpu->irq < 0) + snd_mpu401_uart_add_timer(mpu, 1); + } + + /* read data in advance */ + /* prevent double enter via rawmidi->event callback */ + if (! test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) { + spin_lock_irqsave(&mpu->input_lock, flags); + snd_mpu401_uart_input_read(mpu); + spin_unlock_irqrestore(&mpu->input_lock, flags); + clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); + } + } else { + if (mpu->irq < 0) + snd_mpu401_uart_remove_timer(mpu, 1); + clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); + } +} + +/* + * transfer input pending data + * call with input_lock spinlock held + */ +static void snd_mpu401_uart_input_read(mpu401_t * mpu) +{ + int max = 128; + unsigned char byte; + + while (max-- > 0) { + if (snd_mpu401_input_avail(mpu)) { + byte = mpu->read(mpu, MPU401D(mpu)); + if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) + snd_rawmidi_receive(mpu->substream_input, &byte, 1); + } else { + break; /* input not available */ + } + } +} + +/* + * Tx FIFO sizes: + * CS4237B - 16 bytes + * AudioDrive ES1688 - 12 bytes + * S3 SonicVibes - 8 bytes + * SoundBlaster AWE 64 - 2 bytes (ugly hardware) + */ + +/* + * write output pending bytes + * call with output_lock spinlock held + */ +static void snd_mpu401_uart_output_write(mpu401_t * mpu) +{ + unsigned char byte; + int max = 256, timeout; + + do { + if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { + for (timeout = 100; timeout > 0; timeout--) { + if (snd_mpu401_output_ready(mpu)) { + mpu->write(mpu, byte, MPU401D(mpu)); + snd_rawmidi_transmit_ack(mpu->substream_output, 1); + break; + } + } + } else { + snd_mpu401_uart_remove_timer (mpu, 0); + max = 1; /* no other data - leave the tx loop */ + } + } while (--max > 0); +} + +/* + * output trigger callback + */ +static void snd_mpu401_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + if (up) { + set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + /* try to add the timer at each output trigger, + * since the output timer might have been removed in + * snd_mpu401_uart_output_write(). + */ + snd_mpu401_uart_add_timer(mpu, 0); + + /* output pending data */ + /* prevent double enter via rawmidi->event callback */ + if (! test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode)) { + spin_lock_irqsave(&mpu->output_lock, flags); + snd_mpu401_uart_output_write(mpu); + spin_unlock_irqrestore(&mpu->output_lock, flags); + clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode); + } + } else { + snd_mpu401_uart_remove_timer(mpu, 0); + clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + } +} + +/* + + */ + +static snd_rawmidi_ops_t snd_mpu401_uart_output = +{ + .open = snd_mpu401_uart_output_open, + .close = snd_mpu401_uart_output_close, + .trigger = snd_mpu401_uart_output_trigger, +}; + +static snd_rawmidi_ops_t snd_mpu401_uart_input = +{ + .open = snd_mpu401_uart_input_open, + .close = snd_mpu401_uart_input_close, + .trigger = snd_mpu401_uart_input_trigger, +}; + +static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return); + if (mpu->irq_flags && mpu->irq >= 0) + free_irq(mpu->irq, (void *) mpu); + if (mpu->res) { + release_resource(mpu->res); + kfree_nocheck(mpu->res); + } + snd_magic_kfree(mpu); +} + +/** + * snd_mpu401_uart_new - create an MPU401-UART instance + * @card: the card instance + * @device: the device index, zero-based + * @hardware: the hardware type, MPU401_HW_XXXX + * @port: the base address of MPU401 port + * @integrated: non-zero if the port was already reserved by the chip + * @irq: the irq number, -1 if no interrupt for mpu + * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. + * @rrawmidi: the pointer to store the new rawmidi instance + * + * Creates a new MPU-401 instance. + * + * Note that the rawmidi instance is returned on the rrawmidi argument, + * not the mpu401 instance itself. To access to the mpu401 instance, + * cast from rawmidi->private_data (with mpu401_t magic-cast). + * + * Returns zero if successful, or a negative error code. + */ +int snd_mpu401_uart_new(snd_card_t * card, int device, + unsigned short hardware, + unsigned long port, int integrated, + int irq, int irq_flags, + snd_rawmidi_t ** rrawmidi) +{ + mpu401_t *mpu; + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) + return err; + mpu = snd_magic_kcalloc(mpu401_t, 0, GFP_KERNEL); + if (mpu == NULL) { + snd_device_free(card, rmidi); + return -ENOMEM; + } + rmidi->private_data = mpu; + rmidi->private_free = snd_mpu401_uart_free; + spin_lock_init(&mpu->input_lock); + spin_lock_init(&mpu->output_lock); + spin_lock_init(&mpu->timer_lock); + mpu->hardware = hardware; + if (!integrated) { + int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; + if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) { + snd_device_free(card, rmidi); + return -EBUSY; + } + } + switch (hardware) { + case MPU401_HW_AUREAL: + mpu->write = mpu401_write_mmio; + mpu->read = mpu401_read_mmio; + break; + default: + mpu->write = mpu401_write_port; + mpu->read = mpu401_read_port; + break; + } + mpu->port = port; + if (hardware == MPU401_HW_PC98II) + mpu->cport = port + 2; + else + mpu->cport = port + 1; + if (irq >= 0 && irq_flags) { + if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) { + snd_printk("unable to grab IRQ %d\n", irq); + snd_device_free(card, rmidi); + return -EBUSY; + } + } + mpu->irq = irq; + mpu->irq_flags = irq_flags; + if (card->shortname[0]) + snprintf(rmidi->name, sizeof(rmidi->name), "%s MPU-401", card->shortname); + else + sprintf(rmidi->name, "MPU-401 (UART) %d-%d", card->number, device); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + mpu->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +EXPORT_SYMBOL(snd_mpu401_uart_interrupt); +EXPORT_SYMBOL(snd_mpu401_uart_new); + +/* + * INIT part + */ + +static int __init alsa_mpu401_uart_init(void) +{ + return 0; +} + +static void __exit alsa_mpu401_uart_exit(void) +{ +} + +module_init(alsa_mpu401_uart_init) +module_exit(alsa_mpu401_uart_exit) diff -urN linux-2.4.21-rc1.orig/sound/drivers/mtpav.c linux/sound/drivers/mtpav.c --- linux-2.4.21-rc1.orig/sound/drivers/mtpav.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/mtpav.c 2003-02-19 02:33:44.000000000 -0700 @@ -0,0 +1,823 @@ +/* + * MOTU Midi Timepiece ALSA Main routines + * Copyright by Michael T. Mayers (c) Jan 09, 2000 + * mail: michael@tweakoz.com + * Thanks to John Galbraith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This driver is for the 'Mark Of The Unicorn' (MOTU) + * MidiTimePiece AV multiport MIDI interface + * + * IOPORTS + * ------- + * 8 MIDI Ins and 8 MIDI outs + * Video Sync In (BNC), Word Sync Out (BNC), + * ADAT Sync Out (DB9) + * SMPTE in/out (1/4") + * 2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs. + * Macintosh RS422 serial port + * RS422 "network" port for ganging multiple MTP's + * PC Parallel Port ( which this driver currently uses ) + * + * MISC FEATURES + * ------------- + * Hardware MIDI routing, merging, and filtering + * MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources + * 128 'scene' memories, recallable from MIDI program change + * + * + * ChangeLog + * Jun 11 2001 Takashi Iwai + * - Recoded & debugged + * - Added timer interrupt for midi outputs + * - hwports is between 1 and 8, which specifies the number of hardware ports. + * The three global ports, computer, adat and broadcast ports, are created + * always after h/w and remote ports. + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include +#include + +#include + +/* + * globals + */ +MODULE_AUTHOR("Michael T. Mayers"); +MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{MOTU,MidiTimePiece AV multiport MIDI}}"); + +// io resources +#define MTPAV_IOBASE 0x378 +#define MTPAV_IRQ 7 +#define MTPAV_MAX_PORTS 8 + +static int index = SNDRV_DEFAULT_IDX1; +static char *id = SNDRV_DEFAULT_STR1; +static long port = MTPAV_IOBASE; /* 0x378, 0x278 */ +static int irq = MTPAV_IRQ; /* 7, 5 */ +static int hwports = MTPAV_MAX_PORTS; /* use hardware ports 1-8 */ + +MODULE_PARM(index, "i"); +MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "s"); +MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(port, "l"); +MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x378},{0x278}},dialog:list"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{7},{5}},dialog:list"); +MODULE_PARM(hwports, "i"); +MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(hwports, SNDRV_ENABLED ",allows:{{1,8}},dialog:list"); + +/* + * defines + */ +//#define USE_FAKE_MTP // dont actually read/write to MTP device (for debugging without an actual unit) (does not work yet) + +// parallel port usage masks +#define SIGS_BYTE 0x08 +#define SIGS_RFD 0x80 +#define SIGS_IRQ 0x40 +#define SIGS_IN0 0x10 +#define SIGS_IN1 0x20 + +#define SIGC_WRITE 0x04 +#define SIGC_READ 0x08 +#define SIGC_INTEN 0x10 + +#define DREG 0 +#define SREG 1 +#define CREG 2 + +// +#define MTPAV_MODE_INPUT_OPENED 0x01 +#define MTPAV_MODE_OUTPUT_OPENED 0x02 +#define MTPAV_MODE_INPUT_TRIGGERED 0x04 +#define MTPAV_MODE_OUTPUT_TRIGGERED 0x08 + +#define NUMPORTS (0x12+1) + + +/* + */ + +typedef struct mtpav_port { + u8 number; + u8 hwport; + u8 mode; + u8 running_status; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; +} mtpav_port_t; + +typedef struct mtpav { + snd_card_t *card; + unsigned long port; + struct resource *res_port; + int irq; /* interrupt (for inputs) */ + spinlock_t spinlock; + int share_irq; /* number of accesses to input interrupts */ + int istimer; /* number of accesses to timer interrupts */ + struct timer_list timer; /* timer interrupts for outputs */ + snd_rawmidi_t *rmidi; + int num_ports; /* number of hw ports (1-8) */ + mtpav_port_t ports[NUMPORTS]; /* all ports including computer, adat and bc */ + + u32 inmidiport; /* selected input midi port */ + u32 inmidistate; /* during midi command 0xf5 */ + + u32 outmidihwport; /* selected output midi hw port */ +} mtpav_t; + + +/* + * global instance + * hey, we handle at most only one card.. + */ +static mtpav_t *mtp_card; + +/* + * possible hardware ports (selected by 0xf5 port message) + * 0x00 all ports + * 0x01 .. 0x08 this MTP's ports 1..8 + * 0x09 .. 0x10 networked MTP's ports (9..16) + * 0x11 networked MTP's computer port + * 0x63 to ADAT + * + * mappig: + * subdevice 0 - (X-1) ports + * X - (2*X-1) networked ports + * X computer + * X+1 ADAT + * X+2 all ports + * + * where X = chip->num_ports + */ + +#define MTPAV_PIDX_COMPUTER 0 +#define MTPAV_PIDX_ADAT 1 +#define MTPAV_PIDX_BROADCAST 2 + + +static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev) +{ + if (subdev < 0) + return 0x01; /* invalid - use port 0 as default */ + else if (subdev < chip->num_ports) + return subdev + 1; /* single mtp port */ + else if (subdev < chip->num_ports * 2) + return subdev - chip->num_ports + 0x09; /* remote port */ + else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER) + return 0x11; /* computer port */ + else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT) + return 0x63; /* ADAT */ + return 0; /* all ports */ +} + +static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport) +{ + int p; + if (hwport <= 0x00) /* all ports */ + return chip->num_ports + MTPAV_PIDX_BROADCAST; + else if (hwport <= 0x08) { /* single port */ + p = hwport - 1; + if (p >= chip->num_ports) + p = 0; + return p; + } else if (hwport <= 0x10) { /* remote port */ + p = hwport - 0x09 + chip->num_ports; + if (p >= chip->num_ports * 2) + p = chip->num_ports; + return p; + } else if (hwport == 0x11) /* computer port */ + return chip->num_ports + MTPAV_PIDX_COMPUTER; + else /* ADAT */ + return chip->num_ports + MTPAV_PIDX_ADAT; +} + + +/* + */ + +static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg) +{ + u8 rval = 0; + + if (reg == SREG) { + rval = inb(chip->port + SREG); + rval = (rval & 0xf8); + } else if (reg == CREG) { + rval = inb(chip->port + CREG); + rval = (rval & 0x1c); + } + + return rval; +} + +/* + */ + +static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val) +{ + if (reg == DREG) { + outb(val, chip->port + DREG); + } else if (reg == CREG) { + outb(val, chip->port + CREG); + } +} + +/* + */ + +static void snd_mtpav_wait_rfdhi(mtpav_t *chip) +{ + int counts = 10000; + u8 sbyte; + + sbyte = snd_mtpav_getreg(chip, SREG); + while (!(sbyte & SIGS_RFD) && counts--) { + sbyte = snd_mtpav_getreg(chip, SREG); + udelay(10); + } +} + +static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte) +{ + u8 tcbyt; + u8 clrwrite; + u8 setwrite; + + snd_mtpav_wait_rfdhi(chip); + + ///////////////// + + tcbyt = snd_mtpav_getreg(chip, CREG); + clrwrite = tcbyt & (SIGC_WRITE ^ 0xff); + setwrite = tcbyt | SIGC_WRITE; + + snd_mtpav_mputreg(chip, DREG, byte); + snd_mtpav_mputreg(chip, CREG, clrwrite); // clear write bit + + snd_mtpav_mputreg(chip, CREG, setwrite); // set write bit + +} + + +/* + */ + +/* call this with spin lock held */ +static void snd_mtpav_output_port_write(mtpav_port_t *port, + snd_rawmidi_substream_t *substream) +{ + u8 outbyte; + + // Get the outbyte first, so we can emulate running status if + // necessary + if (snd_rawmidi_transmit(substream, &outbyte, 1) != 1) + return; + + // send port change command if necessary + + if (port->hwport != mtp_card->outmidihwport) { + mtp_card->outmidihwport = port->hwport; + + snd_mtpav_send_byte(mtp_card, 0xf5); + snd_mtpav_send_byte(mtp_card, port->hwport); + //snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport); + + if (!(outbyte & 0x80) && port->running_status) + snd_mtpav_send_byte(mtp_card, port->running_status); + } + + // send data + + do { + if (outbyte & 0x80) + port->running_status = outbyte; + + snd_mtpav_send_byte(mtp_card, outbyte); + } while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1); +} + +static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream) +{ + mtpav_port_t *port = &mtp_card->ports[substream->number]; + unsigned long flags; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + snd_mtpav_output_port_write(port, substream); + spin_unlock_irqrestore(&mtp_card->spinlock, flags); +} + + +/* + * mtpav control + */ + +static void snd_mtpav_portscan(mtpav_t *chip) // put mtp into smart routing mode +{ + u8 p; + + for (p = 0; p < 8; p++) { + snd_mtpav_send_byte(chip, 0xf5); + snd_mtpav_send_byte(chip, p); + snd_mtpav_send_byte(chip, 0xfe); + } +} + +/* + */ + +static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *portp = &mtp_card->ports[substream->number]; + + //printk("mtpav port: %d opened\n", (int) substream->number); + spin_lock_irqsave(&mtp_card->spinlock, flags); + portp->mode |= MTPAV_MODE_INPUT_OPENED; + portp->input = substream; + if (mtp_card->share_irq++ == 0) + snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +} + +/* + */ + +static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream) +{ + unsigned long flags; + mtpav_port_t *portp = &mtp_card->ports[substream->number]; + + //printk("mtpav port: %d closed\n", (int) portp); + + spin_lock_irqsave(&mtp_card->spinlock, flags); + + portp->mode &= (~MTPAV_MODE_INPUT_OPENED); + portp->input = NULL; + if (--mtp_card->share_irq == 0) + snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts + + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +} + +/* + */ + +static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mtpav_port_t *portp = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + if (up) + portp->mode |= MTPAV_MODE_INPUT_TRIGGERED; + else + portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + +} + + +/* + * timer interrupt for outputs + */ + +static void snd_mtpav_output_timer(unsigned long data) +{ + mtpav_t *chip = snd_magic_cast(mtpav_t, (void *)data, return); + int p; + + spin_lock(&chip->spinlock); + /* reprogram timer */ + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); + /* process each port */ + for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) { + mtpav_port_t *portp = &mtp_card->ports[p]; + if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output) + snd_mtpav_output_port_write(portp, portp->output); + } + spin_unlock(&chip->spinlock); +} + +/* spinlock held! */ +static void snd_mtpav_add_output_timer(mtpav_t *chip) +{ + init_timer(&chip->timer); + chip->timer.function = snd_mtpav_output_timer; + chip->timer.data = (unsigned long) mtp_card; + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); +} + +/* spinlock held! */ +static void snd_mtpav_remove_output_timer(mtpav_t *chip) +{ + del_timer(&chip->timer); +} + +/* + */ + +static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *portp = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + portp->mode |= MTPAV_MODE_OUTPUT_OPENED; + portp->output = substream; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +}; + +/* + */ + +static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *portp = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + portp->mode &= (~MTPAV_MODE_OUTPUT_OPENED); + portp->output = NULL; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +}; + +/* + */ + +static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mtpav_port_t *portp = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + if (up) { + if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) { + if (mtp_card->istimer++ == 0) + snd_mtpav_add_output_timer(mtp_card); + portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED; + } + } else { + portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED; + if (--mtp_card->istimer == 0) + snd_mtpav_remove_output_timer(mtp_card); + } + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + + if (up) + snd_mtpav_output_write(substream); +} + +/* + * midi interrupt for inputs + */ + +static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte) +{ + mtpav_port_t *portp; + + if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST) + return; + + portp = &mcrd->ports[mcrd->inmidiport]; + if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED) { + spin_unlock(&mcrd->spinlock); + snd_rawmidi_receive(portp->input, &inbyte, 1); + spin_lock(&mcrd->spinlock); + } +} + +static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte) +{ + snd_assert(mcrd, return); + + if (inbyte >= 0xf8) { + /* real-time midi code */ + snd_mtpav_inmidi_process(mcrd, inbyte); + return; + } + + if (mcrd->inmidistate == 0) { // awaiting command + if (inbyte == 0xf5) // MTP port # + mcrd->inmidistate = 1; + else + snd_mtpav_inmidi_process(mcrd, inbyte); + } else if (mcrd->inmidistate) { + mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte); + mcrd->inmidistate = 0; + } +} + +static void snd_mtpav_read_bytes(mtpav_t * mcrd) +{ + u8 clrread, setread; + u8 mtp_read_byte; + u8 sr, cbyt; + int i; + + u8 sbyt = snd_mtpav_getreg(mcrd, SREG); + + //printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); + + if (!(sbyt & SIGS_BYTE)) + return; + + cbyt = snd_mtpav_getreg(mcrd, CREG); + clrread = cbyt & (SIGC_READ ^ 0xff); + setread = cbyt | SIGC_READ; + + do { + + mtp_read_byte = 0; + for (i = 0; i < 4; i++) { + snd_mtpav_mputreg(mcrd, CREG, setread); + sr = snd_mtpav_getreg(mcrd, SREG); + snd_mtpav_mputreg(mcrd, CREG, clrread); + + sr &= SIGS_IN0 | SIGS_IN1; + sr >>= 4; + mtp_read_byte |= sr << (i * 2); + } + + snd_mtpav_inmidi_h(mcrd, mtp_read_byte); + + sbyt = snd_mtpav_getreg(mcrd, SREG); + + } while (sbyt & SIGS_BYTE); +} + +static void snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs) +{ + mtpav_t *mcard = snd_magic_cast(mtpav_t, dev_id, return); + + //printk("irqh()\n"); + spin_lock(&mcard->spinlock); + snd_mtpav_read_bytes(mcard); + spin_unlock(&mcard->spinlock); +} + +/* + * get ISA resources + */ +static int snd_mtpav_get_ISA(mtpav_t * mcard) +{ + if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) { + snd_printk("MTVAP port 0x%lx is busy\n", port); + return -EBUSY; + } + mcard->port = port; + if (request_irq(irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) { + snd_printk("MTVAP IRQ %d busy\n", irq); + return -EBUSY; + } + mcard->irq = irq; + return 0; +} + + +/* + */ + +static snd_rawmidi_ops_t snd_mtpav_output = { + .open = snd_mtpav_output_open, + .close = snd_mtpav_output_close, + .trigger = snd_mtpav_output_trigger, +}; + +static snd_rawmidi_ops_t snd_mtpav_input = { + .open = snd_mtpav_input_open, + .close = snd_mtpav_input_close, + .trigger = snd_mtpav_input_trigger, +}; + + +/* + * get RAWMIDI resources + */ + +static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream) +{ + if (substream->number >= 0 && substream->number < chip->num_ports) + sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1); + else if (substream->number >= 8 && substream->number < chip->num_ports * 2) + sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); + else if (substream->number == chip->num_ports * 2) + strcpy(substream->name, "MTP computer"); + else if (substream->number == chip->num_ports * 2 + 1) + strcpy(substream->name, "MTP ADAT"); + else + strcpy(substream->name, "MTP broadcast"); +} + +static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard) +{ + int rval = 0; + snd_rawmidi_t *rawmidi; + snd_rawmidi_substream_t *substream; + struct list_head *list; + + //printk("entering snd_mtpav_get_RAWMIDI\n"); + + if (hwports < 1) + mcard->num_ports = 1; + else if (hwports > 8) + mcard->num_ports = 8; + else + mcard->num_ports = hwports; + + if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0, + mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, + mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, + &mcard->rmidi)) < 0) + return rval; + rawmidi = mcard->rmidi; + + list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_mtpav_set_name(mcard, substream); + substream->ops = &snd_mtpav_input; + } + list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_mtpav_set_name(mcard, substream); + substream->ops = &snd_mtpav_output; + mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number); + } + rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + sprintf(rawmidi->name, "MTP AV MIDI"); + //printk("exiting snd_mtpav_get_RAWMIDI() \n"); + return 0; +} + +/* + */ + +static mtpav_t *new_mtpav(void) +{ + mtpav_t *ncrd = (mtpav_t *) snd_magic_kcalloc(mtpav_t, 0, GFP_KERNEL); + if (ncrd != NULL) { + spin_lock_init(&ncrd->spinlock); + + init_timer(&ncrd->timer); + ncrd->card = NULL; + ncrd->irq = -1; + ncrd->share_irq = 0; + + ncrd->inmidiport = 0xffffffff; + ncrd->inmidistate = 0; + ncrd->outmidihwport = 0xffffffff; + } + return ncrd; +} + +/* + */ + +static void free_mtpav(mtpav_t * crd) +{ + unsigned long flags; + + spin_lock_irqsave(&crd->spinlock, flags); + if (crd->istimer > 0) + snd_mtpav_remove_output_timer(crd); + spin_unlock_irqrestore(&crd->spinlock, flags); + if (crd->irq >= 0) + free_irq(crd->irq, (void *)crd); + if (crd->res_port) { + release_resource(crd->res_port); + kfree_nocheck(crd->res_port); + } + snd_magic_kfree(crd); +} + +/* + */ + +static int __init alsa_card_mtpav_init(void) +{ + int err = 0; + char longname_buffer[80]; + + mtp_card = new_mtpav(); + if (mtp_card == NULL) + return -ENOMEM; + + mtp_card->card = snd_card_new(index, id, THIS_MODULE, 0); + if (mtp_card->card == NULL) { + free_mtpav(mtp_card); + return -ENOMEM; + } + + err = snd_mtpav_get_ISA(mtp_card); + //printk("snd_mtpav_get_ISA returned: %d\n", err); + if (err < 0) + goto __error; + + strcpy(mtp_card->card->driver, "MTPAV"); + strcpy(mtp_card->card->shortname, "MTPAV on parallel port"); + memset(longname_buffer, 0, sizeof(longname_buffer)); + sprintf(longname_buffer, "MTPAV on parallel port at"); + + err = snd_mtpav_get_RAWMIDI(mtp_card); + //snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err); + if (err < 0) + goto __error; + + err = snd_card_register(mtp_card->card); // dont snd_card_register until AFTER all cards reources done! + + //printk("snd_card_register returned %d\n", err); + if (err < 0) + goto __error; + + + snd_mtpav_portscan(mtp_card); + + printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port); + + return 0; + + __error: + snd_card_free(mtp_card->card); + free_mtpav(mtp_card); + return err; +} + +/* + */ + +static void __exit alsa_card_mtpav_exit(void) +{ + if (mtp_card == NULL) + return; + if (mtp_card->card) + snd_card_free(mtp_card->card); + free_mtpav(mtp_card); +} + +/* + */ + +module_init(alsa_card_mtpav_init) +module_exit(alsa_card_mtpav_exit) + +#ifndef MODULE + +/* format is: snd-mtpav=enable,index,id, + port,irq,hwports */ + +static int __init alsa_card_mtpav_setup(char *str) +{ + int __attribute__ ((__unused__)) enable = 1; + + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&index) == 2 && + get_id(&str,&id) == 2 && + get_option(&str,(int *)&port) == 2 && + get_option(&str,&irq) == 2 && + get_option(&str,&hwports) == 2); + return 1; +} + +__setup("snd-mtpav=", alsa_card_mtpav_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/Makefile linux/sound/drivers/opl3/Makefile --- linux-2.4.21-rc1.orig/sound/drivers/opl3/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,80 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _opl3.o + +list-multi := snd-opl3-lib.o snd-opl3-synth.o + +export-objs := opl3_lib.o + +snd-opl3-lib-objs := opl3_lib.o opl3_synth.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) +snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) +snd-opl3-synth-objs += opl3_oss.o +endif +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-opl3-lib.o +obj-$(CONFIG_SND_AZT2320) += snd-opl3-lib.o +obj-$(CONFIG_SND_DT019X) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES18XX) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-lib.o +obj-$(CONFIG_SND_AD1816A) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4232) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4236) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES1688) += snd-opl3-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI93X) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB8) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB16) += snd-opl3-lib.o +obj-$(CONFIG_SND_SBAWE) += snd-opl3-lib.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-lib.o +obj-$(CONFIG_SND_ALS4000) += snd-opl3-lib.o +obj-$(CONFIG_SND_CMIPCI) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4281) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES1938) += snd-opl3-lib.o +obj-$(CONFIG_SND_FM801) += snd-opl3-lib.o +obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-lib.o +obj-$(CONFIG_SND_YMFPCI) += snd-opl3-lib.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_ALS100) += snd-opl3-synth.o + obj-$(CONFIG_SND_AZT2320) += snd-opl3-synth.o + obj-$(CONFIG_SND_DT019X) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES18XX) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-synth.o + obj-$(CONFIG_SND_AD1816A) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4232) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4236) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES1688) += snd-opl3-synth.o + obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI93X) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB8) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB16) += snd-opl3-synth.o + obj-$(CONFIG_SND_SBAWE) += snd-opl3-synth.o + obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-synth.o + obj-$(CONFIG_SND_ALS4000) += snd-opl3-synth.o + obj-$(CONFIG_SND_CMIPCI) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4281) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES1938) += snd-opl3-synth.o + obj-$(CONFIG_SND_FM801) += snd-opl3-synth.o + obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-synth.o + obj-$(CONFIG_SND_YMFPCI) += snd-opl3-synth.o +endif + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-opl3-lib.o: $(snd-opl3-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-lib-objs) + +snd-opl3-synth.o: $(snd-opl3-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-synth-objs) diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_drums.c linux/sound/drivers/opl3/opl3_drums.c --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_drums.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_drums.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,223 @@ +/* + * Copyright (c) by Uros Bizjak + * + * OPL2/OPL3/OPL4 FM routines for internal percussion channels + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "opl3_voice.h" + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +static char snd_opl3_drum_table[47] = +{ + OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */ + OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */ + OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, OPL3_BASSDRUM_ON, /* 41 - 43 */ + OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_HIHAT_ON, /* 44 - 46 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, /* 47 - 49 */ + + OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 50 - 52 */ + OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 53 - 55 */ + OPL3_HIHAT_ON, OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, /* 56 - 58 */ + OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 59 - 61 */ + OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 62 - 64 */ + + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 65 - 67 */ + OPL3_TOMTOM_ON, OPL3_HIHAT_ON, OPL3_HIHAT_ON, /* 68 - 70 */ + OPL3_HIHAT_ON, OPL3_HIHAT_ON, OPL3_TOMTOM_ON, /* 71 - 73 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 74 - 76 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 77 - 79 */ + OPL3_CYMBAL_ON, OPL3_CYMBAL_ON /* 80 - 81 */ +}; + +typedef struct snd_opl3_drum_voice { + int voice; + int op; + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char feedback_connection; + unsigned char wave_select; +} snd_opl3_drum_voice_t; + +typedef struct snd_opl3_drum_note { + int voice; + unsigned char fnum; + unsigned char octave_f; + unsigned char feedback_connection; +} snd_opl3_drum_note_t; + +static snd_opl3_drum_voice_t bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00}; +static snd_opl3_drum_voice_t bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00}; +static snd_opl3_drum_note_t bass_note = {6, 0x90, 0x09}; + +static snd_opl3_drum_voice_t hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00}; + +static snd_opl3_drum_voice_t snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02}; +static snd_opl3_drum_note_t snare_note = {7, 0xf4, 0x0d}; + +static snd_opl3_drum_voice_t tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00}; +static snd_opl3_drum_note_t tomtom_note = {8, 0xf4, 0x09}; + +static snd_opl3_drum_voice_t cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00}; + +/* + * set drum voice characteristics + */ +void snd_opl3_drum_voice_set(opl3_t *opl3, snd_opl3_drum_voice_t *data) +{ + unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; + unsigned char voice_offset = data->voice; + unsigned short opl3_reg; + + /* Set OPL3 AM_VIB register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, data->am_vib); + + /* Set OPL3 KSL_LEVEL register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, data->ksl_level); + + /* Set OPL3 ATTACK_DECAY register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, data->attack_decay); + + /* Set OPL3 SUSTAIN_RELEASE register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, data->sustain_release); + + /* Set OPL3 FEEDBACK_CONNECTION register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, data->feedback_connection); + + /* Select waveform */ + opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, data->wave_select); +} + +/* + * Set drum voice pitch + */ +void snd_opl3_drum_note_set(opl3_t *opl3, snd_opl3_drum_note_t *data) +{ + unsigned char voice_offset = data->voice; + unsigned short opl3_reg; + + /* Set OPL3 FNUM_LOW register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, data->fnum); + + /* Set OPL3 KEYON_BLOCK register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, data->octave_f); +} + +/* + * Set drum voice volume and position + */ +void snd_opl3_drum_vol_set(opl3_t *opl3, snd_opl3_drum_voice_t *data, int vel, + snd_midi_channel_t *chan) +{ + unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; + unsigned char voice_offset = data->voice; + unsigned char reg_val; + unsigned short opl3_reg; + + /* Set OPL3 KSL_LEVEL register */ + reg_val = data->ksl_level; + snd_opl3_calc_volume(®_val, vel, chan); + opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 FEEDBACK_CONNECTION register */ + /* Set output voice connection */ + reg_val = data->feedback_connection | OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); +} + +/* + * Loads drum voices at init time + */ +void snd_opl3_load_drums(opl3_t *opl3) +{ + snd_opl3_drum_voice_set(opl3, &bass_op0); + snd_opl3_drum_voice_set(opl3, &bass_op1); + snd_opl3_drum_note_set(opl3, &bass_note); + + snd_opl3_drum_voice_set(opl3, &hihat); + + snd_opl3_drum_voice_set(opl3, &snare); + snd_opl3_drum_note_set(opl3, &snare_note); + + snd_opl3_drum_voice_set(opl3, &tomtom); + snd_opl3_drum_note_set(opl3, &tomtom_note); + + snd_opl3_drum_voice_set(opl3, &cymbal); +} + +/* + * Switch drum voice on or off + */ +void snd_opl3_drum_switch(opl3_t *opl3, int note, int vel, int on_off, + snd_midi_channel_t *chan) +{ + unsigned char drum_mask; + snd_opl3_drum_voice_t *drum_voice; + + if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE)) + return; + + if ((note < 35) || (note > 81)) + return; + drum_mask = snd_opl3_drum_table[note - 35]; + + if (on_off) { + switch (drum_mask) { + case OPL3_BASSDRUM_ON: + drum_voice = &bass_op1; + break; + case OPL3_HIHAT_ON: + drum_voice = &hihat; + break; + case OPL3_SNAREDRUM_ON: + drum_voice = &snare; + break; + case OPL3_TOMTOM_ON: + drum_voice = &tomtom; + break; + case OPL3_CYMBAL_ON: + drum_voice = &cymbal; + break; + default: + drum_voice = &tomtom; + } + + snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan); + opl3->drum_reg |= drum_mask; + } else { + opl3->drum_reg &= ~drum_mask; + } + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); +} diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_lib.c linux/sound/drivers/opl3/opl3_lib.c --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_lib.c 2002-11-04 07:42:54.000000000 -0700 @@ -0,0 +1,582 @@ +/* + * Copyright (c) by Jaroslav Kysela , + * Hannu Savolainen 1993-1996, + * Rob Hooft + * + * Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) + * + * Most if code is ported from OSS/Lite. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Hannu Savolainen 1993-1996, Rob Hooft"); +MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)"); +MODULE_LICENSE("GPL"); + +#define chip_t opl3_t + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +void snd_opl2_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * The original 2-OP synth requires a quite long delay + * after writing to a register. + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + outb((unsigned char) cmd, port); + udelay(10); + + outb((unsigned char) val, port + 1); + udelay(30); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +void snd_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * The OPL-3 survives with just two INBs + * after writing to a register. + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + outb((unsigned char) cmd, port); + inb(opl3->l_port); + inb(opl3->l_port); + + outb((unsigned char) val, port + 1); + inb(opl3->l_port); + inb(opl3->l_port); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +void snd_opl3_cs4281_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * CS4281 requires a special access to I/O registers + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + writel((unsigned int)cmd, port << 2); + udelay(10); + + writel((unsigned int)val, (port + 1) << 2); + udelay(30); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +static int snd_opl3_detect(opl3_t * opl3) +{ + /* + * This function returns 1 if the FM chip is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, stat2, signature; + + /* Reset timers 1 and 2 */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); + /* Reset the IRQ of the FM chip */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); + signature = stat1 = inb(opl3->l_port); /* Status register */ + if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */ + snd_printd("OPL3: stat1 = 0x%x\n", stat1); + return -ENODEV; + } + /* Set timer1 to 0xff */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff); + /* Unmask and start timer 1 */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START); + /* Now we have to delay at least 80us */ + udelay(200); + /* Read status after timers have expired */ + stat2 = inb(opl3->l_port); + /* Stop the timers */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); + /* Reset the IRQ of the FM chip */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); + if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */ + snd_printd("OPL3: stat2 = 0x%x\n", stat2); + return -ENODEV; + } + + /* If the toplevel code knows exactly the type of chip, don't try + to detect it. */ + if (opl3->hardware != OPL3_HW_AUTO) + return 0; + + /* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */ + if (signature == 0x06) { /* OPL2 */ + opl3->hardware = OPL3_HW_OPL2; + } else { + /* + * Detect availability of OPL4. Unfortunately, the OPL4 + * port of the chip may not be connected to the PC bus. + */ + snd_assert(opl3->r_port != 0, return -ENODEV); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE); + /* All OPL4 registers are readable. */ + if (inb(opl3->r_port + 1) == (OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE)) { + opl3->hardware = OPL3_HW_OPL4; + } else { + opl3->hardware = OPL3_HW_OPL3; + } + } + return 0; +} + +/* + * AdLib timers + */ + +/* + * Timer 1 - 80us + */ + +static int snd_opl3_timer1_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + ticks = timer->sticks; + tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +static int snd_opl3_timer1_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +/* + * Timer 2 - 320us + */ + +static int snd_opl3_timer2_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + ticks = timer->sticks; + tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +static int snd_opl3_timer2_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +/* + + */ + +static struct _snd_timer_hardware snd_opl3_timer1 = +{ + .flags = SNDRV_TIMER_HW_STOP, + .resolution = 80000, + .ticks = 256, + .start = snd_opl3_timer1_start, + .stop = snd_opl3_timer1_stop, +}; + +static struct _snd_timer_hardware snd_opl3_timer2 = +{ + .flags = SNDRV_TIMER_HW_STOP, + .resolution = 320000, + .ticks = 256, + .start = snd_opl3_timer2_start, + .stop = snd_opl3_timer2_stop, +}; + +static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no) +{ + snd_timer_t *timer = NULL; + snd_timer_id_t tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = opl3->card->number; + tid.device = timer_no; + tid.subdevice = 0; + if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) { + strcpy(timer->name, "AdLib timer #1"); + timer->private_data = opl3; + timer->hw = snd_opl3_timer1; + } + opl3->timer1 = timer; + return err; +} + +static int snd_opl3_timer2_init(opl3_t * opl3, int timer_no) +{ + snd_timer_t *timer = NULL; + snd_timer_id_t tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = opl3->card->number; + tid.device = timer_no; + tid.subdevice = 0; + if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) { + strcpy(timer->name, "AdLib timer #2"); + timer->private_data = opl3; + timer->hw = snd_opl3_timer2; + } + opl3->timer2 = timer; + return err; +} + +/* + + */ + +void snd_opl3_interrupt(snd_hwdep_t * hw) +{ + unsigned char status; + opl3_t *opl3; + snd_timer_t *timer; + + if (hw == NULL) + return; + + opl3 = snd_magic_cast(opl3_t, hw->private_data, return); + status = inb(opl3->l_port); +#if 0 + snd_printk("AdLib IRQ status = 0x%x\n", status); +#endif + if (!(status & 0x80)) + return; + + if (status & 0x40) { + timer = opl3->timer1; + snd_timer_interrupt(timer, timer->sticks); + } + if (status & 0x20) { + timer = opl3->timer2; + snd_timer_interrupt(timer, timer->sticks); + } +} + +/* + + */ + +static int snd_opl3_free(opl3_t *opl3) +{ + if (opl3->res_l_port) { + release_resource(opl3->res_l_port); + kfree_nocheck(opl3->res_l_port); + } + if (opl3->res_r_port) { + release_resource(opl3->res_r_port); + kfree_nocheck(opl3->res_r_port); + } + snd_magic_kfree(opl3); + return 0; +} + +static int snd_opl3_dev_free(snd_device_t *device) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, device->device_data, return -ENXIO); + return snd_opl3_free(opl3); +} + +int snd_opl3_create(snd_card_t * card, + unsigned long l_port, + unsigned long r_port, + unsigned short hardware, + int integrated, + opl3_t ** ropl3) +{ + opl3_t *opl3; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_opl3_dev_free, + }; + + *ropl3 = NULL; + + opl3 = snd_magic_kcalloc(opl3_t, 0, GFP_KERNEL); + if (opl3 == NULL) + return -ENOMEM; + + if (integrated) + goto __step1; /* ports are already reserved */ + + if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) { + snd_opl3_free(opl3); + return -EBUSY; + } + if (r_port != 0 && + (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) { + snd_opl3_free(opl3); + return -EBUSY; + } + + __step1: + + opl3->card = card; + opl3->hardware = hardware; + opl3->l_port = l_port; + opl3->r_port = r_port; + + spin_lock_init(&opl3->reg_lock); + spin_lock_init(&opl3->timer_lock); + init_MUTEX(&opl3->access_mutex); + + switch (opl3->hardware) { + /* some hardware doesn't support timers */ + case OPL3_HW_OPL3_SV: + case OPL3_HW_OPL3_CS: + case OPL3_HW_OPL3_FM801: + opl3->command = &snd_opl3_command; + break; + case OPL3_HW_OPL3_PC98: + opl3->command = &snd_opl3_command; + + /* Initialize? */ + opl3->command(opl3, OPL3_RIGHT | 0x05, 0x05); + opl3->command(opl3, OPL3_RIGHT | 0x08, 0x04); + opl3->command(opl3, OPL3_RIGHT | 0x08, 0x00); + opl3->command(opl3, OPL3_LEFT | 0xf7, 0x00); + opl3->command(opl3, OPL3_LEFT | 0x04, 0x60); + opl3->command(opl3, OPL3_LEFT | 0x04, 0x80); + inb(opl3->l_port); + + opl3->command(opl3, OPL3_LEFT | 0x02, 0xff); + opl3->command(opl3, OPL3_LEFT | 0x04, 0x21); + inb(opl3->l_port); + + opl3->command(opl3, OPL3_LEFT | 0x04, 0x60); + opl3->command(opl3, OPL3_LEFT | 0x04, 0x80); + + break; + case OPL3_HW_OPL3_CS4281: + opl3->command = &snd_opl3_cs4281_command; + break; + default: + opl3->command = &snd_opl2_command; + if ((err = snd_opl3_detect(opl3)) < 0) { + snd_opl3_free(opl3); + snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n", + opl3->l_port, opl3->r_port); + return err; + } + /* detect routine returns correct hardware type */ + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL3: + case OPL3_HW_OPL4: + opl3->command = &snd_opl3_command; + } + } + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ + + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL2: + opl3->max_voices = MAX_OPL2_VOICES; + break; + case OPL3_HW_OPL3: + case OPL3_HW_OPL4: + opl3->max_voices = MAX_OPL3_VOICES; + snd_assert(opl3->r_port != 0, snd_opl3_free(opl3); return -ENODEV); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, opl3, &ops)) < 0) { + snd_opl3_free(opl3); + return err; + } + + *ropl3 = opl3; + return 0; +} + +int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev) +{ + int err; + + if (timer1_dev >= 0) + if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0) + return err; + if (timer2_dev >= 0) { + if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) { + snd_device_free(opl3->card, opl3->timer1); + opl3->timer1 = NULL; + return err; + } + } + return 0; +} + +int snd_opl3_hwdep_new(opl3_t * opl3, + int device, int seq_device, + snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hw; + snd_card_t *card = opl3->card; + int err; + + if (rhwdep) + *rhwdep = NULL; + + /* create hardware dependent device (direct FM) */ + + if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) { + snd_device_free(card, opl3); + return err; + } + hw->private_data = opl3; +#ifdef CONFIG_SND_OSSEMUL + if (device == 0) { + hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; + sprintf(hw->oss_dev, "dmfm%i", card->number); + } +#endif + strcpy(hw->name, hw->id); + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL2: + strcpy(hw->name, "OPL2 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL2; + break; + case OPL3_HW_OPL3: + strcpy(hw->name, "OPL3 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL3; + break; + case OPL3_HW_OPL4: + strcpy(hw->name, "OPL4 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL4; + break; + } + + /* operators - only ioctl */ + hw->ops.open = snd_opl3_open; + hw->ops.ioctl = snd_opl3_ioctl; + hw->ops.release = snd_opl3_release; + + opl3->seq_dev_num = seq_device; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, + sizeof(opl3_t*), &opl3->seq_dev) >= 0) { + strcpy(opl3->seq_dev->name, hw->name); + *(opl3_t**)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; + } +#endif + if (rhwdep) + *rhwdep = hw; + return 0; +} + +EXPORT_SYMBOL(snd_opl3_interrupt); +EXPORT_SYMBOL(snd_opl3_create); +EXPORT_SYMBOL(snd_opl3_timer_new); +EXPORT_SYMBOL(snd_opl3_hwdep_new); + +/* opl3_synth.c */ +EXPORT_SYMBOL(snd_opl3_regmap); +EXPORT_SYMBOL(snd_opl3_reset); + +/* + * INIT part + */ + +static int __init alsa_opl3_init(void) +{ + return 0; +} + +static void __exit alsa_opl3_exit(void) +{ +} + +module_init(alsa_opl3_init) +module_exit(alsa_opl3_exit) diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_midi.c linux/sound/drivers/opl3/opl3_midi.c --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_midi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_midi.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,873 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Midi synth routines for OPL2/OPL3/OPL4 FM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#undef DEBUG_ALLOC +#undef DEBUG_MIDI + +#include "opl3_voice.h" +#include + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +extern int use_internal_drums; + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (Rob Hooft ) + */ + +static char opl3_volume_table[128] = +{ + -63, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; + +void snd_opl3_calc_volume(unsigned char *volbyte, int vel, + snd_midi_channel_t *chan) +{ + int oldvol, newvol, n; + int volume; + + volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127); + if (volume > 127) + volume = 127; + + oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK); + + newvol = opl3_volume_table[volume] + oldvol; + if (newvol > OPL3_TOTAL_LEVEL_MASK) + newvol = OPL3_TOTAL_LEVEL_MASK; + else if (newvol < 0) + newvol = 0; + + n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK); + + *volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK); +} + +/* + * Converts the note frequency to block and fnum values for the FM chip + */ +static short opl3_note_table[16] = +{ + 305, 323, /* for pitch bending, -2 semitones */ + 343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, + 686, 726 /* for pitch bending, +2 semitones */ +}; + +static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum, + int note, snd_midi_channel_t *chan) +{ + int block = ((note / 12) & 0x07) - 1; + int idx = (note % 12) + 2; + int freq; + + if (chan->midi_pitchbend) { + int pitchbend = chan->midi_pitchbend; + int segment; + + if (pitchbend > 0x1FFF) + pitchbend = 0x1FFF; + + segment = pitchbend / 0x1000; + freq = opl3_note_table[idx+segment]; + freq += ((opl3_note_table[idx+segment+1] - freq) * + (pitchbend % 0x1000)) / 0x1000; + } else { + freq = opl3_note_table[idx]; + } + + *fnum = (unsigned char) freq; + *blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) | + ((block << 2) & OPL3_BLOCKNUM_MASK); +} + + +#ifdef DEBUG_ALLOC +static void debug_alloc(opl3_t *opl3, char *s, int voice) { + int i; + char *str = "x.24"; + + printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice); + for (i = 0; i < opl3->max_voices; i++) + printk("%c", *(str + opl3->voices[i].state + 1)); + printk("\n"); +} +#endif + +/* + * Get a FM voice (channel) to play a note on. + */ +static int opl3_get_voice(opl3_t *opl3, int instr_4op, + snd_midi_channel_t *chan) { + int chan_4op_1; /* first voice for 4op instrument */ + int chan_4op_2; /* second voice for 4op instrument */ + + snd_opl3_voice_t *vp, *vp2; + unsigned int voice_time; + int i; + +#ifdef DEBUG_ALLOC + char *alloc_type[3] = { "FREE ", "CHEAP ", "EXPENSIVE" }; +#endif + + /* This is our "allocation cost" table */ + enum { + FREE = 0, CHEAP, EXPENSIVE, END + }; + + /* Keeps track of what we are finding */ + struct best { + unsigned int time; + int voice; + } best[END]; + struct best *bp; + + for (i = 0; i < END; i++) { + best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* Look through all the channels for the most suitable. */ + for (i = 0; i < opl3->max_voices; i++) { + vp = &opl3->voices[i]; + + if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL) + /* skip unavailable channels, allocated by + drum voices or by bounded 4op voices) */ + continue; + + voice_time = vp->time; + bp = best; + + chan_4op_1 = ((i < 3) || (i > 8 && i < 12)); + chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15)); + if (instr_4op) { + /* allocate 4op voice */ + /* skip channels unavailable to 4op instrument */ + if (!chan_4op_1) + continue; + + if (vp->state) + /* kill one voice, CHEAP */ + bp++; + /* get state of bounded 2op channel + to be allocated for 4op instrument */ + vp2 = &opl3->voices[i + 3]; + if (vp2->state == SNDRV_OPL3_ST_ON_2OP) { + /* kill two voices, EXPENSIVE */ + bp++; + voice_time = (voice_time > vp->time) ? + voice_time : vp->time; + } + } else { + /* allocate 2op voice */ + if ((chan_4op_1) || (chan_4op_2)) + /* use bounded channels for 2op, CHEAP */ + bp++; + else if (vp->state) + /* kill one voice on 2op channel, CHEAP */ + bp++; + /* raise kill cost to EXPENSIVE for all channels */ + if (vp->state) + bp++; + } + if (voice_time < bp->time) { + bp->time = voice_time; + bp->voice = i; + } + } + + for (i = 0; i < END; i++) { + if (best[i].voice >= 0) { +#ifdef DEBUG_ALLOC + printk("%s %iop allocation on voice %i\n", + alloc_type[i], instr_4op ? 4 : 2, + best[i].voice); +#endif + return best[i].voice; + } + } + /* not found */ + return -1; +} + +/* ------------------------------ */ + +/* + * System timer interrupt function + */ +void snd_opl3_timer_func(unsigned long data) +{ + + opl3_t *opl3 = (opl3_t *)data; + int again = 0; + int i; + + spin_lock(&opl3->sys_timer_lock); + for (i = 0; i < opl3->max_voices; i++) { + snd_opl3_voice_t *vp = &opl3->voices[i]; + if (vp->state > 0 && vp->note_off_check) { + if (vp->note_off == jiffies) + snd_opl3_note_off(opl3, vp->note, 0, vp->chan); + else + again++; + } + } + if (again) { + opl3->tlist.expires = jiffies + 1; /* invoke again */ + add_timer(&opl3->tlist); + } else { + opl3->sys_timer_status = 0; + } + spin_unlock(&opl3->sys_timer_lock); +} + +/* + * Start system timer + */ +void snd_opl3_start_timer(opl3_t *opl3) +{ + unsigned long flags; + spin_lock_irqsave(&opl3->sys_timer_lock, flags); + if (! opl3->sys_timer_status) { + opl3->tlist.expires = jiffies + 1; + add_timer(&opl3->tlist); + opl3->sys_timer_status = 1; + } + spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); +} + +/* ------------------------------ */ + + +static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { + 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14 +}; + +/* + * Start a note. + */ +void snd_opl3_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + snd_seq_instr_t wanted; + snd_seq_kinstr_t *kinstr; + int instr_4op; + + int voice; + snd_opl3_voice_t *vp, *vp2; + unsigned short connect_mask; + unsigned char connection; + unsigned char vol_op[4]; + + int extra_prg = 0; + + unsigned short reg_side; + unsigned char op_offset; + unsigned char voice_offset; + unsigned short opl3_reg; + unsigned char reg_val; + + int key = note; + unsigned char fnum, blocknum; + int i; + + fm_instrument_t *fm; + unsigned long flags; + + opl3 = snd_magic_cast(opl3_t, p, return); + +#ifdef DEBUG_MIDI + snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", + chan->number, chan->midi_program, note, vel); +#endif + wanted.cluster = 0; + wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; + + /* in SYNTH mode, application takes care of voices */ + /* in SEQ mode, drum voice numbers are notes on drum channel */ + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + if (chan->drum_channel) { + /* percussion instruments are located in bank 128 */ + wanted.bank = 128; + wanted.prg = note; + } else { + wanted.bank = chan->gm_bank_select; + wanted.prg = chan->midi_program; + } + } else { + /* Prepare for OSS mode */ + if (chan->number >= MAX_OPL3_VOICES) + return; + + /* OSS instruments are located in bank 127 */ + wanted.bank = 127; + wanted.prg = chan->midi_program; + } + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (use_internal_drums) { + snd_opl3_drum_switch(opl3, note, vel, 1, chan); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + + __extra_prg: + kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); + if (kinstr == NULL) { + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + + fm = KINSTR_DATA(kinstr); + + switch (fm->type) { + case FM_PATCH_OPL2: + instr_4op = 0; + break; + case FM_PATCH_OPL3: + if (opl3->hardware >= OPL3_HW_OPL3) { + instr_4op = 1; + break; + } + default: + snd_seq_instr_free_use(opl3->ilist, kinstr); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + +#ifdef DEBUG_MIDI + snd_printk(" --> OPL%i instrument: %s\n", + instr_4op ? 3 : 2, kinstr->name); +#endif + /* in SYNTH mode, application takes care of voices */ + /* in SEQ mode, allocate voice on free OPL3 channel */ + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + voice = opl3_get_voice(opl3, instr_4op, chan); + } else { + /* remap OSS voice */ + voice = snd_opl3_oss_map[chan->number]; + } + + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38; + } + + /* kill voice on channel */ + vp = &opl3->voices[voice]; + if (vp->state > 0) { + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; + opl3->command(opl3, opl3_reg, reg_val); + } + if (instr_4op) { + vp2 = &opl3->voices[voice + 3]; + if (vp->state > 0) { + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + + voice_offset + 3); + reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; + opl3->command(opl3, opl3_reg, reg_val); + } + } + + /* set connection register */ + if (instr_4op) { + if ((opl3->connection_reg ^ connect_mask) & connect_mask) { + opl3->connection_reg |= connect_mask; + /* set connection bit */ + opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; + opl3->command(opl3, opl3_reg, opl3->connection_reg); + } + } else { + if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) { + opl3->connection_reg &= ~connect_mask; + /* clear connection bit */ + opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; + opl3->command(opl3, opl3_reg, opl3->connection_reg); + } + } + +#ifdef DEBUG_MIDI + snd_printk(" --> setting OPL3 connection: 0x%x\n", + opl3->connection_reg); +#endif + /* + * calculate volume depending on connection + * between FM operators (see include/opl3.h) + */ + for (i = 0; i < (instr_4op ? 4 : 2); i++) + vol_op[i] = fm->op[i].ksl_level; + + connection = fm->feedback_connection[0] & 0x01; + if (instr_4op) { + connection <<= 1; + connection |= fm->feedback_connection[1] & 0x01; + + snd_opl3_calc_volume(&vol_op[3], vel, chan); + switch (connection) { + case 0x03: + snd_opl3_calc_volume(&vol_op[2], vel, chan); + /* fallthru */ + case 0x02: + snd_opl3_calc_volume(&vol_op[0], vel, chan); + break; + case 0x01: + snd_opl3_calc_volume(&vol_op[1], vel, chan); + } + } else { + snd_opl3_calc_volume(&vol_op[1], vel, chan); + if (connection) + snd_opl3_calc_volume(&vol_op[0], vel, chan); + } + + /* Program the FM voice characteristics */ + for (i = 0; i < (instr_4op ? 4 : 2); i++) { +#ifdef DEBUG_MIDI + snd_printk(" --> programming operator %i\n", i); +#endif + op_offset = snd_opl3_regmap[voice_offset][i]; + + /* Set OPL3 AM_VIB register of requested voice/operator */ + reg_val = fm->op[i].am_vib; + opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 KSL_LEVEL register of requested voice/operator */ + reg_val = vol_op[i]; + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ + reg_val = fm->op[i].attack_decay; + opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ + reg_val = fm->op[i].sustain_release; + opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Select waveform */ + reg_val = fm->op[i].wave_select; + opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + } + + /* Set operator feedback and 2op inter-operator connection */ + reg_val = fm->feedback_connection[0]; + /* Set output voice connection */ + reg_val |= OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + if (instr_4op) { + /* Set 4op inter-operator connection */ + reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT; + /* Set output voice connection */ + reg_val |= OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + + voice_offset + 3); + opl3->command(opl3, opl3_reg, reg_val); + } + + /* + * Special treatment of percussion notes for fm: + * Requested pitch is really program, and pitch for + * device is whatever was specified in the patch library. + */ + if (fm->fix_key) + note = fm->fix_key; + /* + * use transpose if defined in patch library + */ + if (fm->trnsps) + note += (fm->trnsps - 64); + + snd_opl3_calc_pitch(&fnum, &blocknum, note, chan); + + /* Set OPL3 FNUM_LOW register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, fnum); + + opl3->voices[voice].keyon_reg = blocknum; + + /* Set output sound flag */ + blocknum |= OPL3_KEYON_BIT; + +#ifdef DEBUG_MIDI + snd_printk(" --> trigger voice %i\n", voice); +#endif + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, blocknum); + + /* kill note after fixed duration (in centiseconds) */ + if (fm->fix_dur) { + opl3->voices[voice].note_off = jiffies + + (fm->fix_dur * HZ) / 100; + snd_opl3_start_timer(opl3); + opl3->voices[voice].note_off_check = 1; + } else + opl3->voices[voice].note_off_check = 0; + + /* get extra pgm, but avoid possible loops */ + extra_prg = (extra_prg) ? 0 : fm->modes; + + snd_seq_instr_free_use(opl3->ilist, kinstr); + + /* do the bookkeeping */ + vp->time = opl3->use_time++; + vp->note = key; + vp->chan = chan; + + if (instr_4op) { + vp->state = SNDRV_OPL3_ST_ON_4OP; + + vp2 = &opl3->voices[voice + 3]; + vp2->time = opl3->use_time++; + vp2->note = key; + vp2->chan = chan; + vp2->state = SNDRV_OPL3_ST_NOT_AVAIL; + } else { + if (vp->state == SNDRV_OPL3_ST_ON_4OP) { + /* 4op killed by 2op, release bounded voice */ + vp2 = &opl3->voices[voice + 3]; + vp2->time = opl3->use_time++; + vp2->state = SNDRV_OPL3_ST_OFF; + } + vp->state = SNDRV_OPL3_ST_ON_2OP; + } + +#ifdef DEBUG_ALLOC + debug_alloc(opl3, "note on ", voice); +#endif + + /* allocate extra program if specified in patch library */ + if (extra_prg) { + if (extra_prg > 128) { + wanted.bank = 128; + /* percussions start at 35 */ + wanted.prg = extra_prg - 128 + 35 - 1; + } else { + wanted.bank = 0; + wanted.prg = extra_prg - 1; + } +#ifdef DEBUG_MIDI + snd_printk(" *** allocating extra program\n"); +#endif + goto __extra_prg; + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +static void snd_opl3_kill_voice(opl3_t *opl3, int voice) +{ + unsigned short reg_side; + unsigned char voice_offset; + unsigned short opl3_reg; + + snd_opl3_voice_t *vp, *vp2; + + snd_assert(voice < MAX_OPL3_VOICES, return); + + vp = &opl3->voices[voice]; + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + } + + /* kill voice */ +#ifdef DEBUG_MIDI + snd_printk(" --> kill voice %i\n", voice); +#endif + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + /* clear Key ON bit */ + opl3->command(opl3, opl3_reg, vp->keyon_reg); + + /* do the bookkeeping */ + vp->time = opl3->use_time++; + + if (vp->state == SNDRV_OPL3_ST_ON_4OP) { + vp2 = &opl3->voices[voice + 3]; + + vp2->time = opl3->use_time++; + vp2->state = SNDRV_OPL3_ST_OFF; + } + vp->state = SNDRV_OPL3_ST_OFF; +#ifdef DEBUG_ALLOC + debug_alloc(opl3, "note off", voice); +#endif + +} + +/* + * Release a note in response to a midi note off. + */ +void snd_opl3_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + int voice; + snd_opl3_voice_t *vp; + + unsigned long flags; + + opl3 = snd_magic_cast(opl3_t, p, return); + +#ifdef DEBUG_MIDI + snd_printk("Note off, ch %i, inst %i, note %i\n", + chan->number, chan->midi_program, note); +#endif + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + if (chan->drum_channel && use_internal_drums) { + snd_opl3_drum_switch(opl3, note, vel, 0, chan); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + /* this loop will hopefully kill all extra voices, because + they are grouped by the same channel and note values */ + for (voice = 0; voice < opl3->max_voices; voice++) { + vp = &opl3->voices[voice]; + if (vp->state > 0 && vp->chan == chan && vp->note == note) { + snd_opl3_kill_voice(opl3, voice); + } + } + } else { + /* remap OSS voices */ + if (chan->number < MAX_OPL3_VOICES) { + voice = snd_opl3_oss_map[chan->number]; + snd_opl3_kill_voice(opl3, voice); + } + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +/* + * key pressure change + */ +void snd_opl3_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Key pressure, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +/* + * terminate note + */ +void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Terminate note, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +static void snd_opl3_update_pitch(opl3_t *opl3, int voice) +{ + unsigned short reg_side; + unsigned char voice_offset; + unsigned short opl3_reg; + + unsigned char fnum, blocknum; + + snd_opl3_voice_t *vp; + + snd_assert(voice < MAX_OPL3_VOICES, return); + + vp = &opl3->voices[voice]; + if (vp->chan == NULL) + return; /* not allocated? */ + + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + } + + snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan); + + /* Set OPL3 FNUM_LOW register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, fnum); + + vp->keyon_reg = blocknum; + + /* Set output sound flag */ + blocknum |= OPL3_KEYON_BIT; + + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, blocknum); + + vp->time = opl3->use_time++; +} + +/* + * Update voice pitch controller + */ +static void snd_opl3_pitch_ctrl(opl3_t *opl3, snd_midi_channel_t *chan) +{ + int voice; + snd_opl3_voice_t *vp; + + unsigned long flags; + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + for (voice = 0; voice < opl3->max_voices; voice++) { + vp = &opl3->voices[voice]; + if (vp->state > 0 && vp->chan == chan) { + snd_opl3_update_pitch(opl3, voice); + } + } + } else { + /* remap OSS voices */ + if (chan->number < MAX_OPL3_VOICES) { + voice = snd_opl3_oss_map[chan->number]; + snd_opl3_update_pitch(opl3, voice); + } + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +/* + * Deal with a controler type event. This includes all types of + * control events, not just the midi controllers + */ +void snd_opl3_control(void *p, int type, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n", + type, chan->number, chan->midi_program); +#endif + + switch (type) { + case MIDI_CTL_MSB_MODWHEEL: + if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63) + opl3->drum_reg |= OPL3_VIBRATO_DEPTH; + else + opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); + break; + case MIDI_CTL_E2_TREMOLO_DEPTH: + if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63) + opl3->drum_reg |= OPL3_TREMOLO_DEPTH; + else + opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); + break; + case MIDI_CTL_PITCHBEND: + snd_opl3_pitch_ctrl(opl3, chan); + break; + } +} + +/* + * NRPN events + */ +void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("NRPN, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +/* + * receive sysex + */ +void snd_opl3_sysex(void *p, unsigned char *buf, int len, + int parsed, snd_midi_channel_set_t *chset) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("SYSEX\n"); +#endif +} diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_oss.c linux/sound/drivers/opl3/opl3_oss.c --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_oss.c 2003-02-28 08:08:22.000000000 -0700 @@ -0,0 +1,357 @@ +/* + * Interface for OSS sequencer emulation + * + * Copyright (C) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "opl3_voice.h" +#include + +static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); +static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); +static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count); +static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg); + +/* */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* operators */ + +extern snd_midi_op_t opl3_ops; + +static snd_seq_oss_callback_t oss_callback = { + .owner = THIS_MODULE, + .open = snd_opl3_open_seq_oss, + .close = snd_opl3_close_seq_oss, + .ioctl = snd_opl3_ioctl_seq_oss, + .load_patch = snd_opl3_load_patch_seq_oss, + .reset = snd_opl3_reset_seq_oss, +}; + +static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct, + void *private_data, int atomic, int hop) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + + if (ev->type != SNDRV_SEQ_EVENT_OSS) + snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset); + return 0; +} + +/* ------------------------------ */ + +static void snd_opl3_oss_free_port(void *private_data) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + + snd_midi_channel_free_set(opl3->oss_chset); +} + +static int snd_opl3_oss_create_port(opl3_t * opl3) +{ + snd_seq_port_callback_t callbacks; + char name[32]; + int voices, opl_ver; + + voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + opl3->oss_chset = snd_midi_channel_alloc_set(voices); + if (opl3->oss_chset == NULL) + return -ENOMEM; + opl3->oss_chset->private_data = opl3; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.event_input = snd_opl3_oss_event_input; + callbacks.private_free = snd_opl3_oss_free_port; + callbacks.private_data = opl3; + + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(name, "OPL%i OSS Port", opl_ver); + + opl3->oss_chset->client = opl3->seq_client; + opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_SYNTH, + voices, voices, + name); + if (opl3->oss_chset->port < 0) { + snd_midi_channel_free_set(opl3->oss_chset); + return opl3->oss_chset->port; + } + return 0; +} + +/* ------------------------------ */ + +/* register OSS synth */ +void snd_opl3_init_seq_oss(opl3_t *opl3, char *name) +{ + snd_seq_oss_reg_t *arg; + snd_seq_device_t *dev; + + if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS, + sizeof(snd_seq_oss_reg_t), &dev) < 0) + return; + + opl3->oss_seq_dev = dev; + strncpy(dev->name, name, sizeof(dev->name) - 1); + dev->name[sizeof(dev->name) - 1] = 0; + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + arg->type = SYNTH_TYPE_FM; + if (opl3->hardware < OPL3_HW_OPL3) { + arg->subtype = FM_TYPE_ADLIB; + arg->nvoices = MAX_OPL2_VOICES; + } else { + arg->subtype = FM_TYPE_OPL3; + arg->nvoices = MAX_OPL3_VOICES; + } + arg->oper = oss_callback; + arg->private_data = opl3; + + snd_opl3_oss_create_port(opl3); + + /* register to OSS synth table */ + snd_device_register(opl3->card, dev); +} + +/* unregister */ +void snd_opl3_free_seq_oss(opl3_t *opl3) +{ + if (opl3->oss_seq_dev) { + snd_device_free(opl3->card, opl3->oss_seq_dev); + opl3->oss_seq_dev = NULL; + } +} + +/* ------------------------------ */ + +/* open OSS sequencer */ +static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, closure, return -EINVAL); + int err; + + snd_assert(arg != NULL, return -ENXIO); + + if ((err = snd_opl3_synth_setup(opl3)) < 0) + return err; + + /* fill the argument data */ + arg->private_data = opl3; + arg->addr.client = opl3->oss_chset->client; + arg->addr.port = opl3->oss_chset->port; + + if ((err = snd_opl3_synth_use_inc(opl3)) < 0) + return err; + + opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH; + return 0; +} + +/* close OSS sequencer */ +static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + snd_opl3_synth_cleanup(opl3); + + snd_opl3_synth_use_dec(opl3); + return 0; +} + +/* load patch */ + +/* offsets for SBI params */ +#define AM_VIB 0 +#define KSL_LEVEL 2 +#define ATTACK_DECAY 4 +#define SUSTAIN_RELEASE 6 +#define WAVE_SELECT 8 + +/* offset for SBI instrument */ +#define CONNECTION 10 +#define OFFSET_4OP 11 + +/* from sound_config.h */ +#define SBFM_MAXINSTR 256 + +static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, + const char *buf, int offs, int count) +{ + opl3_t *opl3; + int err = -EINVAL; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + if ((format == FM_PATCH) || (format == OPL3_PATCH)) { + struct sbi_instrument sbi; + + size_t size; + snd_seq_instr_header_t *put; + snd_seq_instr_data_t *data; + fm_xinstrument_t *xinstr; + + snd_seq_event_t ev; + int i; + + mm_segment_t fs; + + if (count < (int)sizeof(sbi)) { + snd_printk("FM Error: Patch record too short\n"); + return -EINVAL; + } + if (copy_from_user(&sbi, buf, sizeof(sbi))) + return -EFAULT; + + if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { + snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); + return -EINVAL; + } + + size = sizeof(*put) + sizeof(fm_xinstrument_t); + put = (snd_seq_instr_header_t *)snd_kcalloc(size, GFP_KERNEL); + if (put == NULL) + return -ENOMEM; + /* build header */ + data = &put->data; + data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; + strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); + /* build data section */ + xinstr = (fm_xinstrument_t *)(data + 1); + xinstr->stype = FM_STRU_INSTR; + + for (i = 0; i < 2; i++) { + xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; + xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; + xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; + xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; + xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; + } + xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; + + if (format == OPL3_PATCH) { + xinstr->type = FM_PATCH_OPL3; + for (i = 0; i < 2; i++) { + xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; + xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; + xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; + xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; + xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; + } + xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; + } else { + xinstr->type = FM_PATCH_OPL2; + } + + put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; + put->id.instr.bank = 127; + put->id.instr.prg = sbi.channel; + put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; + + memset (&ev, 0, sizeof(ev)); + ev.source.client = SNDRV_SEQ_CLIENT_OSS; + ev.dest = arg->addr; + + ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; + ev.queue = SNDRV_SEQ_QUEUE_DIRECT; + + fs = snd_enter_user(); + __again: + ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; + ev.data.ext.len = size; + ev.data.ext.ptr = put; + + err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, + opl3->seq_client, 0, 0); + if (err == -EBUSY) { + snd_seq_instr_header_t remove; + + memset (&remove, 0, sizeof(remove)); + remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; + remove.id.instr = put->id.instr; + + /* remove instrument */ + ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; + ev.data.ext.len = sizeof(remove); + ev.data.ext.ptr = &remove; + + snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, + opl3->seq_client, 0, 0); + goto __again; + } + snd_leave_user(fs); + + kfree(put); + } + return err; +} + +/* ioctl */ +static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, + unsigned long ioarg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + switch (cmd) { + case SNDCTL_FM_LOAD_INSTR: + snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + return -EINVAL; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + case SNDCTL_FM_4OP_ENABLE: + // handled automatically by OPL instrument type + return 0; + + default: + return -EINVAL; + } + return 0; +} + +/* reset device */ +static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_seq.c linux/sound/drivers/opl3/opl3_seq.c --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_seq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_seq.c 2003-02-28 08:08:23.000000000 -0700 @@ -0,0 +1,316 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM + * + * OPL2/3 FM instrument loader: + * alsa-tools/seq/sbiload/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "opl3_voice.h" +#include +#include + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth"); +MODULE_CLASSES("{sound}"); + +int use_internal_drums = 0; +MODULE_PARM(use_internal_drums, "i"); +MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums."); + +int snd_opl3_synth_use_inc(opl3_t * opl3) +{ + if (!try_module_get(opl3->card->module)) + return -EFAULT; + return 0; + +} + +void snd_opl3_synth_use_dec(opl3_t * opl3) +{ + module_put(opl3->card->module); +} + +int snd_opl3_synth_setup(opl3_t * opl3) +{ + int idx; + + down(&opl3->access_mutex); + if (opl3->used) { + up(&opl3->access_mutex); + return -EBUSY; + } + opl3->used++; + up(&opl3->access_mutex); + + snd_opl3_reset(opl3); + + for (idx = 0; idx < MAX_OPL3_VOICES; idx++) { + opl3->voices[idx].state = SNDRV_OPL3_ST_OFF; + opl3->voices[idx].time = 0; + opl3->voices[idx].keyon_reg = 0x00; + } + opl3->use_time = 0; + opl3->connection_reg = 0x00; + if (opl3->hardware >= OPL3_HW_OPL3) { + /* Enter OPL3 mode */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); + /* Clear 4-op connections */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, + opl3->connection_reg); + opl3->max_voices = MAX_OPL3_VOICES; + } + return 0; +} + +void snd_opl3_synth_cleanup(opl3_t * opl3) +{ + unsigned long flags; + + /* Stop system timer */ + spin_lock_irqsave(&opl3->sys_timer_lock, flags); + if (opl3->sys_timer_status) { + del_timer(&opl3->tlist); + opl3->sys_timer_status = 0; + } + spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); + + snd_opl3_reset(opl3); + down(&opl3->access_mutex); + opl3->used--; + up(&opl3->access_mutex); +} + +int snd_opl3_synth_use(void *private_data, snd_seq_port_subscribe_t * info) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + int err; + + if ((err = snd_opl3_synth_setup(opl3)) < 0) + return err; + + if (use_internal_drums) { + /* Percussion mode */ + opl3->voices[6].state = opl3->voices[7].state = + opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL; + snd_opl3_load_drums(opl3); + opl3->drum_reg = OPL3_PERCUSSION_ENABLE; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); + } else { + opl3->drum_reg = 0x00; + } + + if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) { + if ((err = snd_opl3_synth_use_inc(opl3)) < 0) + return err; + } + opl3->synth_mode = SNDRV_OPL3_MODE_SEQ; + return 0; +} + +int snd_opl3_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + + snd_opl3_synth_cleanup(opl3); + + if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) + snd_opl3_synth_use_dec(opl3); + return 0; +} + +/* + * MIDI emulation operators + */ +snd_midi_op_t opl3_ops = { + .note_on = snd_opl3_note_on, + .note_off = snd_opl3_note_off, + .key_press = snd_opl3_key_press, + .note_terminate = snd_opl3_terminate_note, + .control = snd_opl3_control, + .nrpn = snd_opl3_nrpn, + .sysex = snd_opl3_sysex, +}; + +static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct, + void *private_data, int atomic, int hop) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && + ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { + if (direct) { + snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, + opl3->seq_client, atomic, hop); + } + } else { + snd_midi_process_event(&opl3_ops, ev, opl3->chset); + } + return 0; +} + +/* ------------------------------ */ + +static void snd_opl3_synth_free_port(void *private_data) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + + snd_midi_channel_free_set(opl3->chset); +} + +static int snd_opl3_synth_create_port(opl3_t * opl3) +{ + snd_seq_port_callback_t callbacks; + char name[32]; + int voices, opl_ver; + + voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + opl3->chset = snd_midi_channel_alloc_set(16); + if (opl3->chset == NULL) + return -ENOMEM; + opl3->chset->private_data = opl3; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_opl3_synth_use; + callbacks.unuse = snd_opl3_synth_unuse; + callbacks.event_input = snd_opl3_synth_event_input; + callbacks.private_free = snd_opl3_synth_free_port; + callbacks.private_data = opl3; + + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(name, "OPL%i Port", opl_ver); + + opl3->chset->client = opl3->seq_client; + opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_SYNTH, + 16, voices, + name); + if (opl3->chset->port < 0) { + snd_midi_channel_free_set(opl3->chset); + return opl3->chset->port; + } + return 0; +} + +/* ------------------------------ */ + +static int snd_opl3_seq_new_device(snd_seq_device_t *dev) +{ + opl3_t *opl3; + int client; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + int opl_ver; + + opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (opl3 == NULL) + return -EINVAL; + + spin_lock_init(&opl3->voice_lock); + + opl3->seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = opl3; + callbacks.allow_output = callbacks.allow_input = 1; + client = opl3->seq_client = + snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(cinfo.name, "OPL%i FM synth", opl_ver); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + snd_opl3_synth_create_port(opl3); + + /* initialize instrument list */ + opl3->ilist = snd_seq_instr_list_new(); + if (opl3->ilist == NULL) { + snd_seq_delete_kernel_client(client); + opl3->seq_client = -1; + return -ENOMEM; + } + opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + snd_seq_fm_init(&opl3->fm_ops, NULL); + + /* setup system timer */ + init_timer(&opl3->tlist); + opl3->tlist.function = snd_opl3_timer_func; + opl3->tlist.data = (unsigned long) opl3; + spin_lock_init(&opl3->sys_timer_lock); + opl3->sys_timer_status = 0; + +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_opl3_init_seq_oss(opl3, cinfo.name); +#endif + return 0; +} + +static int snd_opl3_seq_delete_device(snd_seq_device_t *dev) +{ + opl3_t *opl3; + + opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (opl3 == NULL) + return -EINVAL; + +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_opl3_free_seq_oss(opl3); +#endif + if (opl3->seq_client >= 0) { + snd_seq_delete_kernel_client(opl3->seq_client); + opl3->seq_client = -1; + } + if (opl3->ilist) + snd_seq_instr_list_free(&opl3->ilist); + return 0; +} + +static int __init alsa_opl3_seq_init(void) +{ + static snd_seq_dev_ops_t ops = + { + snd_opl3_seq_new_device, + snd_opl3_seq_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, + sizeof(opl3_t*)); +} + +static void __exit alsa_opl3_seq_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); +} + +module_init(alsa_opl3_seq_init) +module_exit(alsa_opl3_seq_exit) diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_synth.c linux/sound/drivers/opl3/opl3_synth.c --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_synth.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,455 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Routines for OPL2/OPL3/OPL4 control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#define __SND_OSS_COMPAT__ +#include + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + + +/* + * Register offset table for OPL2/3 voices, + * OPL2 / one OPL3 register array side only + */ + +char snd_opl3_regmap[MAX_OPL2_VOICES][4] = +{ +/* OP1 OP2 OP3 OP4 */ +/* ------------------------ */ + { 0x00, 0x03, 0x08, 0x0b }, + { 0x01, 0x04, 0x09, 0x0c }, + { 0x02, 0x05, 0x0a, 0x0d }, + + { 0x08, 0x0b, 0x00, 0x00 }, + { 0x09, 0x0c, 0x00, 0x00 }, + { 0x0a, 0x0d, 0x00, 0x00 }, + + { 0x10, 0x13, 0x00, 0x00 }, /* used by percussive voices */ + { 0x11, 0x14, 0x00, 0x00 }, /* if the percussive mode */ + { 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */ +}; + +/* + * prototypes + */ +static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note); +static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice); +static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params); +static int snd_opl3_set_mode(opl3_t * opl3, int mode); +static int snd_opl3_set_connection(opl3_t * opl3, int connection); + +/* ------------------------------ */ + +/* + * open the device exclusively + */ +int snd_opl3_open(snd_hwdep_t * hw, struct file *file) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + down(&opl3->access_mutex); + if (opl3->used) { + up(&opl3->access_mutex); + return -EAGAIN; + } + opl3->used++; + up(&opl3->access_mutex); + + return 0; +} + +/* + * ioctl for hwdep device: + */ +int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + snd_assert(opl3 != NULL, return -EINVAL); + + switch (cmd) { + /* get information */ + case SNDRV_DM_FM_IOCTL_INFO: + { + snd_dm_fm_info_t info; + + info.fm_mode = opl3->fm_mode; + info.rhythm = opl3->rhythm; + if (copy_to_user((snd_dm_fm_info_t *) arg, &info, sizeof(snd_dm_fm_info_t))) + return -EFAULT; + return 0; + } + + case SNDRV_DM_FM_IOCTL_RESET: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_RESET: +#endif + snd_opl3_reset(opl3); + return 0; + + case SNDRV_DM_FM_IOCTL_PLAY_NOTE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE: +#endif + { + snd_dm_fm_note_t note; + if (copy_from_user(¬e, (snd_dm_fm_note_t *) arg, sizeof(snd_dm_fm_note_t))) + return -EFAULT; + return snd_opl3_play_note(opl3, ¬e); + } + + case SNDRV_DM_FM_IOCTL_SET_VOICE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE: +#endif + { + snd_dm_fm_voice_t voice; + if (copy_from_user(&voice, (snd_dm_fm_voice_t *) arg, sizeof(snd_dm_fm_voice_t))) + return -EFAULT; + return snd_opl3_set_voice(opl3, &voice); + } + + case SNDRV_DM_FM_IOCTL_SET_PARAMS: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS: +#endif + { + snd_dm_fm_params_t params; + if (copy_from_user(¶ms, (snd_dm_fm_params_t *) arg, sizeof(snd_dm_fm_params_t))) + return -EFAULT; + return snd_opl3_set_params(opl3, ¶ms); + } + + case SNDRV_DM_FM_IOCTL_SET_MODE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_MODE: +#endif + return snd_opl3_set_mode(opl3, (int) arg); + + case SNDRV_DM_FM_IOCTL_SET_CONNECTION: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_OPL: +#endif + return snd_opl3_set_connection(opl3, (int) arg); + +#ifdef CONFIG_SND_DEBUG + default: + snd_printk("unknown IOCTL: 0x%x\n", cmd); +#endif + } + return -ENOTTY; +} + +/* + * close the device + */ +int snd_opl3_release(snd_hwdep_t * hw, struct file *file) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + snd_opl3_reset(opl3); + down(&opl3->access_mutex); + opl3->used--; + up(&opl3->access_mutex); + + return 0; +} + +/* ------------------------------ */ + +void snd_opl3_reset(opl3_t * opl3) +{ + unsigned short opl3_reg; + + unsigned short reg_side; + unsigned char voice_offset; + + int max_voices, i; + + max_voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + + for (i = 0; i < max_voices; i++) { + /* Get register array side and offset of voice */ + if (i < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = i; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = i - MAX_OPL2_VOICES; + } + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]); + opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */ + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]); + opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */ + + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, 0x00); /* Note off */ + } + + if (opl3->hardware >= OPL3_HW_OPL3) + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + + opl3->max_voices = MAX_OPL2_VOICES; + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ + opl3->rhythm = 0; +} + + +static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note) +{ + unsigned short reg_side; + unsigned char voice_offset; + + unsigned short opl3_reg; + unsigned char reg_val; + + /* Voices 0 - 8 in OPL2 mode */ + /* Voices 0 - 17 in OPL3 mode */ + if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? + MAX_OPL3_VOICES : MAX_OPL2_VOICES)) + return -EINVAL; + + /* Get register array side and offset of voice */ + if (note->voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = note->voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = note->voice - MAX_OPL2_VOICES; + } + + /* Set lower 8 bits of note frequency */ + reg_val = (unsigned char) note->fnum; + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + reg_val = 0x00; + /* Set output sound flag */ + if (note->key_on) + reg_val |= OPL3_KEYON_BIT; + /* Set octave */ + reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK; + /* Set higher 2 bits of note frequency */ + reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK; + + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + return 0; +} + + +static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice) +{ + unsigned short reg_side; + unsigned char op_offset; + unsigned char voice_offset; + + unsigned short opl3_reg; + unsigned char reg_val; + + /* Only operators 1 and 2 */ + if (voice->op > 1) + return -EINVAL; + /* Voices 0 - 8 in OPL2 mode */ + /* Voices 0 - 17 in OPL3 mode */ + if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? + MAX_OPL3_VOICES : MAX_OPL2_VOICES)) + return -EINVAL; + + /* Get register array side and offset of voice */ + if (voice->voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice->voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice->voice - MAX_OPL2_VOICES; + } + /* Get register offset of operator */ + op_offset = snd_opl3_regmap[voice_offset][voice->op]; + + reg_val = 0x00; + /* Set amplitude modulation (tremolo) effect */ + if (voice->am) + reg_val |= OPL3_TREMOLO_ON; + /* Set vibrato effect */ + if (voice->vibrato) + reg_val |= OPL3_VIBRATO_ON; + /* Set sustaining sound phase */ + if (voice->do_sustain) + reg_val |= OPL3_SUSTAIN_ON; + /* Set keyboard scaling bit */ + if (voice->kbd_scale) + reg_val |= OPL3_KSR; + /* Set harmonic or frequency multiplier */ + reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK; + + /* Set OPL3 AM_VIB register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set decreasing volume of higher notes */ + reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK; + /* Set output volume */ + reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK; + + /* Set OPL3 KSL_LEVEL register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set attack phase level */ + reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK; + /* Set decay phase level */ + reg_val |= voice->decay & OPL3_DECAY_MASK; + + /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set sustain phase level */ + reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK; + /* Set release phase level */ + reg_val |= voice->release & OPL3_RELEASE_MASK; + + /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set inter-operator feedback */ + reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK; + /* Set inter-operator connection */ + if (voice->connection) + reg_val |= OPL3_CONNECTION_BIT; + /* OPL-3 only */ + if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) { + if (voice->left) + reg_val |= OPL3_VOICE_TO_LEFT; + if (voice->right) + reg_val |= OPL3_VOICE_TO_RIGHT; + } + /* Feedback/connection bits are applicable to voice */ + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Select waveform */ + reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK; + opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + return 0; +} + +static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params) +{ + unsigned char reg_val; + + reg_val = 0x00; + /* Set keyboard split method */ + if (params->kbd_split) + reg_val |= OPL3_KEYBOARD_SPLIT; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val); + + reg_val = 0x00; + /* Set amplitude modulation (tremolo) depth */ + if (params->am_depth) + reg_val |= OPL3_TREMOLO_DEPTH; + /* Set vibrato depth */ + if (params->vib_depth) + reg_val |= OPL3_VIBRATO_DEPTH; + /* Set percussion mode */ + if (params->rhythm) { + reg_val |= OPL3_PERCUSSION_ENABLE; + opl3->rhythm = 1; + } else { + opl3->rhythm = 0; + } + /* Play percussion instruments */ + if (params->bass) + reg_val |= OPL3_BASSDRUM_ON; + if (params->snare) + reg_val |= OPL3_SNAREDRUM_ON; + if (params->tomtom) + reg_val |= OPL3_TOMTOM_ON; + if (params->cymbal) + reg_val |= OPL3_CYMBAL_ON; + if (params->hihat) + reg_val |= OPL3_HIHAT_ON; + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val); + return 0; +} + +static int snd_opl3_set_mode(opl3_t * opl3, int mode) +{ + if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3)) + return -EINVAL; + + if (mode == SNDRV_DM_FM_MODE_OPL3) { + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); /* Enter OPL3 mode */ + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL3; + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00); /* Clear 4-op connections */ + } else { + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; + } + + return 0; +} + +static int snd_opl3_set_connection(opl3_t * opl3, int connection) +{ + unsigned char reg_val; + + /* OPL-3 only */ + if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3) + return -EINVAL; + + reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 | + OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2); + /* Set 4-op connections */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val); + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_voice.h linux/sound/drivers/opl3/opl3_voice.h --- linux-2.4.21-rc1.orig/sound/drivers/opl3/opl3_voice.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/opl3/opl3_voice.h 2002-05-23 02:20:38.000000000 -0600 @@ -0,0 +1,52 @@ +#ifndef __OPL3_VOICE_H +#define __OPL3_VOICE_H + +/* + * Copyright (c) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* Prototypes for opl3_seq.c */ +int snd_opl3_synth_use_inc(opl3_t * opl3); +void snd_opl3_synth_use_dec(opl3_t * opl3); +int snd_opl3_synth_setup(opl3_t * opl3); +void snd_opl3_synth_cleanup(opl3_t * opl3); + +/* Prototypes for opl3_midi.c */ +void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan); +void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan); +void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); + +void snd_opl3_calc_volume(unsigned char *reg, int vel, snd_midi_channel_t *chan); +void snd_opl3_timer_func(unsigned long data); + +/* Prototypes for opl3_drums.c */ +void snd_opl3_load_drums(opl3_t *opl3); +void snd_opl3_drum_switch(opl3_t *opl3, int note, int on_off, int vel, snd_midi_channel_t *chan); + +/* Prototypes for opl3_oss.c */ +#ifdef CONFIG_SND_SEQUENCER_OSS +void snd_opl3_init_seq_oss(opl3_t *opl3, char *name); +void snd_opl3_free_seq_oss(opl3_t *opl3); +#endif + +#endif diff -urN linux-2.4.21-rc1.orig/sound/drivers/serial-u16550.c linux/sound/drivers/serial-u16550.c --- linux-2.4.21-rc1.orig/sound/drivers/serial-u16550.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/serial-u16550.c 2003-02-25 05:35:44.000000000 -0700 @@ -0,0 +1,990 @@ +/* + * serial.c + * Copyright (c) by Jaroslav Kysela , + * Isaku Yamahata , + * George Hansper , + * Hannu Savolainen + * + * This code is based on the code from ALSA 0.5.9, but heavily rewritten. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com + * Added support for the Midiator MS-124T and for the MS-124W in + * Single Addressed (S/A) or Multiple Burst (M/B) mode, with + * power derived either parasitically from the serial port or + * from a separate power supply. + * + * More documentation can be found in serial-u16550.txt. + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#include + +MODULE_DESCRIPTION("MIDI serial u16550"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA, MIDI serial u16550}}"); + +#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */ +#define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */ +#define SNDRV_SERIAL_MS124W_SA 2 /* Midiator MS-124W in S/A mode */ +#define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */ +#define SNDRV_SERIAL_GENERIC 4 /* Generic Interface */ +#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_GENERIC +static char *adaptor_names[] = { + "Soundcanvas", + "MS-124T", + "MS-124W S/A", + "MS-124W M/B", + "Generic" +}; + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */ +static int speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */ +static int base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */ +static int outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */ +static int ins[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */ +static int adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Serial MIDI."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Serial MIDI."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(enable, "Enable UART16550A chip."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for UART16550A chip."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for UART16550A chip."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(speed, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(speed, "Speed in bauds."); +MODULE_PARM_SYNTAX(speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list"); +MODULE_PARM(base, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(base, "Base for divisor in bauds."); +MODULE_PARM_SYNTAX(base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list"); +MODULE_PARM(outs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(outs, "Number of MIDI outputs."); +MODULE_PARM(ins, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(ins, "Number of MIDI inputs."); + +MODULE_PARM_SYNTAX(outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list"); +MODULE_PARM_SYNTAX(ins, SNDRV_ENABLED ",allows:{{1,16}},dialog:list"); +MODULE_PARM(adaptor, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(adaptor, "Type of adaptor."); +MODULE_PARM_SYNTAX(adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B,4=Generic}},dialog:list"); + +/*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/ /* Address outs as 0-3 instead of bitmap */ + +#define SNDRV_SERIAL_MAX_OUTS 16 /* max 64, min 16 */ +#define SNDRV_SERIAL_MAX_INS 16 /* max 64, min 16 */ + +#define TX_BUFF_SIZE (1<<15) /* Must be 2^n */ +#define TX_BUFF_MASK (TX_BUFF_SIZE - 1) + +#define SERIAL_MODE_NOT_OPENED (0) +#define SERIAL_MODE_INPUT_OPEN (1 << 0) +#define SERIAL_MODE_OUTPUT_OPEN (1 << 1) +#define SERIAL_MODE_INPUT_TRIGGERED (1 << 2) +#define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3) + +typedef struct _snd_uart16550 { + snd_card_t *card; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS]; + snd_rawmidi_substream_t *midi_input[SNDRV_SERIAL_MAX_INS]; + + int filemode; //open status of file + + spinlock_t open_lock; + + int irq; + + unsigned long base; + struct resource *res_base; + + unsigned int speed; + unsigned int speed_base; + unsigned char divisor; + + unsigned char old_divisor_lsb; + unsigned char old_divisor_msb; + unsigned char old_line_ctrl_reg; + + // parameter for using of write loop + short int fifo_limit; //used in uart16550 + short int fifo_count; //used in uart16550 + + // type of adaptor + int adaptor; + + // inputs + int prev_in; + unsigned char rstatus; + + // outputs + int prev_out; + unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS]; + + // write buffer and its writing/reading position + unsigned char tx_buff[TX_BUFF_SIZE]; + int buff_in_count; + int buff_in; + int buff_out; + + // wait timer + unsigned int timer_running:1; + struct timer_list buffer_timer; + +} snd_uart16550_t; + +static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +inline static void snd_uart16550_add_timer(snd_uart16550_t *uart) +{ + if (! uart->timer_running) { + /* timer 38600bps * 10bit * 16byte */ + uart->buffer_timer.expires = jiffies + (HZ+255)/256; + uart->timer_running = 1; + add_timer(&uart->buffer_timer); + } +} + +inline static void snd_uart16550_del_timer(snd_uart16550_t *uart) +{ + if (uart->timer_running) { + del_timer(&uart->buffer_timer); + uart->timer_running = 0; + } +} + +/* This macro is only used in snd_uart16550_io_loop */ +inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart) +{ + unsigned short buff_out = uart->buff_out; + outb(uart->tx_buff[buff_out], uart->base + UART_TX); + uart->fifo_count++; + buff_out++; + buff_out &= TX_BUFF_MASK; + uart->buff_out = buff_out; + uart->buff_in_count--; +} + +/* This loop should be called with interrupts disabled + * We don't want to interrupt this, + * as we're already handling an interrupt + */ +static void snd_uart16550_io_loop(snd_uart16550_t * uart) +{ + unsigned char c, status; + int substream; + + /* recall previous stream */ + substream = uart->prev_in; + + /* Read Loop */ + while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) { + /* while receive data ready */ + c = inb(uart->base + UART_RX); + + /* keep track of last status byte */ + if (c & 0x80) { + uart->rstatus = c; + } + + /* handle stream switch */ + if (uart->adaptor == SNDRV_SERIAL_GENERIC) { + if (uart->rstatus == 0xf5) { + if (c <= SNDRV_SERIAL_MAX_INS && c > 0) + substream = c - 1; + if (c != 0xf5) + uart->rstatus = 0; /* prevent future bytes from being interpreted as streams */ + } + else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) { + snd_rawmidi_receive(uart->midi_input[substream], &c, 1); + } + } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) { + snd_rawmidi_receive(uart->midi_input[substream], &c, 1); + } + + if (status & UART_LSR_OE) + snd_printk("%s: Overrun on device at 0x%lx\n", + uart->rmidi->name, uart->base); + } + + /* remember the last stream */ + uart->prev_in = substream; + + /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not, + buffer is never filled. */ + /* Check write status */ + if (status & UART_LSR_THRE) { + uart->fifo_count = 0; + } + if (uart->adaptor == SNDRV_SERIAL_MS124W_SA + || uart->adaptor == SNDRV_SERIAL_GENERIC) { + /* Can't use FIFO, must send only when CTS is true */ + status = inb(uart->base + UART_MSR); + if (uart->fifo_count == 0 && (status & UART_MSR_CTS) + && uart->buff_in_count > 0) + snd_uart16550_buffer_output(uart); + } else { + /* Write loop */ + while (uart->fifo_count < uart->fifo_limit /* Can we write ? */ + && uart->buff_in_count > 0) /* Do we want to? */ + snd_uart16550_buffer_output(uart); + } + if (uart->irq < 0 && uart->buff_in_count > 0) + snd_uart16550_add_timer(uart); +} + +/* NOTES ON SERVICING INTERUPTS + * --------------------------- + * After receiving a interrupt, it is important to indicate to the UART that + * this has been done. + * For a Rx interrupt, this is done by reading the received byte. + * For a Tx interrupt this is done by either: + * a) Writing a byte + * b) Reading the IIR + * It is particularly important to read the IIR if a Tx interrupt is received + * when there is no data in tx_buff[], as in this case there no other + * indication that the interrupt has been serviced, and it remains outstanding + * indefinitely. This has the curious side effect that and no further interrupts + * will be generated from this device AT ALL!!. + * It is also desirable to clear outstanding interrupts when the device is + * opened/closed. + * + * + * Note that some devices need OUT2 to be set before they will generate + * interrupts at all. (Possibly tied to an internal pull-up on CTS?) + */ +static void snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_uart16550_t *uart; + + uart = (snd_uart16550_t *) dev_id; + spin_lock(&uart->open_lock); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) { + spin_unlock(&uart->open_lock); + return; + } + inb(uart->base + UART_IIR); /* indicate to the UART that the interrupt has been serviced */ + snd_uart16550_io_loop(uart); + spin_unlock(&uart->open_lock); +} + +/* When the polling mode, this function calls snd_uart16550_io_loop. */ +static void snd_uart16550_buffer_timer(unsigned long data) +{ + snd_uart16550_t *uart; + + uart = (snd_uart16550_t *)data; + spin_lock(&uart->open_lock); + snd_uart16550_del_timer(uart); + snd_uart16550_io_loop(uart); + spin_unlock(&uart->open_lock); +} + +/* + * this method probes, if an uart sits on given port + * return 0 if found + * return negative error if not found + */ +static int __init snd_uart16550_detect(snd_uart16550_t *uart) +{ + unsigned long io_base = uart->base; + int ok; + unsigned char c; + + /* Do some vague tests for the presence of the uart */ + if (io_base == 0) { + return -ENODEV; /* Not configured */ + } + + uart->res_base = request_region(io_base, 8, "Serial MIDI"); + if (uart->res_base == NULL) + return -EBUSY; + + ok = 1; /* uart detected unless one of the following tests should fail */ + /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */ + outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */ + c = inb(io_base + UART_IER); + /* The top four bits of the IER should always == 0 */ + if ((c & 0xf0) != 0) + ok = 0; /* failed */ + + outb(0xaa, io_base + UART_SCR); + /* Write arbitrary data into the scratch reg */ + c = inb(io_base + UART_SCR); + /* If it comes back, it's OK */ + if (c != 0xaa) + ok = 0; /* failed */ + + outb(0x55, io_base + UART_SCR); + /* Write arbitrary data into the scratch reg */ + c = inb(io_base + UART_SCR); + /* If it comes back, it's OK */ + if (c != 0x55) + ok = 0; /* failed */ + + return ok; +} + +static void snd_uart16550_do_open(snd_uart16550_t * uart) +{ + char byte; + + /* Initialize basic variables */ + uart->buff_in_count = 0; + uart->buff_in = 0; + uart->buff_out = 0; + uart->fifo_limit = 1; + uart->fifo_count = 0; + uart->timer_running = 0; + + outb(UART_FCR_ENABLE_FIFO /* Enable FIFO's (if available) */ + | UART_FCR_CLEAR_RCVR /* Clear receiver FIFO */ + | UART_FCR_CLEAR_XMIT /* Clear transmitter FIFO */ + | UART_FCR_TRIGGER_4 /* Set FIFO trigger at 4-bytes */ + /* NOTE: interrupt generated after T=(time)4-bytes + * if less than UART_FCR_TRIGGER bytes received + */ + ,uart->base + UART_FCR); /* FIFO Control Register */ + + if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0) + uart->fifo_limit = 16; + if (uart->divisor != 0) { + uart->old_line_ctrl_reg = inb(uart->base + UART_LCR); + outb(UART_LCR_DLAB /* Divisor latch access bit */ + ,uart->base + UART_LCR); /* Line Control Register */ + uart->old_divisor_lsb = inb(uart->base + UART_DLL); + uart->old_divisor_msb = inb(uart->base + UART_DLM); + + outb(uart->divisor + ,uart->base + UART_DLL); /* Divisor Latch Low */ + outb(0 + ,uart->base + UART_DLM); /* Divisor Latch High */ + /* DLAB is reset to 0 in next outb() */ + } + /* Set serial parameters (parity off, etc) */ + outb(UART_LCR_WLEN8 /* 8 data-bits */ + | 0 /* 1 stop-bit */ + | 0 /* parity off */ + | 0 /* DLAB = 0 */ + ,uart->base + UART_LCR); /* Line Control Register */ + + switch (uart->adaptor) { + default: + outb(UART_MCR_RTS /* Set Request-To-Send line active */ + | UART_MCR_DTR /* Set Data-Terminal-Ready line active */ + | UART_MCR_OUT2 /* Set OUT2 - not always required, but when + * it is, it is ESSENTIAL for enabling interrupts + */ + ,uart->base + UART_MCR); /* Modem Control Register */ + break; + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2, + uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are both asserted. */ + outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2, + uart->base + UART_MCR); + break; + } + + if (uart->irq < 0) { + byte = (0 & UART_IER_RDI) /* Disable Receiver data interrupt */ + |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interrupt */ + ; + } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) { + byte = UART_IER_RDI /* Enable Receiver data interrupt */ + | UART_IER_MSI /* Enable Modem status interrupt */ + ; + } else if (uart->adaptor == SNDRV_SERIAL_GENERIC) { + byte = UART_IER_RDI /* Enable Receiver data interrupt */ + | UART_IER_MSI /* Enable Modem status interrupt */ + | UART_IER_THRI /* Enable Transmitter holding register empty interrupt */ + ; + } else { + byte = UART_IER_RDI /* Enable Receiver data interrupt */ + | UART_IER_THRI /* Enable Transmitter holding register empty interrupt */ + ; + } + outb(byte, uart->base + UART_IER); /* Interupt enable Register */ + + inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */ + inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */ + inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */ +} + +static void snd_uart16550_do_close(snd_uart16550_t * uart) +{ + if (uart->irq < 0) + snd_uart16550_del_timer(uart); + + /* NOTE: may need to disable interrupts before de-registering out handler. + * For now, the consequences are harmless. + */ + + outb((0 & UART_IER_RDI) /* Disable Receiver data interrupt */ + |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interrupt */ + ,uart->base + UART_IER); /* Interupt enable Register */ + + switch (uart->adaptor) { + default: + outb((0 & UART_MCR_RTS) /* Deactivate Request-To-Send line */ + |(0 & UART_MCR_DTR) /* Deactivate Data-Terminal-Ready line */ + |(0 & UART_MCR_OUT2) /* Deactivate OUT2 */ + ,uart->base + UART_MCR); /* Modem Control Register */ + break; + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states; leave it powered. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2), + uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are both asserted; leave it powered. */ + outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2), + uart->base + UART_MCR); + break; + } + + inb(uart->base + UART_IIR); /* Clear any outstanding interrupts */ + + /* Restore old divisor */ + if (uart->divisor != 0) { + outb(UART_LCR_DLAB /* Divisor latch access bit */ + ,uart->base + UART_LCR); /* Line Control Register */ + outb(uart->old_divisor_lsb + ,uart->base + UART_DLL); /* Divisor Latch Low */ + outb(uart->old_divisor_msb + ,uart->base + UART_DLM); /* Divisor Latch High */ + /* Restore old LCR (data bits, stop bits, parity, DLAB) */ + outb(uart->old_line_ctrl_reg + ,uart->base + UART_LCR); /* Line Control Register */ + } +} + +static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_open(uart); + uart->filemode |= SERIAL_MODE_INPUT_OPEN; + uart->midi_input[substream->number] = substream; + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +} + +static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + uart->filemode &= ~SERIAL_MODE_INPUT_OPEN; + uart->midi_input[substream->number] = NULL; + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_close(uart); + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +} + +static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&uart->open_lock, flags); + if (up) { + uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED; + } else { + uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED; + } + spin_unlock_irqrestore(&uart->open_lock, flags); +} + +static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_open(uart); + uart->filemode |= SERIAL_MODE_OUTPUT_OPEN; + uart->midi_output[substream->number] = substream; + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +}; + +static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN; + uart->midi_output[substream->number] = NULL; + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_close(uart); + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +}; + +inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) +{ + unsigned short buff_in = uart->buff_in; + uart->tx_buff[buff_in] = byte; + buff_in++; + buff_in &= TX_BUFF_MASK; + uart->buff_in = buff_in; + uart->buff_in_count++; + if (uart->irq < 0) /* polling mode */ + snd_uart16550_add_timer(uart); +} + +static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte) +{ + if (uart->buff_in_count == 0 /* Buffer empty? */ + && ((uart->adaptor != SNDRV_SERIAL_MS124W_SA && + uart->adaptor != SNDRV_SERIAL_GENERIC) || + (uart->fifo_count == 0 /* FIFO empty? */ + && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */ + + /* Tx Buffer Empty - try to write immediately */ + if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) { + /* Transmitter holding register (and Tx FIFO) empty */ + uart->fifo_count = 1; + outb(midi_byte, uart->base + UART_TX); + } else { + if (uart->fifo_count < uart->fifo_limit) { + uart->fifo_count++; + outb(midi_byte, uart->base + UART_TX); + } else { + /* Cannot write (buffer empty) - put char in buffer */ + snd_uart16550_write_buffer(uart, midi_byte); + } + } + } else { + if (uart->buff_in_count >= TX_BUFF_SIZE) { + snd_printk("%s: Buffer overrun on device at 0x%lx\n", + uart->rmidi->name, uart->base); + return; + } + snd_uart16550_write_buffer(uart, midi_byte); + } +} + +static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + unsigned char midi_byte, addr_byte; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + char first; + static unsigned long lasttime=0; + + /* Interupts are disabled during the updating of the tx_buff, + * since it is 'bad' to have two processes updating the same + * variables (ie buff_in & buff_out) + */ + + spin_lock_irqsave(&uart->open_lock, flags); + + if (uart->irq < 0) //polling + snd_uart16550_io_loop(uart); + + if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) { + while (1) { + /* buffer full? */ + /* in this mode we need two bytes of space */ + if (uart->buff_in_count > TX_BUFF_SIZE - 2) + break; + if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) + break; +#if SNDRV_SERIAL_MS124W_MB_NOCOMBO + /* select exactly one of the four ports */ + addr_byte = (1 << (substream->number + 4)) | 0x08; +#else + /* select any combination of the four ports */ + addr_byte = (substream->number << 4) | 0x08; + /* ...except none */ + if (addr_byte == 0x08) addr_byte = 0xf8; +#endif + snd_uart16550_output_byte(uart, substream, addr_byte); + /* send midi byte */ + snd_uart16550_output_byte(uart, substream, midi_byte); + } + } else { + first = 0; + while (1) { + if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) + break; + /* Also send F5 after 3 seconds with no data to handle device disconnect */ + if (first == 0 && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS || + uart->adaptor == SNDRV_SERIAL_GENERIC) && + (uart->prev_out != substream->number || jiffies-lasttime > 3*HZ)) { + + /* We will need three bytes of data here (worst case). */ + if (uart->buff_in_count >= TX_BUFF_SIZE - 3) + break; + + /* Roland Soundcanvas part selection */ + /* If this substream of the data is different previous + substream in this uart, send the change part event */ + uart->prev_out = substream->number; + /* change part */ + snd_uart16550_output_byte(uart, substream, 0xf5); + /* data */ + snd_uart16550_output_byte(uart, substream, uart->prev_out + 1); + /* If midi_byte is a data byte, send the previous status byte */ + if ((midi_byte < 0x80) && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS)) + snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]); + } + + /* buffer full? */ + if (uart->buff_in_count >= TX_BUFF_SIZE) + break; + + /* send midi byte */ + snd_uart16550_output_byte(uart, substream, midi_byte); + if (midi_byte >= 0x80 && midi_byte < 0xf0) + uart->prev_status[uart->prev_out] = midi_byte; + first = 1; + } + lasttime = jiffies; + } + spin_unlock_irqrestore(&uart->open_lock, flags); +} + +static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&uart->open_lock, flags); + if (up) { + uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED; + } else { + uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED; + } + spin_unlock_irqrestore(&uart->open_lock, flags); + if (up) + snd_uart16550_output_write(substream); +} + +static snd_rawmidi_ops_t snd_uart16550_output = +{ + .open = snd_uart16550_output_open, + .close = snd_uart16550_output_close, + .trigger = snd_uart16550_output_trigger, +}; + +static snd_rawmidi_ops_t snd_uart16550_input = +{ + .open = snd_uart16550_input_open, + .close = snd_uart16550_input_close, + .trigger = snd_uart16550_input_trigger, +}; + +static int snd_uart16550_free(snd_uart16550_t *uart) +{ + if (uart->irq >= 0) + free_irq(uart->irq, (void *)uart); + if (uart->res_base) { + release_resource(uart->res_base); + kfree_nocheck(uart->res_base); + } + snd_magic_kfree(uart); + return 0; +}; + +static int snd_uart16550_dev_free(snd_device_t *device) +{ + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, device->device_data, return -ENXIO); + return snd_uart16550_free(uart); +} + +static int __init snd_uart16550_create(snd_card_t * card, + unsigned long iobase, + int irq, + unsigned int speed, + unsigned int base, + int adaptor, + snd_uart16550_t **ruart) +{ + static snd_device_ops_t ops = { + .dev_free = snd_uart16550_dev_free, + }; + snd_uart16550_t *uart; + int err; + + + if ((uart = snd_magic_kcalloc(snd_uart16550_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + uart->adaptor = adaptor; + uart->card = card; + spin_lock_init(&uart->open_lock); + uart->irq = -1; + uart->base = iobase; + + if ((err = snd_uart16550_detect(uart)) <= 0) { + printk(KERN_ERR "no UART detected at 0x%lx\n", iobase); + return err; + } + + if (irq >= 0) { + if (request_irq(irq, snd_uart16550_interrupt, + SA_INTERRUPT, "Serial MIDI", (void *) uart)) { + uart->irq = -1; + snd_printk("irq %d busy. Using Polling.\n", irq); + } else { + uart->irq = irq; + } + } + uart->divisor = base / speed; + uart->speed = base / (unsigned int)uart->divisor; + uart->speed_base = base; + uart->prev_out = -1; + uart->prev_in = 0; + uart->rstatus = 0; + memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS); + init_timer(&uart->buffer_timer); + uart->buffer_timer.function = snd_uart16550_buffer_timer; + uart->buffer_timer.data = (unsigned long)uart; + uart->timer_running = 0; + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) { + snd_uart16550_free(uart); + return err; + } + + switch (uart->adaptor) { + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are asserted. */ + outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR); + break; + default: + break; + } + + if (ruart) + *ruart = uart; + + return 0; +} + +static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, int ins, snd_rawmidi_t **rmidi) +{ + snd_rawmidi_t *rrawmidi; + int err; + + if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, ins, &rrawmidi)) < 0) + return err; + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input); + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); + sprintf(rrawmidi->name, "uart16550 MIDI #%d", device); + rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rrawmidi->private_data = uart; + if (rmidi) + *rmidi = rrawmidi; + return 0; +} + +static int __init snd_serial_probe(int dev) +{ + snd_card_t *card; + snd_uart16550_t *uart; + int err; + + if (!enable[dev]) + return -ENOENT; + + switch (adaptor[dev]) { + case SNDRV_SERIAL_SOUNDCANVAS: + ins[dev] = 1; + break; + case SNDRV_SERIAL_MS124T: + case SNDRV_SERIAL_MS124W_SA: + outs[dev] = 1; + ins[dev] = 1; + break; + case SNDRV_SERIAL_MS124W_MB: + outs[dev] = 16; + ins[dev] = 1; + break; + case SNDRV_SERIAL_GENERIC: + break; + default: + snd_printk("Adaptor type is out of range 0-%d (%d)\n", + SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]); + return -ENODEV; + } + + if (outs[dev] < 1 || outs[dev] > SNDRV_SERIAL_MAX_OUTS) { + snd_printk("Count of outputs is out of range 1-%d (%d)\n", + SNDRV_SERIAL_MAX_OUTS, outs[dev]); + return -ENODEV; + } + + if (ins[dev] < 1 || ins[dev] > SNDRV_SERIAL_MAX_INS) { + snd_printk("Count of inputs is out of range 1-%d (%d)\n", + SNDRV_SERIAL_MAX_INS, ins[dev]); + return -ENODEV; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "Serial"); + strcpy(card->shortname, "Serial midi (uart16550A)"); + + if ((err = snd_uart16550_create(card, + port[dev], + irq[dev], + speed[dev], + base[dev], + adaptor[dev], + &uart)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d ins %d adaptor %s", + card->shortname, + uart->base, + uart->irq, + uart->speed, + (int)uart->divisor, + outs[dev], + ins[dev], + adaptor_names[uart->adaptor]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_serial_cards[dev] = card; + return 0; +} + +static int __init alsa_card_serial_init(void) +{ + int dev = 0; + int cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (snd_serial_probe(dev) == 0) + cards++; + } + + if (cards == 0) { +#ifdef MODULE + printk(KERN_ERR "serial midi soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_serial_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (snd_serial_cards[dev] != NULL) + snd_card_free(snd_serial_cards[dev]); + } +} + +module_init(alsa_card_serial_init) +module_exit(alsa_card_serial_exit) + +#ifndef MODULE + +/* format is: snd-serial=enable,index,id, + port,irq,speed,base,outs, + ins,adaptor */ + +static int __init alsa_card_serial_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&speed[nr_dev]) == 2 && + get_option(&str,&base[nr_dev]) == 2 && + get_option(&str,&outs[nr_dev]) == 2 && + get_option(&str,&ins[nr_dev]) == 2 && + get_option(&str,&adaptor[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-serial=", alsa_card_serial_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/drivers/virmidi.c linux/sound/drivers/virmidi.c --- linux-2.4.21-rc1.orig/sound/drivers/virmidi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/drivers/virmidi.c 2002-11-15 07:38:47.000000000 -0700 @@ -0,0 +1,186 @@ +/* + * Dummy soundcard for virtual rawmidi devices + * + * Copyright (c) 2000 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * VIRTUAL RAW MIDI DEVICE CARDS + * + * This dummy card contains up to 4 virtual rawmidi devices. + * They are not real rawmidi devices but just associated with sequencer + * clients, so that any input/output sources can be connected as a raw + * MIDI device arbitrary. + * Also, multiple access is allowed to a single rawmidi device. + * + * Typical usage is like following: + * - Load snd-virmidi module. + * # modprobe snd-virmidi index=2 + * Then, sequencer clients 72:0 to 75:0 will be created, which are + * mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively. + * + * - Connect input/output via aconnect. + * % aconnect 64:0 72:0 # keyboard input redirection 64:0 -> 72:0 + * % aconnect 72:0 65:0 # output device redirection 72:0 -> 65:0 + * + * - Run application using a midi device (eg. /dev/snd/midiC1D0) + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +/* hack: OSS defines midi_devs, so undefine it (versioned symbols) */ +#undef midi_devs + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA,Virtual rawmidi device}}"); + +#define MAX_MIDI_DEVICES 8 + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for virmidi soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for virmidi soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(midi_devs, "MIDI devices # (1-8)"); +MODULE_PARM_SYNTAX(midi_devs, SNDRV_ENABLED ",allows:{{1,8}}"); + +typedef struct snd_card_virmidi { + snd_card_t *card; + snd_rawmidi_t *midi[MAX_MIDI_DEVICES]; +} snd_card_virmidi_t; + +static snd_card_t *snd_virmidi_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_virmidi_probe(int dev) +{ + snd_card_t *card; + struct snd_card_virmidi *vmidi; + int idx, err; + + if (!enable[dev]) + return -ENODEV; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_virmidi)); + if (card == NULL) + return -ENOMEM; + vmidi = (struct snd_card_virmidi *)card->private_data; + vmidi->card = card; + + if (midi_devs[dev] > MAX_MIDI_DEVICES) { + snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES); + midi_devs[dev] = MAX_MIDI_DEVICES; + } + for (idx = 0; idx < midi_devs[dev]; idx++) { + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) + goto __nodev; + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + vmidi->midi[idx] = rmidi; + strcpy(rmidi->name, "Virtual Raw MIDI"); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; + } + + strcpy(card->driver, "VirMIDI"); + strcpy(card->shortname, "VirMIDI"); + sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); + if ((err = snd_card_register(card)) == 0) { + snd_virmidi_cards[dev] = card; + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __init alsa_card_virmidi_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (snd_card_virmidi_probe(dev) < 0) { +#ifdef MODULE + printk(KERN_ERR "Card-VirMIDI #%i not found or device busy\n", dev + 1); +#endif + break; + } + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_virmidi_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_virmidi_cards[dev]); +} + +module_init(alsa_card_virmidi_init) +module_exit(alsa_card_virmidi_exit) + +#ifndef MODULE + +/* format is: snd-virmidi=enable,index,id,midi_devs */ + +static int __init alsa_card_virmidi_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&midi_devs[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-virmidi=", alsa_card_virmidi_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/i2c/Makefile linux/sound/i2c/Makefile --- linux-2.4.21-rc1.orig/sound/i2c/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/i2c/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,37 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _i2c.o + +list-multi := snd-i2c.o snd-cs8427.o snd-tea6330t.o + +export-objs := i2c.o cs8427.o tea6330t.o + +snd-i2c-objs := i2c.o +snd-cs8427-objs := cs8427.o +snd-tea6330t-objs := tea6330t.o + +ifeq ($(subst m,y,$(CONFIG_L3)),y) + subdir-$(CONFIG_L3) += l3 +endif +ifeq ($(filter $(subdir-y),l3),l3) + subdir-m := l3 + obj-y += l3/l3.o +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o +obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o + +include $(TOPDIR)/Rules.make + +snd-i2c.o: $(snd-i2c-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-i2c-objs) + +snd-cs8427.o: $(snd-cs8427-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs8427-objs) + +snd-tea6330t.o: $(snd-tea6330t-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-tea6330t-objs) diff -urN linux-2.4.21-rc1.orig/sound/i2c/cs8427.c linux/sound/i2c/cs8427.c --- linux-2.4.21-rc1.orig/sound/i2c/cs8427.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/i2c/cs8427.c 2003-01-31 08:19:39.000000000 -0700 @@ -0,0 +1,473 @@ +/* + * Routines for control of the CS8427 via i2c bus + * IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic"); +MODULE_LICENSE("GPL"); + +#define chip_t snd_i2c_device_t + +#define CS8427_ADDR (0x20>>1) /* fixed address */ + +typedef struct { + snd_pcm_substream_t *substream; + char hw_status[24]; /* hardware status */ + char def_status[24]; /* default status */ + char pcm_status[24]; /* PCM private status */ + char hw_udata[32]; + snd_kcontrol_t *pcm_ctl; +} cs8427_stream_t; + +typedef struct { + unsigned char regmap[0x14]; /* map of first 1 + 13 registers */ + cs8427_stream_t playback; + cs8427_stream_t capture; +} cs8427_t; + +static unsigned char swapbits(unsigned char val) +{ + int bit; + unsigned char res = 0; + for (bit = 0; bit < 8; bit++) { + res |= val & 1; + res <<= 1; + val >>= 1; + } + return res; +} + +int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr) +{ + int res; + + snd_i2c_lock(bus); + res = snd_i2c_probeaddr(bus, CS8427_ADDR | (addr & 7)); + snd_i2c_unlock(bus); + return res; +} + +static int snd_cs8427_reg_write(snd_i2c_device_t *device, unsigned char reg, unsigned char val) +{ + int err; + unsigned char buf[2]; + + buf[0] = reg & 0x7f; + buf[1] = val; + if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) { + snd_printk("unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err); + return err < 0 ? err : -EIO; + } + return 0; +} + +static int snd_cs8427_reg_read(snd_i2c_device_t *device, unsigned char reg) +{ + int err; + unsigned char buf; + + if ((err = snd_i2c_sendbytes(device, ®, 1)) != 1) { + snd_printk("unable to send register 0x%x byte to CS8427\n", reg); + return err < 0 ? err : -EIO; + } + if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) { + snd_printk("unable to read register 0x%x byte from CS8427\n", reg); + return err < 0 ? err : -EIO; + } + return buf; +} + +static int snd_cs8427_select_corudata(snd_i2c_device_t *device, int udata) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + int err; + + udata = udata ? CS8427_BSEL : 0; + if (udata != (chip->regmap[CS8427_REG_CSDATABUF] & udata)) { + chip->regmap[CS8427_REG_CSDATABUF] &= ~CS8427_BSEL; + chip->regmap[CS8427_REG_CSDATABUF] |= udata; + err = snd_cs8427_reg_write(device, CS8427_REG_CSDATABUF, chip->regmap[CS8427_REG_CSDATABUF]); + if (err < 0) + return err; + } + return 0; +} + +static int snd_cs8427_send_corudata(snd_i2c_device_t *device, + int udata, + unsigned char *ndata, + int count) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status; + char data[32]; + int err, idx; + + if (!memcmp(hw_data, ndata, count)) + return 0; + if ((err = snd_cs8427_select_corudata(device, udata)) < 0) + return err; + memcpy(hw_data, data, count); + if (udata) { + memset(data, 0, sizeof(data)); + if (memcmp(hw_data, data, 32) == 0) { + chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK; + chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS | CS8427_EFTUI; + if ((err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF, chip->regmap[CS8427_REG_UDATABUF])) < 0) + return err; + return 0; + } + } + data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF; + for (idx = 0; idx < count; idx++) + data[idx + 1] = swapbits(ndata[idx]); + if (snd_i2c_sendbytes(device, data, count) != count) + return -EIO; + return 1; +} + +static void snd_cs8427_free(snd_i2c_device_t *device) +{ + if (device->private_data) + snd_magic_kfree(device->private_data); +} + +int snd_cs8427_create(snd_i2c_bus_t *bus, + unsigned char addr, + snd_i2c_device_t **r_cs8427) +{ + static unsigned char initvals1[] = { + CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC, + /* CS8427_REG_CLOCKSOURCE: RMCK to OMCK, no validity, disable mutes, TCBL=output */ + CS8427_SWCLK, + /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs, normal stereo operation */ + 0x00, + /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial, Rx=>serial */ + CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER, + /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base = + covered input clock, recovered input clock source is Envy24 */ + CS8427_INC, + /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S, 24-bit, 64*Fsi */ + CS8427_SIDEL | CS8427_SILRPOL, + /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format = I2S, 24-bit, 64*Fsi */ + CS8427_SODEL | CS8427_SOLRPOL, + }; + static unsigned char initvals2[] = { + CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC, + /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */ + /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR, + Why setting CS8427_V causes clicks and glitches? */ + CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR, + /* CS8427_REG_CSDATABUF: + Registers 32-55 window to CS buffer + Inhibit D->E transfers from overwriting first 5 bytes of CS data. + Inhibit D->E transfers (all) of CS data. + Allow E->F transfer of CS data. + One byte mode; both A/B channels get same written CB data. + A channel info is output to chip's EMPH* pin. */ + CS8427_CBMR | CS8427_DETCI, + /* CS8427_REG_UDATABUF: + Use internal buffer to transmit User (U) data. + Chip's U pin is an output. + Transmit all O's for user data. + Inhibit D->E transfers. + Inhibit E->F transfers. */ + CS8427_UD | CS8427_EFTUI | CS8427_DETUI, + }; + int err; + cs8427_t *chip; + snd_i2c_device_t *device; + unsigned char buf[32 + 1]; + + if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0) + return err; + chip = device->private_data = snd_magic_kcalloc(cs8427_t, 0, GFP_KERNEL); + if (chip == NULL) { + snd_i2c_device_free(device); + return -ENOMEM; + } + device->private_free = snd_cs8427_free; + + snd_i2c_lock(bus); + if ((err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER)) != CS8427_VER8427A) { + snd_i2c_unlock(bus); + snd_printk("unable to find CS8427 signature (expected 0x%x, read 0x%x), initialization is not completed\n", CS8427_VER8427A, err); + return -EFAULT; + } + /* turn off run bit while making changes to configuration */ + if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00)) < 0) + goto __fail; + /* send initial values */ + memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6); + if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) { + err = err < 0 ? err : -EIO; + goto __fail; + } + /* Turn off CS8427 interrupt stuff that is not used in hardware */ + memset(buf, 0, 7); + /* from address 9 to 15 */ + buf[0] = 9; /* register */ + if ((err = snd_i2c_sendbytes(device, buf, 7)) != 7) + goto __fail; + /* send transfer initialization sequence */ + memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3); + if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) { + err = err < 0 ? err : -EIO; + goto __fail; + } + /* write default channel status bytes */ + buf[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF; + buf[1] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0)); + buf[2] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8)); + buf[3] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16)); + buf[4] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24)); + memset(buf + 5, 0, sizeof(buf)-5); + memcpy(chip->playback.def_status, buf + 1, 24); + memcpy(chip->playback.pcm_status, buf + 1, 24); + if ((err = snd_i2c_sendbytes(device, buf, 33)) != 33) + goto __fail; + /* turn on run bit and rock'n'roll */ + chip->regmap[CS8427_REG_CLOCKSOURCE] = initvals1[4] | CS8427_RUN; + if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE])) < 0) + goto __fail; + +#if 0 // it's nice for read tests + { + char buf[128]; + buf[0] = 0x81; + snd_i2c_sendbytes(device, buf, 1); + snd_i2c_readbytes(device, buf, 127); + } +#endif + + snd_i2c_unlock(bus); + if (r_cs8427) + *r_cs8427 = device; + return 0; + + __fail: + snd_i2c_unlock(bus); + snd_i2c_device_free(device); + return err < 0 ? err : -EIO; +} + +static int snd_cs8427_in_status_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_cs8427_in_status_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + int data; + + snd_i2c_lock(device->bus); + data = snd_cs8427_reg_read(device, 15); + snd_i2c_unlock(device->bus); + if (data < 0) + return data; + ucontrol->value.integer.value[0] = data; + return 0; +} + +static int snd_cs8427_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs8427_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + + snd_i2c_lock(device->bus); + memcpy(ucontrol->value.iec958.status, chip->playback.def_status, 23); + snd_i2c_unlock(device->bus); + return 0; +} + +static int snd_cs8427_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + unsigned char *status = kcontrol->private_value ? chip->playback.pcm_status : chip->playback.def_status; + int err, change; + + snd_i2c_lock(device->bus); + change = memcmp(ucontrol->value.iec958.status, status, 23) != 0; + memcpy(status, ucontrol->value.iec958.status, 23); + if (change && (kcontrol->private_value ? chip->playback.substream != NULL : chip->playback.substream == NULL)) { + err = snd_cs8427_send_corudata(device, 0, status, 23); + if (err < 0) + change = err; + } + snd_i2c_unlock(device->bus); + return change; +} + +static int snd_cs8427_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs8427_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, 23); + return 0; +} + +#define CONTROLS (sizeof(snd_cs8427_iec958_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs8427_iec958_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .info = snd_cs8427_in_status_info, + .name = "IEC958 CS8427 Input Status", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .get = snd_cs8427_in_status_get, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + .info = snd_cs8427_spdif_mask_info, + .get = snd_cs8427_spdif_mask_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_cs8427_spdif_info, + .get = snd_cs8427_spdif_get, + .put = snd_cs8427_spdif_put, + .private_value = 0 +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_cs8427_spdif_info, + .get = snd_cs8427_spdif_get, + .put = snd_cs8427_spdif_put, + .private_value = 1 +}}; + +int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, + snd_pcm_substream_t *play_substream, + snd_pcm_substream_t *cap_substream) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + snd_assert(play_substream && cap_substream, return -EINVAL); + for (idx = 0; idx < CONTROLS; idx++) { + kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427); + if (kctl == NULL) + return -ENOMEM; + kctl->id.device = play_substream->pcm->device; + kctl->id.subdevice = play_substream->number; + err = snd_ctl_add(cs8427->bus->card, kctl); + if (err < 0) + return err; + if (!strcmp(kctl->id.name, SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM))) + chip->playback.pcm_ctl = kctl; + } + + snd_assert(chip->playback.pcm_ctl, return -EIO); + return 0; +} + +int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active) +{ + cs8427_t *chip; + + snd_assert(cs8427, return -ENXIO); + chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + if (active) + memcpy(chip->playback.pcm_status, chip->playback.def_status, 24); + chip->playback.pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(cs8427->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->playback.pcm_ctl->id); + return 0; +} + +int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate) +{ + cs8427_t *chip; + char *status; + int err; + + snd_assert(cs8427, return -ENXIO); + chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + status = chip->playback.pcm_status; + snd_i2c_lock(cs8427->bus); + if (status[0] & IEC958_AES0_PROFESSIONAL) { + status[0] &= ~IEC958_AES0_PRO_FS; + switch (rate) { + case 32000: status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 44100: status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 48000: status[0] |= IEC958_AES0_PRO_FS_48000; break; + default: status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + } else { + status[3] &= ~IEC958_AES3_CON_FS; + switch (rate) { + case 32000: status[3] |= IEC958_AES3_CON_FS_32000; break; + case 44100: status[3] |= IEC958_AES3_CON_FS_44100; break; + case 48000: status[3] |= IEC958_AES3_CON_FS_48000; break; + } + } + err = snd_cs8427_send_corudata(cs8427, 0, status, 23); + if (err > 0) + snd_ctl_notify(cs8427->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->playback.pcm_ctl->id); + snd_i2c_unlock(cs8427->bus); + return err < 0 ? err : 0; +} + +EXPORT_SYMBOL(snd_cs8427_detect); +EXPORT_SYMBOL(snd_cs8427_create); +EXPORT_SYMBOL(snd_cs8427_iec958_build); +EXPORT_SYMBOL(snd_cs8427_iec958_active); +EXPORT_SYMBOL(snd_cs8427_iec958_pcm); diff -urN linux-2.4.21-rc1.orig/sound/i2c/i2c.c linux/sound/i2c/i2c.c --- linux-2.4.21-rc1.orig/sound/i2c/i2c.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/i2c/i2c.c 2002-09-30 15:42:53.000000000 -0600 @@ -0,0 +1,333 @@ +/* + * Generic i2c interface for ALSA + * + * (c) 1998 Gerd Knorr + * Modified for the ALSA driver by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic i2c interface for ALSA"); +MODULE_LICENSE("GPL"); + +static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr); + +static snd_i2c_ops_t snd_i2c_bit_ops = { + .sendbytes = snd_i2c_bit_sendbytes, + .readbytes = snd_i2c_bit_readbytes, + .probeaddr = snd_i2c_bit_probeaddr, +}; + +static int snd_i2c_bus_free(snd_i2c_bus_t *bus) +{ + snd_i2c_bus_t *slave; + snd_i2c_device_t *device; + + snd_assert(bus != NULL, return -EINVAL); + while (!list_empty(&bus->devices)) { + device = snd_i2c_device(bus->devices.next); + snd_i2c_device_free(device); + } + if (bus->master) + list_del(&bus->buses); + else { + while (!list_empty(&bus->buses)) { + slave = snd_i2c_slave_bus(bus->buses.next); + snd_device_free(bus->card, slave); + } + } + if (bus->private_free) + bus->private_free(bus); + snd_magic_kfree(bus); + return 0; +} + +static int snd_i2c_bus_dev_free(snd_device_t *device) +{ + snd_i2c_bus_t *bus = snd_magic_cast(snd_i2c_bus_t, device->device_data, return -ENXIO); + return snd_i2c_bus_free(bus); +} + +int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c) +{ + snd_i2c_bus_t *bus; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_i2c_bus_dev_free, + }; + + *ri2c = NULL; + bus = (snd_i2c_bus_t *)snd_magic_kcalloc(snd_i2c_bus_t, 0, GFP_KERNEL); + if (bus == NULL) + return -ENOMEM; + spin_lock_init(&bus->lock); + INIT_LIST_HEAD(&bus->devices); + INIT_LIST_HEAD(&bus->buses); + bus->card = card; + bus->ops = &snd_i2c_bit_ops; + if (master) { + list_add_tail(&bus->buses, &master->buses); + bus->master = master; + } + strncpy(bus->name, name, sizeof(bus->name) - 1); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) { + snd_i2c_bus_free(bus); + return err; + } + *ri2c = bus; + return 0; +} + +int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice) +{ + snd_i2c_device_t *device; + + *rdevice = NULL; + snd_assert(bus != NULL, return -EINVAL); + device = (snd_i2c_device_t *)snd_magic_kcalloc(snd_i2c_device_t, 0, GFP_KERNEL); + if (device == NULL) + return -ENOMEM; + device->addr = addr; + strncpy(device->name, name, sizeof(device->name)-1); + list_add_tail(&device->list, &bus->devices); + device->bus = bus; + *rdevice = device; + return 0; +} + +int snd_i2c_device_free(snd_i2c_device_t *device) +{ + if (device->bus) + list_del(&device->list); + if (device->private_free) + device->private_free(device); + snd_magic_kfree(device); + return 0; +} + +int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + return device->bus->ops->sendbytes(device, bytes, count); +} + + +int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + return device->bus->ops->readbytes(device, bytes, count); +} + +int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + return bus->ops->probeaddr(bus, addr); +} + +/* + * bit-operations + */ + +static inline void snd_i2c_bit_hw_start(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->start) + bus->hw_ops.bit->start(bus); +} + +static inline void snd_i2c_bit_hw_stop(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->stop) + bus->hw_ops.bit->stop(bus); +} + +static void snd_i2c_bit_direction(snd_i2c_bus_t *bus, int clock, int data) +{ + if (bus->hw_ops.bit->direction) + bus->hw_ops.bit->direction(bus, clock, data); +} + +static void snd_i2c_bit_set(snd_i2c_bus_t *bus, int clock, int data) +{ + bus->hw_ops.bit->setlines(bus, clock, data); +} + +#if 0 +static int snd_i2c_bit_clock(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->getclock) + return bus->hw_ops.bit->getclock(bus); + return -ENXIO; +} +#endif + +static int snd_i2c_bit_data(snd_i2c_bus_t *bus, int ack) +{ + return bus->hw_ops.bit->getdata(bus, ack); +} + +static void snd_i2c_bit_start(snd_i2c_bus_t *bus) +{ + snd_i2c_bit_hw_start(bus); + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_set(bus, 1, 0); + snd_i2c_bit_set(bus, 0, 0); +} + +static void snd_i2c_bit_stop(snd_i2c_bus_t *bus) +{ + snd_i2c_bit_set(bus, 0, 0); + snd_i2c_bit_set(bus, 1, 0); + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_hw_stop(bus); +} + +static void snd_i2c_bit_send(snd_i2c_bus_t *bus, int data) +{ + snd_i2c_bit_set(bus, 0, data); + snd_i2c_bit_set(bus, 1, data); + snd_i2c_bit_set(bus, 0, data); +} + +static int snd_i2c_bit_ack(snd_i2c_bus_t *bus) +{ + int ack; + + snd_i2c_bit_set(bus, 0, 1); + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_direction(bus, 1, 0); /* SCL - wr, SDA - rd */ + ack = snd_i2c_bit_data(bus, 1); + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_set(bus, 0, 1); + return ack ? -EIO : 0; +} + +static int snd_i2c_bit_sendbyte(snd_i2c_bus_t *bus, unsigned char data) +{ + int i, err; + + for (i = 7; i >= 0; i--) + snd_i2c_bit_send(bus, !!(data & (1 << i))); + if ((err = snd_i2c_bit_ack(bus)) < 0) + return err; + return 0; +} + +static int snd_i2c_bit_readbyte(snd_i2c_bus_t *bus, int last) +{ + int i; + unsigned char data = 0; + + snd_i2c_bit_set(bus, 0, 1); + snd_i2c_bit_direction(bus, 1, 0); /* SCL - wr, SDA - rd */ + for (i = 7; i >= 0; i--) { + snd_i2c_bit_set(bus, 1, 1); + if (snd_i2c_bit_data(bus, 0)) + data |= (1 << i); + snd_i2c_bit_set(bus, 0, 1); + } + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_send(bus, !!last); + return data; +} + +static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + snd_i2c_bus_t *bus = device->bus; + int err, res = 0; + + if (device->flags & SND_I2C_DEVICE_ADDRTEN) + return -EIO; /* not yet implemented */ + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) { + snd_i2c_bit_hw_stop(bus); + return err; + } + while (count-- > 0) { + if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) { + snd_i2c_bit_hw_stop(bus); + return err; + } + res++; + } + snd_i2c_bit_stop(bus); + return res; +} + +static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + snd_i2c_bus_t *bus = device->bus; + int err, res = 0; + + if (device->flags & SND_I2C_DEVICE_ADDRTEN) + return -EIO; /* not yet implemented */ + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) { + snd_i2c_bit_hw_stop(bus); + return err; + } + while (count-- > 0) { + if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) { + snd_i2c_bit_hw_stop(bus); + return err; + } + *bytes++ = (unsigned char)err; + res++; + } + snd_i2c_bit_stop(bus); + return res; +} + +static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + int err; + + if (addr & 0x8000) /* 10-bit address */ + return -EIO; /* not yet implemented */ + if (addr & 0x7f80) /* invalid address */ + return -EINVAL; + snd_i2c_bit_start(bus); + err = snd_i2c_bit_sendbyte(bus, addr << 1); + snd_i2c_bit_stop(bus); + return err; +} + +EXPORT_SYMBOL(snd_i2c_bus_create); +EXPORT_SYMBOL(snd_i2c_device_create); +EXPORT_SYMBOL(snd_i2c_device_free); +EXPORT_SYMBOL(snd_i2c_sendbytes); +EXPORT_SYMBOL(snd_i2c_readbytes); +EXPORT_SYMBOL(snd_i2c_probeaddr); + +static int __init alsa_i2c_init(void) +{ + return 0; +} + +static void __exit alsa_i2c_exit(void) +{ +} + +module_init(alsa_i2c_init) +module_exit(alsa_i2c_exit) diff -urN linux-2.4.21-rc1.orig/sound/i2c/l3/Makefile linux/sound/i2c/l3/Makefile --- linux-2.4.21-rc1.orig/sound/i2c/l3/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/i2c/l3/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,19 @@ +# +# Makefile for ALSA +# + +O_TARGET = l3.o + +list-multi += snd-uda1341.o + +export-objs += uda1341.o + +snd-uda1341-objs := uda1341.o + +# Module Dependency +obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-uda1341.o + +include $(TOPDIR)/Rules.make + +snd-uda1341.o: $(snd-uda1341-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-uda1341-objs) diff -urN linux-2.4.21-rc1.orig/sound/i2c/l3/uda1341.c linux/sound/i2c/l3/uda1341.c --- linux-2.4.21-rc1.orig/sound/i2c/l3/uda1341.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/i2c/l3/uda1341.c 2003-02-13 12:19:19.000000000 -0700 @@ -0,0 +1,843 @@ +/* + * Philips UDA1341 mixer device driver + * Copyright (c) 2002 Tomas Kasparek + * + * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2002-03-13 Tomas Kasparek initial release - based on uda1341.c from OSS + * 2002-03-28 Tomas Kasparek basic mixer is working (volume, bass, treble) + * 2002-03-30 Tomas Kasparek proc filesystem support, complete mixer and DSP + * features support + * 2002-04-12 Tomas Kasparek proc interface update, code cleanup + * 2002-05-12 Tomas Kasparek another code cleanup + */ + +/* uda1341.c,v 1.7 2003/02/13 19:19:19 perex Exp */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#undef DEBUG_MODE +#undef DEBUG_FUNCTION_NAMES +#include + +/* {{{ HW regs definition */ + +#define STAT0 0x00 +#define STAT1 0x80 +#define STAT_MASK 0x80 + +#define DATA0_0 0x00 +#define DATA0_1 0x40 +#define DATA0_2 0x80 +#define DATA_MASK 0xc0 + +#define IS_DATA0(x) ((x) >= data0_0 && (x) <= data0_2) +#define IS_DATA1(x) ((x) == data1) +#define IS_STATUS(x) ((x) == stat0 || (x) == stat1) +#define IS_EXTEND(x) ((x) >= ext0 && (x) <= ext6) + +/* }}} */ + +enum uda1341_regs_names { + stat0, + stat1, + data0_0, + data0_1, + data0_2, + data1, + ext0, + ext1, + ext2, + empty, + ext4, + ext5, + ext6, + uda1341_reg_last, +}; + +const char *uda1341_reg_names[] = { + "stat 0 ", + "stat 1 ", + "data 00", + "data 01", + "data 02", + "data 1 ", + "ext 0", + "ext 1", + "ext 2", + "empty", + "ext 4", + "ext 5", + "ext 6", +}; + +const int uda1341_enum_items[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, //peak - before/after + 4, //deemp - none/32/44.1/48 + 0, + 4, //filter - flat/min/min/max + 0, 0, 0, + 4, //mixer - differ/line/mic/mixer + 0, 0, 0, 0, 0, +}; + +const char ** uda1341_enum_names[] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + peak_names, //peak - before/after + deemp_names, //deemp - none/32/44.1/48 + NULL, + filter_names, //filter - flat/min/min/max + NULL, NULL, NULL, + mixer_names, //mixer - differ/line/mic/mixer + NULL, NULL, NULL, NULL, NULL, +}; + +typedef int uda1341_cfg[CMD_LAST]; + +typedef struct uda1341 uda1341_t; + +struct uda1341{ + void (*write) (struct l3_client *uda1341, unsigned short reg, unsigned short val); + unsigned char (*read) (struct l3_client *uda1341, unsigned short reg); + + unsigned char regs[uda1341_reg_last]; + int active; + + spinlock_t reg_lock; + + snd_card_t *card; + + uda1341_cfg cfg; +}; + +//hack for ALSA magic casting +typedef struct l3_client l3_client_t; +#define chip_t l3_client_t + +static struct l3_client *uda1341=NULL; + +/* transfer 8bit integer into string with binary representation */ +void int2str_bin8(uint8_t val, char *buf){ + const int size = sizeof(val) * 8; + int i; + + for (i= 0; i < size; i++){ + *(buf++) = (val >> (size - 1)) ? '1' : '0'; + val <<= 1; + } + *buf = '\0'; //end the string with zero +} + +/* {{{ HW manipulation routines */ + +void snd_uda1341_codec_write(struct l3_client *clnt, unsigned short reg, unsigned short val) +{ + struct uda1341 *uda = clnt->driver_data; + + int err=0; + unsigned char buf[2] = { 0xc0, 0xe0 }; // for EXT addressing + + DEBUG_NAME(KERN_DEBUG "codec_write: reg: %s val: %d ", uda1341_reg_names[reg], val); + + uda->regs[reg] = val; + + if (uda->active) { + DEBUG("O"); + if (IS_DATA0(reg)) { + DEBUG(" D0 "); + err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)&val, 1); + } else if (IS_DATA1(reg)) { + DEBUG(" D1 "); + err = l3_write(clnt, UDA1341_DATA1, (const unsigned char *)&val, 1); + } else if (IS_STATUS(reg)) { + DEBUG(" S "); + err = l3_write(clnt, UDA1341_STATUS, (const unsigned char *)&val, 1); + } else if (IS_EXTEND(reg)) { + DEBUG(" E "); + buf[0] |= (reg - ext0) & 0x7; //EXT address + buf[1] |= val; //EXT data + DEBUG("%x %x ", buf[0], buf[1]); + err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)buf, 2); + } + + if (err == 1 || err == 2) + DEBUG("K\n"); + else + DEBUG(" Error: %d\n", err); + } else + printk(KERN_ERR "UDA1341 codec not active!\n"); +} + +unsigned char snd_uda1341_codec_read(struct l3_client *clnt, unsigned short reg) +{ + int err=0; + unsigned char val; + + DEBUG_NAME(KERN_DEBUG "codec_read: reg: %d ", reg); + + DEBUG("O"); + + err = l3_read(clnt, reg, &val, 1); + + if (err == 1) + DEBUG("K\n"); + else + DEBUG(" Error: %d\n", err); + + return val & 63; //use just 6bits - the rest is address of the reg +} + +static int snd_uda1341_valid_reg(struct l3_client *clnt, unsigned short reg) +{ + DEBUG_NAME(KERN_DEBUG "valid_reg\n"); + return reg < uda1341_reg_last; +} + +int snd_uda1341_update_bits(struct l3_client *clnt, unsigned short reg, unsigned short mask, + unsigned short shift, unsigned short value, int flush) +{ + int change; + unsigned short old, new; + struct uda1341 *uda = clnt->driver_data; + + DEBUG(KERN_DEBUG "update_bits: reg: %s mask: %d shift: %d val: %d\n", + uda1341_reg_names[reg], mask, shift, value); + + if (!snd_uda1341_valid_reg(clnt, reg)) + return -EINVAL; + spin_lock(&uda->reg_lock); + old = uda->regs[reg]; + new = (old & ~(mask << shift)) | (value << shift); + change = old != new; + if (change) { + if (flush) uda->write(clnt, reg, new); + uda->regs[reg] = new; + } + spin_unlock(&uda->reg_lock); + return change; +} + +int snd_uda1341_cfg_write(struct l3_client *clnt, unsigned short what, + unsigned short value, int flush) +{ + struct uda1341 *uda = clnt->driver_data; + int ret = 0; + + DEBUG_NAME(KERN_DEBUG "cfg_write what: %d value: %d\n", what, value); + + uda->cfg[what] = value; + + switch(what) { + case CMD_RESET: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, 1, flush);//MUTE + ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 1, flush);//RESET + ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 0, flush);// RESTORE + uda->cfg[CMD_RESET]=0; + break; + case CMD_FS: ret = snd_uda1341_update_bits(clnt, stat0, 3, 4, value, flush); + break; + case CMD_FORMAT: ret = snd_uda1341_update_bits(clnt, stat0, 7, 1, value, flush); + break; + case CMD_OGAIN: ret = snd_uda1341_update_bits(clnt, stat1, 1, 6, value, flush); + break; + case CMD_IGAIN: ret = snd_uda1341_update_bits(clnt, stat1, 1, 5, value, flush); + break; + case CMD_DAC: ret = snd_uda1341_update_bits(clnt, stat1, 1, 0, value, flush); + break; + case CMD_ADC: ret = snd_uda1341_update_bits(clnt, stat1, 1, 1, value, flush); + break; + case CMD_VOLUME: ret = snd_uda1341_update_bits(clnt, data0_0, 63, 0, value, flush); + break; + case CMD_BASS: ret = snd_uda1341_update_bits(clnt, data0_1, 15, 2, value, flush); + break; + case CMD_TREBBLE: ret = snd_uda1341_update_bits(clnt, data0_1, 3, 0, value, flush); + break; + case CMD_PEAK: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 5, value, flush); + break; + case CMD_DEEMP: ret = snd_uda1341_update_bits(clnt, data0_2, 3, 3, value, flush); + break; + case CMD_MUTE: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, value, flush); + break; + case CMD_FILTER: ret = snd_uda1341_update_bits(clnt, data0_2, 3, 0, value, flush); + break; + case CMD_CH1: ret = snd_uda1341_update_bits(clnt, ext0, 31, 0, value, flush); + break; + case CMD_CH2: ret = snd_uda1341_update_bits(clnt, ext1, 31, 0, value, flush); + break; + case CMD_MIC: ret = snd_uda1341_update_bits(clnt, ext2, 7, 2, value, flush); + break; + case CMD_MIXER: ret = snd_uda1341_update_bits(clnt, ext2, 3, 0, value, flush); + break; + case CMD_AGC: ret = snd_uda1341_update_bits(clnt, ext4, 1, 4, value, flush); + break; + case CMD_IG: ret = snd_uda1341_update_bits(clnt, ext4, 3, 0, value & 0x3, flush); + ret = snd_uda1341_update_bits(clnt, ext5, 31, 0, value >> 2, flush); + break; + case CMD_AGC_TIME: ret = snd_uda1341_update_bits(clnt, ext6, 7, 2, value, flush); + break; + case CMD_AGC_LEVEL: ret = snd_uda1341_update_bits(clnt, ext6, 3, 0, value, flush); + break; + default: ret = -EINVAL; break; + } + + if (!uda->active) { + printk(KERN_ERR "UDA1341 codec not active!\n"); + } + return ret; +} + + +/* }}} */ + +/* {{{ Proc interface */ + +static void snd_uda1341_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + struct l3_client *clnt = snd_magic_cast(l3_client_t, entry->private_data, return); + struct uda1341 *uda = clnt->driver_data; + int peak; + + peak = snd_uda1341_codec_read(clnt, UDA1341_DATA1); + + DEBUG_NAME(KERN_DEBUG "proc_read\n"); + + snd_iprintf(buffer, "%s\n\n", uda->card->longname); + + // for information about computed values see UDA1341TS datasheet pages 15 - 21 + snd_iprintf(buffer, "DAC power : %s\n", uda->cfg[CMD_DAC] ? "on" : "off"); + snd_iprintf(buffer, "ADC power : %s\n", uda->cfg[CMD_ADC] ? "on" : "off"); + snd_iprintf(buffer, "Clock frequency : %s\n", fs_names[uda->cfg[CMD_FS]]); + snd_iprintf(buffer, "Data format : %s\n\n", format_names[uda->cfg[CMD_FORMAT]]); + + snd_iprintf(buffer, "Filter mode : %s\n", filter_names[uda->cfg[CMD_FILTER]]); + snd_iprintf(buffer, "Mixer mode : %s\n", mixer_names[uda->cfg[CMD_MIXER]]); + snd_iprintf(buffer, "De-emphasis : %s\n", deemp_names[uda->cfg[CMD_DEEMP]]); + snd_iprintf(buffer, "Peak detection pos. : %s\n", uda->cfg[CMD_PEAK] ? "after" : "before"); + snd_iprintf(buffer, "Peak value : %s\n\n", peak_value[peak]); + + snd_iprintf(buffer, "Automatic Gain Ctrl : %s\n", uda->cfg[CMD_AGC] ? "on" : "off"); + snd_iprintf(buffer, "AGC attack time : %d ms\n", AGC_atime[uda->cfg[CMD_AGC_TIME]]); + snd_iprintf(buffer, "AGC decay time : %d ms\n", AGC_dtime[uda->cfg[CMD_AGC_TIME]]); + snd_iprintf(buffer, "AGC output level : %s dB\n\n", AGC_level[uda->cfg[CMD_AGC_LEVEL]]); + + snd_iprintf(buffer, "Mute : %s\n", uda->cfg[CMD_MUTE] ? "on" : "off"); + + if(uda->cfg[CMD_VOLUME] == 0) + snd_iprintf(buffer, "Volume : 0 dB\n"); + else if (uda->cfg[CMD_VOLUME] < 62) + snd_iprintf(buffer, "Volume : %d dB\n", -1*uda->cfg[CMD_VOLUME] +1); + else + snd_iprintf(buffer, "Volume : -INF dB\n"); + snd_iprintf(buffer, "Bass : %s\n", bass_values[uda->cfg[CMD_FILTER]][uda->cfg[CMD_BASS]]); + snd_iprintf(buffer, "Trebble : %d dB\n", uda->cfg[CMD_FILTER] ? 2*uda->cfg[CMD_TREBBLE] : 0); + snd_iprintf(buffer, "Input Gain (6dB) : %s\n", uda->cfg[CMD_IGAIN] ? "on" : "off"); + snd_iprintf(buffer, "Output Gain (6dB) : %s\n", uda->cfg[CMD_OGAIN] ? "on" : "off"); + snd_iprintf(buffer, "Mic sensitivity : %s\n", mic_sens_value[uda->cfg[CMD_MIC]]); + + + if(uda->cfg[CMD_CH1] < 31) + snd_iprintf(buffer, "Mixer gain channel 1: -%d.%c dB\n", + ((uda->cfg[CMD_CH1] >> 1) * 3) + (uda->cfg[CMD_CH1] & 1), + uda->cfg[CMD_CH1] & 1 ? '5' : '0'); + else + snd_iprintf(buffer, "Mixer gain channel 1: -INF dB\n"); + if(uda->cfg[CMD_CH2] < 31) + snd_iprintf(buffer, "Mixer gain channel 2: -%d.%c dB\n", + ((uda->cfg[CMD_CH2] >> 1) * 3) + (uda->cfg[CMD_CH2] & 1), + uda->cfg[CMD_CH2] & 1 ? '5' : '0'); + else + snd_iprintf(buffer, "Mixer gain channel 2: -INF dB\n"); + + if(uda->cfg[CMD_IG] > 5) + snd_iprintf(buffer, "Input Amp. Gain ch 2: %d.%c dB\n", + (uda->cfg[CMD_IG] >> 1) -3, uda->cfg[CMD_IG] & 1 ? '5' : '0'); + else + snd_iprintf(buffer, "Input Amp. Gain ch 2: %s dB\n", ig_small_value[uda->cfg[CMD_IG]]); +} + +static void snd_uda1341_proc_regs_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + struct l3_client *clnt = snd_magic_cast(l3_client_t, entry->private_data, return); + struct uda1341 *uda = clnt->driver_data; + int reg; + char buf[12]; + + DEBUG_NAME(KERN_DEBUG "proc_regs_read\n"); + + spin_lock(&uda->reg_lock); + for (reg = 0; reg < uda1341_reg_last; reg ++) { + if (reg == empty) + continue; + int2str_bin8(uda->regs[reg], buf); + snd_iprintf(buffer, "%s = %s\n", uda1341_reg_names[reg], buf); + } + + int2str_bin8(snd_uda1341_codec_read(clnt, UDA1341_DATA1), buf); + snd_iprintf(buffer, "DATA1 = %s\n", buf); + + spin_unlock(&uda->reg_lock); +} + +static void __devinit snd_uda1341_proc_init(snd_card_t *card, struct l3_client *clnt) +{ + snd_info_entry_t *entry; + struct uda1341 *uda = clnt->driver_data; + + DEBUG_NAME(KERN_DEBUG "proc_init\n"); + + if (! snd_card_proc_new(card, "uda1341", &entry)) + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read); + if (! snd_card_proc_new(card, "uda1341-regs", &entry)) { + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read); +} + +/* }}} */ + +/* {{{ Mixer controls setting */ + +/* {{{ UDA1341 single functions */ + +#define UDA1341_SINGLE(xname, where, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_uda1341_info_single, \ + .get = snd_uda1341_get_single, .put = snd_uda1341_put_single, \ + .private_value = where | (reg << 5) | (shift << 9) | (mask << 12) | (invert << 18) \ +} + +static int snd_uda1341_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 12) & 63; + + DEBUG_NAME(KERN_DEBUG "info_single where: %ld\n", kcontrol->private_value & 31); + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_uda1341_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + struct l3_client *clnt = snd_kcontrol_chip(kcontrol); + uda1341_t *uda = clnt->driver_data; + int where = kcontrol->private_value & 31; + int mask = (kcontrol->private_value >> 12) & 63; + int invert = (kcontrol->private_value >> 18) & 1; + + DEBUG_NAME(KERN_DEBUG "get_single where: %d (val: %d)\n", where, uda->cfg[where]); + + ucontrol->value.integer.value[0] = uda->cfg[where]; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + + return 0; +} + +static int snd_uda1341_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + struct l3_client *clnt = snd_kcontrol_chip(kcontrol); + uda1341_t *uda = clnt->driver_data; + int where = kcontrol->private_value & 31; + int reg = (kcontrol->private_value >> 5) & 15; + int shift = (kcontrol->private_value >> 9) & 7; + int mask = (kcontrol->private_value >> 12) & 63; + int invert = (kcontrol->private_value >> 18) & 1; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + + DEBUG(KERN_DEBUG "put_single where: %d reg: %d mask: %d shift: %d inv: %d val: %d\n", + where, reg, mask, shift, invert, val); + + uda->cfg[where] = val; + return snd_uda1341_update_bits(clnt, reg, mask, shift, val, FLUSH); +} + +/* }}} */ + +/* {{{ UDA1341 enum functions */ + +#define UDA1341_ENUM(xname, where, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_uda1341_info_enum, \ + .get = snd_uda1341_get_enum, .put = snd_uda1341_put_enum, \ + .private_value = where | (reg << 5) | (shift << 9) | (mask << 12) | (invert << 18) \ +} + +static int snd_uda1341_info_enum(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int where = kcontrol->private_value & 31; + const char **texts; + + DEBUG_NAME(KERN_DEBUG "info_enum where: %d\n", where); + + // this register we dont handle this way + if (!uda1341_enum_items[where]) + return -EINVAL; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = uda1341_enum_items[where]; + + if (uinfo->value.enumerated.item >= uda1341_enum_items[where]) + uinfo->value.enumerated.item = uda1341_enum_items[where] - 1; + + texts = uda1341_enum_names[where]; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_uda1341_get_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + struct l3_client *clnt = snd_kcontrol_chip(kcontrol); + uda1341_t *uda = clnt->driver_data; + int where = kcontrol->private_value & 31; + + DEBUG_NAME(KERN_DEBUG "get_enum where: %d (val: %d)\n", where, uda->cfg[where]); + + ucontrol->value.enumerated.item[0] = uda->cfg[where]; + return 0; +} + +static int snd_uda1341_put_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + struct l3_client *clnt = snd_kcontrol_chip(kcontrol); + uda1341_t *uda = clnt->driver_data; + int where = kcontrol->private_value & 31; + int reg = (kcontrol->private_value >> 5) & 15; + int shift = (kcontrol->private_value >> 9) & 7; + int mask = (kcontrol->private_value >> 12) & 63; + + uda->cfg[where] = (ucontrol->value.enumerated.item[0] & mask); + + DEBUG(KERN_DEBUG "put_enum where: %d reg: %d mask: %d shift: %d val: %d\n", + where, reg, mask, shift, uda->cfg[where]); + + return snd_uda1341_update_bits(clnt, reg, mask, shift, uda->cfg[where], FLUSH); +} + +/* }}} */ + +/* {{{ UDA1341 2regs functions */ + +#define UDA1341_2REGS(xname, where, reg_1, reg_2, shift_1, shift_2, mask_1, mask_2, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_uda1341_info_2regs, \ + .get = snd_uda1341_get_2regs, .put = snd_uda1341_put_2regs, \ + .private_value = where | (reg_1 << 5) | (reg_2 << 9) | (shift_1 << 13) | (shift_2 << 16) | \ + (mask_1 << 19) | (mask_2 << 25) | (invert << 31) \ +} + + +static int snd_uda1341_info_2regs(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask_1 = (kcontrol->private_value >> 19) & 63; + int mask_2 = (kcontrol->private_value >> 25) & 63; + int mask; + + DEBUG_NAME(KERN_DEBUG "info_2regs where: %ld\n", kcontrol->private_value & 31); + + mask = (mask_2 + 1) * (mask_1 + 1) - 1; + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_uda1341_get_2regs(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + struct l3_client *clnt = snd_kcontrol_chip(kcontrol); + uda1341_t *uda = clnt->driver_data; + int where = kcontrol->private_value & 31; + int mask_1 = (kcontrol->private_value >> 19) & 63; + int mask_2 = (kcontrol->private_value >> 25) & 63; + int invert = (kcontrol->private_value >> 31) & 1; + int mask; + + DEBUG_NAME(KERN_DEBUG "get_2regs where: %d (val: %d)\n", where, uda->cfg[where]); + + mask = (mask_2 + 1) * (mask_1 + 1) - 1; + + ucontrol->value.integer.value[0] = uda->cfg[where]; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_uda1341_put_2regs(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + struct l3_client *clnt = snd_kcontrol_chip(kcontrol); + uda1341_t *uda = clnt->driver_data; + int where = kcontrol->private_value & 31; + int reg_1 = (kcontrol->private_value >> 5) & 15; + int reg_2 = (kcontrol->private_value >> 9) & 15; + int shift_1 = (kcontrol->private_value >> 13) & 7; + int shift_2 = (kcontrol->private_value >> 16) & 7; + int mask_1 = (kcontrol->private_value >> 19) & 63; + int mask_2 = (kcontrol->private_value >> 25) & 63; + int invert = (kcontrol->private_value >> 31) & 1; + int mask; + unsigned short val1, val2, val; + + val = ucontrol->value.integer.value[0]; + + DEBUG_NAME(KERN_DEBUG "put_2regs where: %d reg1: %d reg2: %d mask1: %d mask2: %d " + "shift1: %d shift2: %d inv: %d val: %d\n", where, reg_1, reg_2, mask_1, mask_2, + shift_1, shift_2, invert, val); + + mask = (mask_2 + 1) * (mask_1 + 1) - 1; + + val1 = val & mask_1; + val2 = (val / (mask_1 + 1)) & mask_2; + + if (invert) { + val1 = mask_1 - val1; + val2 = mask_2 - val2; + } + + uda->cfg[where] = invert ? mask - val : val; + + //FIXME - return value + snd_uda1341_update_bits(clnt, reg_1, mask_1, shift_1, val1, FLUSH); + return snd_uda1341_update_bits(clnt, reg_2, mask_2, shift_2, val2, FLUSH); +} + +/* }}} */ + +#define UDA1341_CONTROLS (sizeof(snd_uda1341_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_uda1341_controls[] = { + UDA1341_SINGLE("Master Playback Switch", CMD_MUTE, data0_2, 2, 1, 1), + UDA1341_SINGLE("Master Playback Volume", CMD_VOLUME, data0_0, 0, 63, 1), + + UDA1341_SINGLE("Bass Playback Volume", CMD_BASS, data0_1, 2, 15, 0), + UDA1341_SINGLE("Treble Playback Volume", CMD_TREBBLE, data0_1, 0, 3, 0), + + UDA1341_SINGLE("Input Gain Switch", CMD_IGAIN, stat1, 5, 1, 0), + UDA1341_SINGLE("Output Gain Switch", CMD_OGAIN, stat1, 6, 1, 0), + + UDA1341_SINGLE("Mixer Gain Channel 1 Volume", CMD_CH1, ext0, 0, 31, 1), + UDA1341_SINGLE("Mixer Gain Channel 2 Volume", CMD_CH2, ext1, 0, 31, 1), + + UDA1341_SINGLE("Mic Sensitivity Volume", CMD_MIC, ext2, 2, 7, 0), + + UDA1341_SINGLE("AGC Output Level", CMD_AGC_LEVEL, ext6, 0, 3, 0), + UDA1341_SINGLE("AGC Time Constant", CMD_AGC_TIME, ext6, 2, 7, 0), + UDA1341_SINGLE("AGC Time Constant Switch", CMD_AGC, ext4, 4, 1, 0), + + UDA1341_SINGLE("DAC Power", CMD_DAC, stat1, 0, 1, 0), + UDA1341_SINGLE("ADC Power", CMD_ADC, stat1, 1, 1, 0), + + UDA1341_ENUM("Peak detection", CMD_PEAK, data0_2, 5, 1, 0), + UDA1341_ENUM("De-emphasis", CMD_DEEMP, data0_2, 3, 3, 0), + UDA1341_ENUM("Mixer mode", CMD_MIXER, ext2, 0, 3, 0), + UDA1341_ENUM("Filter mode", CMD_FILTER, data0_2, 0, 3, 0), + + UDA1341_2REGS("Gain Input Amplifier Gain (channel 2)", CMD_IG, ext4, ext5, 0, 0, 3, 31, 0), +}; + +int __init snd_chip_uda1341_mixer_new(snd_card_t *card, struct l3_client **clnt) +{ + int idx, err; + + DEBUG_NAME(KERN_DEBUG "uda1341 mixer_new\n"); + + snd_assert(card != NULL, return -EINVAL); + + uda1341 = snd_magic_kcalloc(l3_client_t, 0, GFP_KERNEL); + if (uda1341 == NULL) + return -ENOMEM; + + if ((err = l3_attach_client(uda1341, "l3-bit-sa1100-gpio", "snd-uda1341"))) + return -ENODEV; + + for (idx = 0; idx < UDA1341_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_uda1341_controls[idx], uda1341))) < 0) + return err; + } + + *clnt = uda1341; + strcpy(card->mixername, "UDA1341TS Mixer"); + ((uda1341_t *)uda1341->driver_data)->card = card; + + snd_uda1341_proc_init(card, uda1341); + + return 0; +} + +void __init snd_chip_uda1341_mixer_del(snd_card_t *card) +{ + DEBUG_NAME(KERN_DEBUG "uda1341 mixer_del\n"); + + l3_detach_client(uda1341); + + snd_magic_kfree(uda1341); + uda1341 = NULL; +} + +/* }}} */ + +/* {{{ L3 operations */ + +static int uda1341_attach(struct l3_client *clnt) +{ + struct uda1341 *uda; + + DEBUG_NAME(KERN_DEBUG "uda1341 attach\n"); + + uda = snd_magic_kcalloc(uda1341_t, 0, GFP_KERNEL); + if (!uda) + return -ENOMEM; + + memset(uda, 0, sizeof(*uda)); + + /* init fixed parts of my copy of registers */ + uda->regs[stat0] = STAT0; + uda->regs[stat1] = STAT1; + + uda->regs[data0_0] = DATA0_0; + uda->regs[data0_1] = DATA0_1; + uda->regs[data0_2] = DATA0_2; + + uda->write = snd_uda1341_codec_write; + uda->read = snd_uda1341_codec_read; + + spin_lock_init(&uda->reg_lock); + + clnt->driver_data = uda; + + //l3_open(clnt); + return 0; +} + +static void uda1341_detach(struct l3_client *clnt) +{ + DEBUG_NAME(KERN_DEBUG "uda1341 detach\n"); + snd_magic_kfree(clnt->driver_data); +} + +static int +uda1341_command(struct l3_client *clnt, int cmd, void *arg) +{ + DEBUG_NAME(KERN_DEBUG "l3_command\n"); + + if (cmd != CMD_READ_REG) + return snd_uda1341_cfg_write(clnt, cmd, (int) arg, FLUSH); + + return snd_uda1341_codec_read(clnt, (int) arg); +} + +static int uda1341_open(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + + DEBUG_NAME(KERN_DEBUG "uda1341 open\n"); + + uda->active = 1; + + /* init default configuration */ + snd_uda1341_cfg_write(clnt, CMD_RESET, 0, REGS_ONLY); + snd_uda1341_cfg_write(clnt, CMD_FS, F256, FLUSH); // unknown state after reset + snd_uda1341_cfg_write(clnt, CMD_FORMAT, LSB16, FLUSH); // unknown state after reset + snd_uda1341_cfg_write(clnt, CMD_OGAIN, ON, FLUSH); // default off after reset + snd_uda1341_cfg_write(clnt, CMD_IGAIN, ON, FLUSH); // default off after reset + snd_uda1341_cfg_write(clnt, CMD_DAC, ON, FLUSH); // ??? default value after reset + snd_uda1341_cfg_write(clnt, CMD_ADC, ON, FLUSH); // ??? default value after reset + snd_uda1341_cfg_write(clnt, CMD_VOLUME, 20, FLUSH); // default 0dB after reset + snd_uda1341_cfg_write(clnt, CMD_BASS, 0, REGS_ONLY); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_TREBBLE, 0, REGS_ONLY); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_PEAK, AFTER, REGS_ONLY);// default value after reset + snd_uda1341_cfg_write(clnt, CMD_DEEMP, NONE, REGS_ONLY);// default value after reset + //at this moment should be QMUTED by h3600_audio_init + snd_uda1341_cfg_write(clnt, CMD_MUTE, OFF, REGS_ONLY); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_FILTER, MAX, FLUSH); // defaul flat after reset + snd_uda1341_cfg_write(clnt, CMD_CH1, 31, FLUSH); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_CH2, 4, FLUSH); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_MIC, 4, FLUSH); // default 0dB after reset + snd_uda1341_cfg_write(clnt, CMD_MIXER, MIXER, FLUSH); // default doub.dif.mode + snd_uda1341_cfg_write(clnt, CMD_AGC, OFF, FLUSH); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_IG, 0, FLUSH); // unknown state after reset + snd_uda1341_cfg_write(clnt, CMD_AGC_TIME, 0, FLUSH); // default value after reset + snd_uda1341_cfg_write(clnt, CMD_AGC_LEVEL, 0, FLUSH); // default value after reset + + return 0; +} + +static void uda1341_close(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + + DEBUG_NAME(KERN_DEBUG "uda1341 close\n"); + uda->active = 0; +} + +/* }}} */ + +/* {{{ Module and L3 initialization */ + +static struct l3_ops uda1341_ops = { + .open = uda1341_open, + .command = uda1341_command, + .close = uda1341_close, +}; + +static struct l3_driver uda1341_driver = { + .name = UDA1341_ALSA_NAME, + .attach_client = uda1341_attach, + .detach_client = uda1341_detach, + .ops = &uda1341_ops, + .owner = THIS_MODULE, +}; + +static int __init uda1341_init(void) +{ + return l3_add_driver(&uda1341_driver); +} + +static void __exit uda1341_exit(void) +{ + l3_del_driver(&uda1341_driver); +} + +module_init(uda1341_init); +module_exit(uda1341_exit); + +MODULE_AUTHOR("Tomas Kasparek "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Philips UDA1341 CODEC driver for ALSA"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{UDA1341,UDA1341TS}}"); + +EXPORT_SYMBOL(snd_chip_uda1341_mixer_new); +EXPORT_SYMBOL(snd_chip_uda1341_mixer_del); + +/* }}} */ + +/* + * Local variables: + * indent-tabs-mode: t + * End: + */ diff -urN linux-2.4.21-rc1.orig/sound/i2c/tea6330t.c linux/sound/i2c/tea6330t.c --- linux-2.4.21-rc1.orig/sound/i2c/tea6330t.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/i2c/tea6330t.c 2003-01-31 08:19:39.000000000 -0700 @@ -0,0 +1,373 @@ +/* + * Routines for control of the TEA6330T circuit via i2c bus + * Sound fader control circuit for car radios by Philips Semiconductors + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of the TEA6330T circuit via i2c bus"); +MODULE_LICENSE("GPL"); + +#define chip_t tea6330t_t + +#define TEA6330T_ADDR (0x80>>1) /* fixed address */ + +#define TEA6330T_SADDR_VOLUME_LEFT 0x00 /* volume left */ +#define TEA6330T_SADDR_VOLUME_RIGHT 0x01 /* volume right */ +#define TEA6330T_SADDR_BASS 0x02 /* bass control */ +#define TEA6330T_SADDR_TREBLE 0x03 /* treble control */ +#define TEA6330T_SADDR_FADER 0x04 /* fader control */ +#define TEA6330T_MFN 0x20 /* mute control for selected channels */ +#define TEA6330T_FCH 0x10 /* select fader channels - front or rear */ +#define TEA6330T_SADDR_AUDIO_SWITCH 0x05 /* audio switch */ +#define TEA6330T_GMU 0x80 /* mute control, general mute */ +#define TEA6330T_EQN 0x40 /* equalizer switchover (0=equalizer-on) */ + +int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer) +{ + int res; + + snd_i2c_lock(bus); + res = snd_i2c_probeaddr(bus, TEA6330T_ADDR); + snd_i2c_unlock(bus); + return res; +} + +#if 0 +static void snd_tea6330t_set(tea6330t_t *tea, + unsigned char addr, unsigned char value) +{ +#if 0 + printk("set - 0x%x/0x%x\n", addr, value); +#endif + snd_i2c_write(tea->bus, TEA6330T_ADDR, addr, value, 1); +} +#endif + +#define TEA6330T_MASTER_VOLUME(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_tea6330t_info_master_volume, \ + .get = snd_tea6330t_get_master_volume, .put = snd_tea6330t_put_master_volume } + +static int snd_tea6330t_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 43; + return 0; +} + +static int snd_tea6330t_get_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + snd_i2c_lock(tea->bus); + ucontrol->value.integer.value[0] = tea->mleft - 0x14; + ucontrol->value.integer.value[1] = tea->mright - 0x14; + snd_i2c_unlock(tea->bus); + return 0; +} + +static int snd_tea6330t_put_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, count, err; + unsigned char bytes[3]; + unsigned char val1, val2; + + val1 = (ucontrol->value.integer.value[0] % 44) + 0x14; + val2 = (ucontrol->value.integer.value[1] % 44) + 0x14; + snd_i2c_lock(tea->bus); + change = val1 != tea->mleft || val2 != tea->mright; + tea->mleft = val1; + tea->mright = val2; + count = 0; + if (tea->regs[TEA6330T_SADDR_VOLUME_LEFT] != 0) { + bytes[count++] = TEA6330T_SADDR_VOLUME_LEFT; + bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = tea->mleft; + } + if (tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] != 0) { + if (count == 0) + bytes[count++] = TEA6330T_SADDR_VOLUME_RIGHT; + bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = tea->mright; + } + if (count > 0) { + if ((err = snd_i2c_sendbytes(tea->device, bytes, count)) < 0) + change = err; + } + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_MASTER_SWITCH(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_tea6330t_info_master_switch, \ + .get = snd_tea6330t_get_master_switch, .put = snd_tea6330t_put_master_switch } + +static int snd_tea6330t_info_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_tea6330t_get_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + snd_i2c_lock(tea->bus); + ucontrol->value.integer.value[0] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1; + ucontrol->value.integer.value[1] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1; + snd_i2c_unlock(tea->bus); + return 0; +} + +static int snd_tea6330t_put_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[3]; + unsigned char oval1, oval2, val1, val2; + + val1 = ucontrol->value.integer.value[0] & 1; + val2 = ucontrol->value.integer.value[1] & 1; + snd_i2c_lock(tea->bus); + oval1 = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1; + oval2 = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1; + change = val1 != oval1 || val2 != oval2; + tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = val1 ? tea->mleft : 0; + tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = val2 ? tea->mright : 0; + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + bytes[1] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT]; + bytes[2] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT]; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 3)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_BASS(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_tea6330t_info_bass, \ + .get = snd_tea6330t_get_bass, .put = snd_tea6330t_put_bass } + +static int snd_tea6330t_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tea->max_bass; + return 0; +} + +static int snd_tea6330t_get_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tea->bass; + return 0; +} + +static int snd_tea6330t_put_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[2]; + unsigned char val1; + + val1 = ucontrol->value.integer.value[0] % (tea->max_bass + 1); + snd_i2c_lock(tea->bus); + tea->bass = val1; + val1 += tea->equalizer ? 7 : 3; + change = tea->regs[TEA6330T_SADDR_BASS] != val1; + bytes[0] = TEA6330T_SADDR_BASS; + bytes[1] = tea->regs[TEA6330T_SADDR_BASS] = val1; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_TREBLE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_tea6330t_info_treble, \ + .get = snd_tea6330t_get_treble, .put = snd_tea6330t_put_treble } + +static int snd_tea6330t_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tea->max_treble; + return 0; +} + +static int snd_tea6330t_get_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tea->treble; + return 0; +} + +static int snd_tea6330t_put_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[2]; + unsigned char val1; + + val1 = ucontrol->value.integer.value[0] % (tea->max_treble + 1); + snd_i2c_lock(tea->bus); + tea->treble = val1; + val1 += 3; + change = tea->regs[TEA6330T_SADDR_TREBLE] != val1; + bytes[0] = TEA6330T_SADDR_TREBLE; + bytes[1] = tea->regs[TEA6330T_SADDR_TREBLE] = val1; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_CONTROLS (sizeof(snd_tea6330t_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_tea6330t_controls[] = { +TEA6330T_MASTER_SWITCH("Master Playback Switch", 0), +TEA6330T_MASTER_VOLUME("Master Playback Volume", 0), +TEA6330T_BASS("Tone Control - Bass", 0), +TEA6330T_TREBLE("Tone Control - Treble", 0) +}; + +static void snd_tea6330_free(snd_i2c_device_t *device) +{ + tea6330t_t *tea = snd_magic_cast(tea6330t_t, device->private_data, return); + snd_magic_kfree(tea); +} + +int snd_tea6330t_update_mixer(snd_card_t * card, + snd_i2c_bus_t *bus, + int equalizer, int fader) +{ + snd_i2c_device_t *device; + tea6330t_t *tea; + snd_kcontrol_new_t *knew; + unsigned int idx; + int err = -ENOMEM; + u8 default_treble, default_bass; + unsigned char bytes[7]; + + tea = snd_magic_kcalloc(tea6330t_t, 0, GFP_KERNEL); + if (tea == NULL) + return -ENOMEM; + if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) { + snd_magic_kfree(tea); + return err; + } + tea->device = device; + tea->bus = bus; + tea->equalizer = equalizer; + tea->fader = fader; + device->private_data = tea; + device->private_free = snd_tea6330_free; + + snd_i2c_lock(bus); + + /* turn fader off and handle equalizer */ + tea->regs[TEA6330T_SADDR_FADER] = 0x3f; + tea->regs[TEA6330T_SADDR_AUDIO_SWITCH] = equalizer ? 0 : TEA6330T_EQN; + /* initialize mixer */ + if (!tea->equalizer) { + tea->max_bass = 9; + tea->max_treble = 8; + default_bass = 3 + 4; + tea->bass = 4; + default_treble = 3 + 4; + tea->treble = 4; + } else { + tea->max_bass = 5; + tea->max_treble = 0; + default_bass = 7 + 4; + tea->bass = 4; + default_treble = 3; + tea->treble = 0; + } + tea->mleft = tea->mright = 0x14; + tea->regs[TEA6330T_SADDR_BASS] = default_bass; + tea->regs[TEA6330T_SADDR_TREBLE] = default_treble; + + /* compose I2C message and put the hardware to initial state */ + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + for (idx = 0; idx < 6; idx++) + bytes[idx+1] = tea->regs[idx]; + if ((err = snd_i2c_sendbytes(device, bytes, 7)) < 0) + goto __error; + + strcat(card->mixername, ",TEA6330T"); + if ((err = snd_component_add(card, "TEA6330T")) < 0) + goto __error; + + for (idx = 0; idx < TEA6330T_CONTROLS; idx++) { + knew = &snd_tea6330t_controls[idx]; + if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble")) + continue; + if ((err = snd_ctl_add(card, snd_ctl_new1(knew, tea))) < 0) + goto __error; + } + + snd_i2c_unlock(bus); + return 0; + + __error: + snd_i2c_unlock(bus); + snd_i2c_device_free(device); + return err; +} + +EXPORT_SYMBOL(snd_tea6330t_detect); +EXPORT_SYMBOL(snd_tea6330t_update_mixer); + +/* + * INIT part + */ + +static int __init alsa_tea6330t_init(void) +{ + return 0; +} + +static void __exit alsa_tea6330t_exit(void) +{ +} + +module_init(alsa_tea6330t_init) +module_exit(alsa_tea6330t_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/Config.in linux/sound/isa/Config.in --- linux-2.4.21-rc1.orig/sound/isa/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/Config.in 2003-04-29 05:02:23.000000000 -0600 @@ -0,0 +1,36 @@ +# ALSA ISA drivers + +mainmenu_option next_comment +comment 'ISA devices' + +dep_tristate 'Analog Devices SoundPort AD1816A' CONFIG_SND_AD1816A $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Generic AD1848/CS4248 driver' CONFIG_SND_AD1848 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4231 driver' CONFIG_SND_CS4231 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4232 driver' CONFIG_SND_CS4232 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4236+ driver' CONFIG_SND_CS4236 $CONFIG_SND +dep_tristate 'Generic ESS ES968 driver' CONFIG_SND_ES968 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Generic ESS ES688/ES1688 driver' CONFIG_SND_ES1688 $CONFIG_SND +dep_tristate 'Generic ESS ES18xx driver' CONFIG_SND_ES18XX $CONFIG_SND +dep_tristate 'Gravis UltraSound Classic' CONFIG_SND_GUSCLASSIC $CONFIG_SND +dep_tristate 'Gravis UltraSound Extreme' CONFIG_SND_GUSEXTREME $CONFIG_SND +dep_tristate 'Gravis UltraSound MAX' CONFIG_SND_GUSMAX $CONFIG_SND +dep_tristate 'AMD InterWave, Gravis UltraSound PnP' CONFIG_SND_INTERWAVE $CONFIG_SND +dep_tristate 'AMD InterWave + TEA6330T (UltraSound 32-Pro)' CONFIG_SND_INTERWAVE_STB $CONFIG_SND +dep_tristate 'OPTi 82C92x - AD1848' CONFIG_SND_OPTI92X_AD1848 $CONFIG_SND +dep_tristate 'OPTi 82C92x - CS4231' CONFIG_SND_OPTI92X_CS4231 $CONFIG_SND +dep_tristate 'OPTi 82C93x' CONFIG_SND_OPTI93X $CONFIG_SND +dep_tristate 'Sound Blaster 1.0/2.0/Pro (8-bit)' CONFIG_SND_SB8 $CONFIG_SND +dep_tristate 'Sound Blaster 16 (PnP)' CONFIG_SND_SB16 $CONFIG_SND +dep_tristate 'Sound Blaster AWE (32,64) (PnP)' CONFIG_SND_SBAWE $CONFIG_SND +if [ "$CONFIG_SND_SB16" != "n" -o "$CONFIG_SND_SBAWE" != "n" ]; then + bool ' Sound Blaster 16/AWE CSP support' CONFIG_SND_SB16_CSP +fi +dep_tristate 'Turtle Beach Maui,Tropez,Tropez+ (Wavefront)' CONFIG_SND_WAVEFRONT $CONFIG_SND +dep_tristate 'Avance Logic ALS100/ALS120' CONFIG_SND_ALS100 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Aztech Systems AZT2320' CONFIG_SND_AZT2320 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'C-Media CMI8330' CONFIG_SND_CMI8330 $CONFIG_SND +dep_tristate 'Diamond Technologies DT-019X, Avance Logic ALS-007' CONFIG_SND_DT019X $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Yamaha OPL3-SA2/SA3' CONFIG_SND_OPL3SA2 $CONFIG_SND +dep_tristate 'Aztech Sound Galaxy' CONFIG_SND_SGALAXY $CONFIG_SND + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/isa/Makefile linux/sound/isa/Makefile --- linux-2.4.21-rc1.orig/sound/isa/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,52 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := isa.o + +subdir-y := ad1816a ad1848 cs423x es1688 gus opti9xx sb wavefront +subdir-m := $(subdir-y) + +list-multi := snd-als100.o snd-azt2320.o snd-cmi8330.o snd-dt019x.o \ + snd-es18xx.o snd-opl3sa2.o snd-sgalaxy.o + +snd-als100-objs := als100.o +snd-azt2320-objs := azt2320.o +snd-cmi8330-objs := cmi8330.o +snd-dt019x-objs := dt019x.o +snd-es18xx-objs := es18xx.o +snd-opl3sa2-objs := opl3sa2.o +snd-sgalaxy-objs := sgalaxy.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-als100.o +obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o +obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o +obj-$(CONFIG_SND_DT019X) += snd-dt019x.o +obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o +obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o +obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o + +include $(TOPDIR)/Rules.make + +snd-als100.o: $(snd-als100-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-als100-objs) + +snd-azt2320.o: $(snd-azt2320-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-azt2320-objs) + +snd-cmi8330.o: $(snd-cmi8330-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cmi8330-objs) + +snd-dt019x.o: $(snd-dt019x-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-dt019x-objs) + +snd-es18xx.o: $(snd-es18xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es18xx-objs) + +snd-opl3sa2.o: $(snd-opl3sa2-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3sa2-objs) + +snd-sgalaxy.o: $(snd-sgalaxy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sgalaxy-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/ad1816a/Makefile linux/sound/isa/ad1816a/Makefile --- linux-2.4.21-rc1.orig/sound/isa/ad1816a/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/ad1816a/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ad1816a.o + +list-multi := snd-ad1816a-lib.o snd-ad1816a.o + +export-objs := ad1816a_lib.o + +snd-ad1816a-lib-objs := ad1816a_lib.o +snd-ad1816a-objs := ad1816a.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o snd-ad1816a-lib.o + +include $(TOPDIR)/Rules.make + +snd-ad1816a-lib.o: $(snd-ad1816a-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-lib-objs) + +snd-ad1816a.o: $(snd-ad1816a-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/ad1816a/ad1816a.c linux/sound/isa/ad1816a/ad1816a.c --- linux-2.4.21-rc1.orig/sound/isa/ad1816a/ad1816a.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/ad1816a/ad1816a.c 2003-03-01 12:04:28.000000000 -0700 @@ -0,0 +1,398 @@ + +/* + card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. + Copyright (C) 2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t ad1816a_t + +#define PFX "ad1816a: " + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("AD1816A, AD1815"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Highscreen,Sound-Boostar 16 3D}," + "{Analog Devices,AD1815}," + "{Analog Devices,AD1816A}," + "{TerraTec,Base 64}," + "{TerraTec,AudioSystem EWS64S}," + "{Aztech/Newcom SC-16 3D}," + "{Shark Predator ISA}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for ad1816a driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); + +struct snd_card_ad1816a { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_ad1816a_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_ad1816a_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_ad1816a_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_AD1816A(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \ + ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \ + } + +static struct isapnp_card_id snd_ad1816a_pnpids[] __devinitdata = { + /* Highscreen Sound-Boostar 16 3D */ + ISAPNP_AD1816A('M','D','K',0x1605,'A','D','S',0x7180,0x7181), + /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */ + ISAPNP_AD1816A('L','W','C',0x1061,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1815 */ + ISAPNP_AD1816A('A','D','S',0x7150,'A','D','S',0x7150,0x7151), + /* Analog Devices AD1816A - added by Kenneth Platz */ + ISAPNP_AD1816A('A','D','S',0x7181,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Terratec Base 64 */ + ISAPNP_AD1816A('T','E','R',0x1411,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Terratec AudioSystem EWS64S */ + ISAPNP_AD1816A('T','E','R',0x1112,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */ + ISAPNP_AD1816A('A','Z','T',0x1022,'A','Z','T',0x1018,0x2002), + /* Shark Predator ISA - added by Ken Arromdee */ + ISAPNP_AD1816A('S','M','M',0x7180,'A','D','S',0x7180,0x7181), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_ad1816a_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-ad1816a" + + +#ifdef __ISAPNP__ +static int __init snd_card_ad1816a_isapnp(int dev, + struct snd_card_ad1816a *acard) +{ + const struct isapnp_card_id *id = snd_ad1816a_isapnp_id[dev]; + struct isapnp_card *card = snd_ad1816a_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], port[dev], 16); + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], fm_port[dev], 4); + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], + 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev], + 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + + if (pdev->activate(pdev) < 0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + port[dev] = pdev->resource[2].start; + fm_port[dev] = pdev->resource[1].start; + dma1[dev] = pdev->dma_resource[0].start; + dma2[dev] = pdev->dma_resource[1].start; + irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + mpu_port[dev] = -1; + acard->devmpu = NULL; + return 0; + } + + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], mpu_port[dev], + 2); + if (mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], mpu_irq[dev], + 1); + + if (pdev->activate(pdev) < 0) { + /* not fatal error */ + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + mpu_port[dev] = pdev->resource[0].start; + mpu_irq[dev] = pdev->irq_resource[0].start; + } + + return 0; +} + +static void snd_card_ad1816a_deactivate(struct snd_card_ad1816a *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_ad1816a_free(snd_card_t *card) +{ + struct snd_card_ad1816a *acard = (struct snd_card_ad1816a *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_ad1816a_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_ad1816a_probe(int dev) +{ + int error; + snd_card_t *card; + struct snd_card_ad1816a *acard; + ad1816a_t *chip; + opl3_t *opl3; + + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_ad1816a))) == NULL) + return -ENOMEM; + acard = (struct snd_card_ad1816a *)card->private_data; + card->private_free = snd_card_ad1816a_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_ad1816a_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_ad1816a_create(card, port[dev], + irq[dev], + dma1[dev], + dma2[dev], + &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_ad1816a_mixer(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpu_port[dev], 0, mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]); + } + + if (fm_port[dev] > 0) { + if (snd_opl3_create(card, + fm_port[dev], fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "AD1816A"); + strcpy(card->shortname, "ADI SoundPort AD1816A"); + sprintf(card->longname, "%s soundcard, SS at 0x%lx, irq %d, dma %d&%d", + card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_ad1816a_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_ad1816a_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_ad1816a_isapnp_cards[dev] = card; + snd_ad1816a_isapnp_id[dev] = id; + res = snd_card_ad1816a_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_ad1816a_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_ad1816a_pnpids, snd_ad1816a_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no AD1816A based soundcards found.\n"); +#endif /* MODULE */ + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_ad1816a_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_ad1816a_cards[dev]); +} + +module_init(alsa_card_ad1816a_init) +module_exit(alsa_card_ad1816a_exit) + +#ifndef MODULE + +/* format is: snd-ad1816a=enable,index,id,port, + mpu_port,fm_port,irq,mpu_irq, + dma1,dma2 */ + +static int __init alsa_card_ad1816a_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ad1816a=", alsa_card_ad1816a_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/ad1816a/ad1816a_lib.c linux/sound/isa/ad1816a/ad1816a_lib.c --- linux-2.4.21-rc1.orig/sound/isa/ad1816a/ad1816a_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/ad1816a/ad1816a_lib.c 2003-01-31 08:19:45.000000000 -0700 @@ -0,0 +1,967 @@ + +/* + ad1816a.c - lowlevel code for Analog Devices AD1816A chip. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip"); +MODULE_LICENSE("GPL"); + +#define chip_t ad1816a_t + +static inline int snd_ad1816a_busy_wait(ad1816a_t *chip) +{ + int timeout; + + for (timeout = 1000; timeout-- > 0; udelay(10)) + if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) + return 0; + + snd_printk("chip busy.\n"); + return -EBUSY; +} + +inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg) +{ + snd_ad1816a_busy_wait(chip); + return inb(AD1816A_REG(reg)); +} + +inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg, + unsigned char value) +{ + snd_ad1816a_busy_wait(chip); + outb(value, AD1816A_REG(reg)); +} + +inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + snd_ad1816a_out(chip, reg, + (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); +} + +static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg) +{ + snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); + return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | + (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); +} + +static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg, + unsigned short value) +{ + snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); + snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); + snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); +} + +static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg, + unsigned short mask, unsigned short value) +{ + snd_ad1816a_write(chip, reg, + (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); +} + + +static unsigned char snd_ad1816a_get_format(ad1816a_t *chip, + unsigned int format, int channels) +{ + unsigned char retval = AD1816A_FMT_LINEAR_8; + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + retval = AD1816A_FMT_ULAW_8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + retval = AD1816A_FMT_ALAW_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + retval = AD1816A_FMT_LINEAR_16_LIT; + break; + case SNDRV_PCM_FORMAT_S16_BE: + retval = AD1816A_FMT_LINEAR_16_BIG; + } + return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; +} + +static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (chip->mode & mode) { + spin_unlock_irqrestore(&chip->lock, flags); + return -EAGAIN; + } + + switch ((mode &= AD1816A_MODE_OPEN)) { + case AD1816A_MODE_PLAYBACK: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_PLAYBACK_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); + break; + case AD1816A_MODE_CAPTURE: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_CAPTURE_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); + break; + case AD1816A_MODE_TIMER: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_TIMER_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_IRQ_ENABLE, 0xffff); + } + chip->mode |= mode; + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + switch ((mode &= AD1816A_MODE_OPEN)) { + case AD1816A_MODE_PLAYBACK: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_PLAYBACK_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); + break; + case AD1816A_MODE_CAPTURE: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_CAPTURE_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); + break; + case AD1816A_MODE_TIMER: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_TIMER_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_IRQ_ENABLE, 0x0000); + } + if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN)) + chip->mode = 0; + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what, + int channel, int cmd) +{ + int error = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&chip->lock); + cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; + if (what & AD1816A_PLAYBACK_ENABLE) + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE, cmd); + if (what & AD1816A_CAPTURE_ENABLE) + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE, cmd); + spin_unlock(&chip->lock); + break; + default: + snd_printk("invalid trigger mode 0x%x.\n", what); + error = -EINVAL; + } + + return error; +} + +static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, + SNDRV_PCM_STREAM_PLAYBACK, cmd); +} + +static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, + SNDRV_PCM_STREAM_CAPTURE, cmd); +} + +static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size; + + spin_lock_irqsave(&chip->lock, flags); + + chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); + + snd_dma_program(chip->dma1, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_FMT_ALL | AD1816A_FMT_STEREO, + snd_ad1816a_get_format(chip, runtime->format, + runtime->channels)); + + snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, + snd_pcm_lib_period_bytes(substream) / 4 - 1); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size; + + spin_lock_irqsave(&chip->lock, flags); + + chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); + + snd_dma_program(chip->dma2, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_FMT_ALL | AD1816A_FMT_STEREO, + snd_ad1816a_get_format(chip, runtime->format, + runtime->channels)); + + snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, + snd_pcm_lib_period_bytes(substream) / 4 - 1); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + + +static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(chip->mode & AD1816A_MODE_PLAYBACK)) + return 0; + ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(chip->mode & AD1816A_MODE_CAPTURE)) + return 0; + ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + + +static void snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, dev_id, return); + unsigned char status; + + spin_lock(&chip->lock); + status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); + spin_unlock(&chip->lock); + + if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) + snd_pcm_period_elapsed(chip->playback_substream); + + if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) + snd_pcm_period_elapsed(chip->capture_substream); + + if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + + spin_lock(&chip->lock); + snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); + spin_unlock(&chip->lock); +} + + +static snd_pcm_hardware_t snd_ad1816a_playback = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 55200, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ad1816a_capture = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 55200, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ad1816a_timer_close(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_timer_chip(timer); + snd_ad1816a_close(chip, AD1816A_MODE_TIMER); + return 0; +} + +static int snd_ad1816a_timer_open(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_timer_chip(timer); + snd_ad1816a_open(chip, AD1816A_MODE_TIMER); + return 0; +} + +static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer) +{ + snd_assert(timer != NULL, return 0); + + return 10000; +} + +static int snd_ad1816a_timer_start(snd_timer_t *timer) +{ + unsigned short bits; + unsigned long flags; + ad1816a_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->lock, flags); + bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); + + if (!(bits & AD1816A_TIMER_ENABLE)) { + snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, + timer->sticks & 0xffff); + + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_ENABLE, 0xffff); + } + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_timer_stop(snd_timer_t *timer) +{ + unsigned long flags; + ad1816a_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->lock, flags); + + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_ENABLE, 0x0000); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static struct _snd_timer_hardware snd_ad1816a_timer_table = { + .flags = SNDRV_TIMER_HW_AUTO, + .resolution = 10000, + .ticks = 65535, + .open = snd_ad1816a_timer_open, + .close = snd_ad1816a_timer_close, + .c_resolution = snd_ad1816a_timer_resolution, + .start = snd_ad1816a_timer_start, + .stop = snd_ad1816a_timer_stop, +}; + + +static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int error; + + if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) + return error; + snd_pcm_set_sync(substream); + runtime->hw = snd_ad1816a_playback; + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + chip->playback_substream = substream; + return 0; +} + +static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int error; + + if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) + return error; + snd_pcm_set_sync(substream); + runtime->hw = snd_ad1816a_capture; + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + chip->capture_substream = substream; + return 0; +} + +static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); + return 0; +} + +static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); + return 0; +} + + +static void snd_ad1816a_init(ad1816a_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); + snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); + snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, + AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); + snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); + snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); + + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_ad1816a_probe(ad1816a_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { + case 0: + chip->hardware = AD1816A_HW_AD1815; + break; + case 1: + chip->hardware = AD1816A_HW_AD18MAX10; + break; + case 3: + chip->hardware = AD1816A_HW_AD1816A; + break; + default: + chip->hardware = AD1816A_HW_AUTO; + } + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_free(ad1816a_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma1 >= 0) { + snd_dma_disable(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + snd_dma_disable(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_ad1816a_dev_free(snd_device_t *device) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, device->device_data, return -ENXIO); + return snd_ad1816a_free(chip); +} + +static const char *snd_ad1816a_chip_id(ad1816a_t *chip) +{ + switch (chip->hardware) { + case AD1816A_HW_AD1816A: return "AD1816A"; + case AD1816A_HW_AD1815: return "AD1815"; + case AD1816A_HW_AD18MAX10: return "AD18max10"; + default: + snd_printk("Unknown chip version %d:%d.\n", + chip->version, chip->hardware); + return "AD1816A - unknown"; + } +} + +int snd_ad1816a_create(snd_card_t *card, + unsigned long port, int irq, int dma1, int dma2, + ad1816a_t **rchip) +{ + static snd_device_ops_t ops = { + .dev_free = snd_ad1816a_dev_free, + }; + int error; + ad1816a_t *chip; + + *rchip = NULL; + + chip = snd_magic_kcalloc(ad1816a_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + + if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) { + snd_ad1816a_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma1, "AD1816A - 1")) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->dma1 = dma1; + if (request_dma(dma2, "AD1816A - 2")) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->dma2 = dma2; + + chip->card = card; + chip->port = port; + spin_lock_init(&chip->lock); + + if ((error = snd_ad1816a_probe(chip))) { + snd_ad1816a_free(chip); + return error; + } + + snd_ad1816a_init(chip); + + /* Register device */ + if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ad1816a_free(chip); + return error; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_ad1816a_playback_ops = { + .open = snd_ad1816a_playback_open, + .close = snd_ad1816a_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ad1816a_hw_params, + .hw_free = snd_ad1816a_hw_free, + .prepare = snd_ad1816a_playback_prepare, + .trigger = snd_ad1816a_playback_trigger, + .pointer = snd_ad1816a_playback_pointer, +}; + +static snd_pcm_ops_t snd_ad1816a_capture_ops = { + .open = snd_ad1816a_capture_open, + .close = snd_ad1816a_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ad1816a_hw_params, + .hw_free = snd_ad1816a_hw_free, + .prepare = snd_ad1816a_capture_prepare, + .trigger = snd_ad1816a_capture_trigger, + .pointer = snd_ad1816a_capture_pointer, +}; + +static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm) +{ + int error; + snd_pcm_t *pcm; + + if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm))) + return error; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_ad1816a_pcm_free; + pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; + + strcpy(pcm->name, snd_ad1816a_chip_id(chip)); + snd_ad1816a_init(chip); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_ad1816a_timer_free(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int error; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0) + return error; + strcpy(timer->name, snd_ad1816a_chip_id(chip)); + timer->private_data = chip; + timer->private_free = snd_ad1816a_timer_free; + chip->timer = timer; + timer->hw = snd_ad1816a_timer_table; + if (rtimer) + *rtimer = timer; + return 0; +} + +/* + * + */ + +static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Line", "Mix", "CD", "Synth", "Video", + "Mic", "Phone", + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 7; + if (uinfo->value.enumerated.item > 6) + uinfo->value.enumerated.item = 6; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); + spin_unlock_irqrestore(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = (val >> 12) & 7; + ucontrol->value.enumerated.item[1] = (val >> 4) & 7; + return 0; +} + +static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short val; + int change; + + if (ucontrol->value.enumerated.item[0] > 6 || + ucontrol->value.enumerated.item[1] > 6) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] << 12) | + (ucontrol->value.enumerated.item[1] << 4); + spin_lock_irqsave(&chip->lock, flags); + change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; + snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ + .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short old_val, val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->lock, flags); + old_val = snd_ad1816a_read(chip, reg); + val = (old_val & ~(mask << shift)) | val; + change = val != old_val; + snd_ad1816a_write(chip, reg, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ + .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ + .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_ad1816a_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short old_val, val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->lock, flags); + old_val = snd_ad1816a_read(chip, reg); + val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != old_val; + snd_ad1816a_write(chip, reg, val1); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_CONTROLS (sizeof(snd_ad1816a_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ad1816a_controls[] = { +AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), +AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), +AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), +AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1), +AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), +AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), +AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), +AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), +AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), +AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_ad1816a_info_mux, + .get = snd_ad1816a_get_mux, + .put = snd_ad1816a_put_mux, +}, +AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), +AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), +AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), +AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), +}; + +int snd_ad1816a_mixer(ad1816a_t *chip) +{ + snd_card_t *card; + unsigned int idx; + int err; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_ad1816a_chip_id(chip)); + + for (idx = 0; idx < AD1816A_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_ad1816a_create); +EXPORT_SYMBOL(snd_ad1816a_pcm); +EXPORT_SYMBOL(snd_ad1816a_mixer); + +static int __init alsa_ad1816a_init(void) +{ + return 0; +} + +static void __exit alsa_ad1816a_exit(void) +{ +} + +module_init(alsa_ad1816a_init) +module_exit(alsa_ad1816a_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/ad1848/Makefile linux/sound/isa/ad1848/Makefile --- linux-2.4.21-rc1.orig/sound/isa/ad1848/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/ad1848/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ad1848.o + +list-multi := snd-ad1848-lib.o snd-ad1848.o + +export-objs := ad1848_lib.o + +snd-ad1848-lib-objs := ad1848_lib.o +snd-ad1848-objs := ad1848.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o +obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o +obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-ad1848-lib.o: $(snd-ad1848-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-lib-objs) + +snd-ad1848.o: $(snd-ad1848-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/ad1848/ad1848.c linux/sound/isa/ad1848/ad1848.c --- linux-2.4.21-rc1.orig/sound/isa/ad1848/ad1848.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/ad1848/ad1848.c 2002-10-21 12:28:21.000000000 -0600 @@ -0,0 +1,178 @@ +/* + * Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha) + * Copyright (c) by Tugrul Galatali , + * Jaroslav Kysela + * Based on card-4232.c by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t ad1848_t + +MODULE_AUTHOR("Tugrul Galatali , Jaroslav Kysela "); +MODULE_DESCRIPTION("AD1848/AD1847/CS4248"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Analog Devices,AD1848}," + "{Analog Devices,AD1847}," + "{Crystal Semiconductors,CS4248}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for AD1848 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for AD1848 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable AD1848 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for AD1848 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for AD1848 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for AD1848 driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); + +static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_ad1848_probe(int dev) +{ + snd_card_t *card; + ad1848_t *chip; + snd_pcm_t *pcm; + int err; + + if (port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify port\n"); + return -EINVAL; + } + if (irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify irq\n"); + return -EINVAL; + } + if (dma1[dev] == SNDRV_AUTO_DMA) { + snd_printk("specify dma1\n"); + return -EINVAL; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ad1848_create(card, port[dev], + irq[dev], + dma1[dev], + AD1848_HW_DETECT, + &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ad1848_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + strcpy(card->driver, "AD1848"); + strcpy(card->shortname, pcm->name); + + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + pcm->name, chip->port, irq[dev], dma1[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_ad1848_cards[dev] = card; + return 0; +} + +static int __init alsa_card_ad1848_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) + if (snd_card_ad1848_probe(dev) >= 0) + cards++; + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "AD1848 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_ad1848_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_ad1848_cards[idx]); +} + +module_init(alsa_card_ad1848_init) +module_exit(alsa_card_ad1848_exit) + +#ifndef MODULE + +/* format is: snd-ad1848=enable,index,id,port, + irq,dma1 */ + +static int __init alsa_card_ad1848_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ad1848=", alsa_card_ad1848_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/ad1848/ad1848_lib.c linux/sound/isa/ad1848/ad1848_lib.c --- linux-2.4.21-rc1.orig/sound/isa/ad1848/ad1848_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/ad1848/ad1848_lib.c 2003-01-31 08:19:45.000000000 -0700 @@ -0,0 +1,1224 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of AD1848/AD1847/CS4248 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MAIN_OBJECT_FILE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248"); +MODULE_LICENSE("GPL"); + +#define chip_t ad1848_t + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | AD1848_XTAL2, + /* 6620 */ 0x0E | AD1848_XTAL2, + /* 8000 */ 0x00 | AD1848_XTAL1, + /* 9600 */ 0x0E | AD1848_XTAL1, + /* 11025 */ 0x02 | AD1848_XTAL2, + /* 16000 */ 0x02 | AD1848_XTAL1, + /* 18900 */ 0x04 | AD1848_XTAL2, + /* 22050 */ 0x06 | AD1848_XTAL2, + /* 27042 */ 0x04 | AD1848_XTAL1, + /* 32000 */ 0x06 | AD1848_XTAL1, + /* 33075 */ 0x0C | AD1848_XTAL2, + /* 37800 */ 0x08 | AD1848_XTAL2, + /* 44100 */ 0x0A | AD1848_XTAL2, + /* 48000 */ 0x0C | AD1848_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = 14, + .list = rates, + .mask = 0, +}; + +static unsigned char snd_ad1848_original_image[16] = +{ + 0x00, /* 00 - lic */ + 0x00, /* 01 - ric */ + 0x9f, /* 02 - la1ic */ + 0x9f, /* 03 - ra1ic */ + 0x9f, /* 04 - la2ic */ + 0x9f, /* 05 - ra2ic */ + 0xbf, /* 06 - loc */ + 0xbf, /* 07 - roc */ + 0x20, /* 08 - dfr */ + AD1848_AUTOCALIB, /* 09 - ic */ + 0x00, /* 0a - pc */ + 0x00, /* 0b - ti */ + 0x00, /* 0c - mi */ + 0x00, /* 0d - lbc */ + 0x00, /* 0e - dru */ + 0x00, /* 0f - drl */ +}; + +/* + * Basic I/O functions + */ + +void snd_ad1848_out(ad1848_t *chip, + unsigned char reg, + unsigned char value) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + outb(chip->image[reg] = value, AD1848P(chip, REG)); + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +void snd_ad1848_dout(ad1848_t *chip, + unsigned char reg, + unsigned char value) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + outb(value, AD1848P(chip, REG)); + mb(); +} + +unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("auto calibration time out - reg = 0x%x\n", reg); +#endif + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + mb(); + return inb(AD1848P(chip, REG)); +} + +#ifdef CONFIG_SND_DEBUG + +void snd_ad1848_debug(ad1848_t *chip) +{ + printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL))); + printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00)); + printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08)); + printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01)); + printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09)); + printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02)); + printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a)); + printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03)); + printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b)); + printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04)); + printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c)); + printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05)); + printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d)); + printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06)); + printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e)); + printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07)); + printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f)); +} + +#endif + +/* + * AD1848 detection / MCE routines + */ + +void snd_ad1848_mce_up(ad1848_t *chip) +{ + unsigned long flags; + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + chip->mce_bit |= AD1848_MCE; + timeout = inb(AD1848P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & AD1848_MCE)) + outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +void snd_ad1848_mce_down(ad1848_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (timeout = 5; timeout > 0; timeout--) + inb(AD1848P(chip, REGSEL)); + /* end of cleanup sequence */ + for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL)); +#endif + chip->mce_bit &= ~AD1848_MCE; + timeout = inb(AD1848P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & AD1848_MCE) == 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--); + if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) { + snd_printd("mce_down - auto calibration time out (1)\n"); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + time = HZ / 4; + while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + time = HZ / 10; + while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (3)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL))); +#endif +} + +static unsigned int snd_ad1848_get_count(unsigned char format, + unsigned int size) +{ + switch (format & 0xe0) { + case AD1848_LINEAR_16: + size >>= 1; + break; + } + if (format & AD1848_STEREO) + size >>= 1; + return size; +} + +static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what, + int channel, int cmd) +{ + int result = 0; + +#if 0 + printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS))); +#endif + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->image[AD1848_IFACE_CTRL] & what) { + spin_unlock(&chip->reg_lock); + return 0; + } + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what); + chip->mode |= AD1848_MODE_RUNNING; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->image[AD1848_IFACE_CTRL] & what)) { + spin_unlock(&chip->reg_lock); + return 0; + } + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what); + chip->mode &= ~AD1848_MODE_RUNNING; + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_ad1848_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + snd_BUG(); + return freq_bits[13]; +} + +static int snd_ad1848_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static unsigned char snd_ad1848_get_format(int format, int channels) +{ + unsigned char rformat; + + rformat = AD1848_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_A_LAW: rformat = AD1848_ALAW_8; break; + case SNDRV_PCM_FORMAT_MU_LAW: rformat = AD1848_ULAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = AD1848_LINEAR_16; break; + } + if (channels > 1) + rformat |= AD1848_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + if (!mute) { + snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]); + } + snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]); + snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]); + snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]); + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params) +{ + if (hw_params == NULL) { + chip->image[AD1848_DATA_FORMAT] = 0x20; + } else { + chip->image[AD1848_DATA_FORMAT] = + snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) | + snd_ad1848_get_rate(params_rate(hw_params)); + } + // snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]); +} + +static int snd_ad1848_open(ad1848_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if (chip->mode & AD1848_MODE_OPEN) { + up(&chip->open_mutex); + return -EAGAIN; + } + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (1)\n"); +#endif + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | + AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO | + AD1848_CALIB_MODE); + chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB; + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (2)\n"); +#endif + + snd_ad1848_set_data_format(chip, NULL); + + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (3)\n"); +#endif + + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE; + snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + + return 0; +} + +static void snd_ad1848_close(ad1848_t *chip) +{ + unsigned long flags; + + down(&chip->open_mutex); + if (!chip->mode) { + up(&chip->open_mutex); + return; + } + /* disable IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE; + snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* now disable capture & playback */ + + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | + AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + + /* clear IRQ again */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * ok.. exported functions.. + */ + +static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd); +} + +static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd); +} + +static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + snd_ad1848_calibrate_mute(chip, 1); + snd_ad1848_set_data_format(chip, hw_params); + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + snd_ad1848_calibrate_mute(chip, 0); + return 0; +} + +static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO); + snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); + snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + snd_ad1848_calibrate_mute(chip, 1); + snd_ad1848_set_data_format(chip, hw_params); + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + snd_ad1848_calibrate_mute(chip, 0); + return 0; +} + +static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); + snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); + snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, dev_id, return); + + if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream && + (chip->mode & AD1848_MODE_RUNNING)) + snd_pcm_period_elapsed(chip->playback_substream); + if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream && + (chip->mode & AD1848_MODE_RUNNING)) + snd_pcm_period_elapsed(chip->capture_substream); + outb(0, AD1848P(chip, STATUS)); /* clear global interrupt bit */ +} + +static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE)) + return 0; + ptr = snd_dma_pointer(chip->dma, chip->dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE)) + return 0; + ptr = snd_dma_pointer(chip->dma, chip->dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_ad1848_probe(ad1848_t * chip) +{ + unsigned long flags; + int i, id, rev, ad1847; + unsigned char *ptr; + +#if 0 + snd_ad1848_debug(chip); +#endif + id = ad1847 = 0; + for (i = 0; i < 1000; i++) { + mb(); + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + udelay(500); + else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); + snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa); + snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45); + rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT); + if (rev == 0x65) { + id = 1; + ad1847 = 1; + break; + } + if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) { + id = 1; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + } + if (id != 1) + return -ENODEV; /* no valid device found */ + if (chip->hardware == AD1848_HW_DETECT) { + if (ad1847) { + chip->hardware = AD1848_HW_AD1847; + } else { + chip->hardware = AD1848_HW_AD1848; + rev = snd_ad1848_in(chip, AD1848_MISC_INFO); + if (rev & 0x80) { + chip->hardware = AD1848_HW_CS4248; + } else if ((rev & 0x0f) == 0x0a) { + snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40); + for (i = 0; i < 16; ++i) { + if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) { + chip->hardware = AD1848_HW_CMI8330; + break; + } + } + snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); + } + } + } + spin_lock_irqsave(&chip->reg_lock, flags); + inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */ + outb(0, AD1848P(chip, STATUS)); + mb(); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->image[AD1848_MISC_INFO] = 0x00; + chip->image[AD1848_IFACE_CTRL] = + (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA; + ptr = (unsigned char *) &chip->image; + snd_ad1848_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */ + snd_ad1848_out(chip, i, *ptr++); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_up(chip); + snd_ad1848_mce_down(chip); + return 0; /* all things are ok.. */ +} + +/* + + */ + +static snd_pcm_hardware_t snd_ad1848_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ad1848_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + + */ + +static int snd_ad1848_playback_open(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0) + return err; + chip->playback_substream = substream; + runtime->hw = snd_ad1848_playback; + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ad1848_capture_open(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0) + return err; + chip->capture_substream = substream; + runtime->hw = snd_ad1848_capture; + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ad1848_playback_close(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + + chip->mode &= ~AD1848_MODE_PLAY; + chip->playback_substream = NULL; + snd_ad1848_close(chip); + return 0; +} + +static int snd_ad1848_capture_close(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + + chip->mode &= ~AD1848_MODE_CAPTURE; + chip->capture_substream = NULL; + snd_ad1848_close(chip); + return 0; +} + +static int snd_ad1848_free(ad1848_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma >= 0) { + snd_dma_disable(chip->dma); + free_dma(chip->dma); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_ad1848_dev_free(snd_device_t *device) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, device->device_data, return -ENXIO); + return snd_ad1848_free(chip); +} + +static const char *snd_ad1848_chip_id(ad1848_t *chip) +{ + switch (chip->hardware) { + case AD1848_HW_AD1847: return "AD1847"; + case AD1848_HW_AD1848: return "AD1848"; + case AD1848_HW_CS4248: return "CS4248"; + case AD1848_HW_CMI8330: return "CMI8330/C3D"; + default: return "???"; + } +} + +int snd_ad1848_create(snd_card_t * card, + unsigned long port, + int irq, int dma, + unsigned short hardware, + ad1848_t ** rchip) +{ + static snd_device_ops_t ops = { + .dev_free = snd_ad1848_dev_free, + }; + ad1848_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(ad1848_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->port = port; + chip->irq = -1; + chip->dma = -1; + chip->hardware = hardware; + memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image)); + + if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) { + snd_ad1848_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) { + snd_ad1848_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma, "AD1848")) { + snd_ad1848_free(chip); + return -EBUSY; + } + chip->dma = dma; + + if (snd_ad1848_probe(chip) < 0) { + snd_ad1848_free(chip); + return -ENODEV; + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ad1848_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_ad1848_playback_ops = { + .open = snd_ad1848_playback_open, + .close = snd_ad1848_playback_close, + .ioctl = snd_ad1848_ioctl, + .hw_params = snd_ad1848_playback_hw_params, + .hw_free = snd_ad1848_playback_hw_free, + .prepare = snd_ad1848_playback_prepare, + .trigger = snd_ad1848_playback_trigger, + .pointer = snd_ad1848_playback_pointer, +}; + +static snd_pcm_ops_t snd_ad1848_capture_ops = { + .open = snd_ad1848_capture_open, + .close = snd_ad1848_capture_close, + .ioctl = snd_ad1848_ioctl, + .hw_params = snd_ad1848_capture_hw_params, + .hw_free = snd_ad1848_capture_hw_free, + .prepare = snd_ad1848_capture_prepare, + .trigger = snd_ad1848_capture_trigger, + .pointer = snd_ad1848_capture_pointer, +}; + +static void snd_ad1848_pcm_free(snd_pcm_t *pcm) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops); + + pcm->private_free = snd_ad1848_pcm_free; + pcm->private_data = chip; + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + strcpy(pcm->name, snd_ad1848_chip_id(chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction) +{ + return direction == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_ad1848_playback_ops : &snd_ad1848_capture_ops; +} + +/* + * MIXER part + */ + +static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line", "Aux", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->reg_lock, flags); + left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left; + right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right; + change = left != chip->image[AD1848_LEFT_INPUT] || + right != chip->image[AD1848_RIGHT_INPUT]; + snd_ad1848_out(chip, AD1848_LEFT_INPUT, left); + snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_ad1848_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_ad1848_out(chip, left_reg, val1); + snd_ad1848_out(chip, right_reg, val2); + } else { + val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != chip->image[left_reg]; + snd_ad1848_out(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +/* + */ +int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, unsigned long value) +{ + static snd_kcontrol_new_t newctls[] = { + [AD1848_MIX_SINGLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ad1848_info_single, + .get = snd_ad1848_get_single, + .put = snd_ad1848_put_single, + }, + [AD1848_MIX_DOUBLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ad1848_info_double, + .get = snd_ad1848_get_double, + .put = snd_ad1848_put_double, + }, + [AD1848_MIX_CAPTURE] = { + .info = snd_ad1848_info_mux, + .get = snd_ad1848_get_mux, + .put = snd_ad1848_put_mux, + }, + }; + snd_kcontrol_t *ctl; + int err; + + ctl = snd_ctl_new1(&newctls[type], chip); + if (! ctl) + return -ENOMEM; + strncpy(ctl->id.name, name, sizeof(ctl->id.name)-1); + ctl->id.index = index; + ctl->private_value = value; + if ((err = snd_ctl_add(chip->card, ctl)) < 0) { + snd_ctl_free_one(ctl); + return err; + } + return 0; +} + + +static struct ad1848_mix_elem snd_ad1848_controls[] = { +AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), +AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), +AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), +{ + .name = "Capture Source", + .type = AD1848_MIX_CAPTURE, +}, +AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), +AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) +}; + +int snd_ad1848_mixer(ad1848_t *chip) +{ + snd_card_t *card; + snd_pcm_t *pcm; + unsigned int idx; + int err; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + pcm = chip->pcm; + card = chip->card; + + strcpy(card->mixername, pcm->name); + + for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) + if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0) + return err; + + return 0; +} + +EXPORT_SYMBOL(snd_ad1848_in); +EXPORT_SYMBOL(snd_ad1848_out); +EXPORT_SYMBOL(snd_ad1848_dout); +EXPORT_SYMBOL(snd_ad1848_mce_up); +EXPORT_SYMBOL(snd_ad1848_mce_down); +EXPORT_SYMBOL(snd_ad1848_interrupt); +EXPORT_SYMBOL(snd_ad1848_create); +EXPORT_SYMBOL(snd_ad1848_pcm); +EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); +EXPORT_SYMBOL(snd_ad1848_mixer); +EXPORT_SYMBOL(snd_ad1848_add_ctl); + +/* + * INIT part + */ + +static int __init alsa_ad1848_init(void) +{ + return 0; +} + +static void __exit alsa_ad1848_exit(void) +{ +} + +module_init(alsa_ad1848_init) +module_exit(alsa_ad1848_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/als100.c linux/sound/isa/als100.c --- linux-2.4.21-rc1.orig/sound/isa/als100.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/als100.c 2003-03-01 12:04:28.000000000 -0700 @@ -0,0 +1,420 @@ + +/* + card-als100.c - driver for Avance Logic ALS100 based soundcards. + Copyright (C) 1999-2000 by Massimo Piccioni + + Thanks to Pierfrancesco 'qM2' Passerini. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t sb_t + +#define PFX "als100: " + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Avance Logic ALS1X0"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Avance Logic,ALS100 - PRO16PNP}," + "{Avance Logic,ALS110}," + "{Avance Logic,ALS120}," + "{Avance Logic,ALS200}," + "{3D Melody,MF1000}," + "{Digimate,3D Sound}," + "{Avance Logic,ALS120}," + "{RTL,RTL3000}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for als100 based soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for als100 based soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable als100 based soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for als100 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for als100 driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for als100 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); +MODULE_PARM(dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver."); +MODULE_PARM_SYNTAX(dma16, SNDRV_DMA16_DESC); + +struct snd_card_als100 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; + struct isapnp_dev *devopl; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_als100_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_als100_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_als100_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_ALS100(_va, _vb, _vc, _device, _audio, _mpu401, _opl) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID('@', '@', '@', _audio), \ + ISAPNP_DEVICE_ID('@', 'X', '@', _mpu401), \ + ISAPNP_DEVICE_ID('@', 'H', '@', _opl) } \ + } + +static struct isapnp_card_id snd_als100_pnpids[] __devinitdata = { + /* ALS100 - PRO16PNP */ + ISAPNP_ALS100('A','L','S',0x0001,0x0001,0x0001,0x0001), + /* ALS110 - MF1000 - Digimate 3D Sound */ + ISAPNP_ALS100('A','L','S',0x0110,0x1001,0x1001,0x1001), + /* ALS120 */ + ISAPNP_ALS100('A','L','S',0x0120,0x2001,0x2001,0x2001), + /* ALS200 */ + ISAPNP_ALS100('A','L','S',0x0200,0x0020,0x0020,0x0001), + /* RTL3000 */ + ISAPNP_ALS100('R','T','L',0x3000,0x2001,0x2001,0x2001), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_als100_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-als100" + + +#ifdef __ISAPNP__ +static int __init snd_card_als100_isapnp(int dev, struct snd_card_als100 *acard) +{ + const struct isapnp_card_id *id = snd_als100_isapnp_id[dev]; + struct isapnp_card *card = snd_als100_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->devopl->active) { + acard->dev = acard->devmpu = acard->devopl = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + if (dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma8[dev], + 1); + if (dma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma16[dev], + 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + port[dev] = pdev->resource[0].start; + dma8[dev] = pdev->dma_resource[1].start; + dma16[dev] = pdev->dma_resource[0].start; + irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev)<0) { + mpu_port[dev] = -1; + return 0; + } + + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], mpu_port[dev], + 2); + if (mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], mpu_irq[dev], + 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + mpu_port[dev] = pdev->resource[0].start; + mpu_irq[dev] = pdev->irq_resource[0].start; + } + + pdev = acard->devopl; + if (pdev == NULL || pdev->prepare(pdev)<0) { + fm_port[dev] = -1; + return 0; + } + + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], fm_port[dev], 4); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "OPL isapnp configure failure\n"); + fm_port[dev] = -1; + acard->devopl = NULL; + } else { + fm_port[dev] = pdev->resource[0].start; + } + + return 0; +} + +static void snd_card_als100_deactivate(struct snd_card_als100 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } + if (acard->devopl) { + acard->devopl->deactivate(acard->devopl); + acard->devopl = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_als100_free(snd_card_t *card) +{ + struct snd_card_als100 *acard = (struct snd_card_als100 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_als100_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_als100_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_als100 *acard; + opl3_t *opl3; + + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_als100))) == NULL) + return -ENOMEM; + acard = (struct snd_card_als100 *)card->private_data; + card->private_free = snd_card_als100_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_als100_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + printk(KERN_ERR PFX "you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, port[dev], + irq[dev], + snd_sb16dsp_interrupt, + dma8[dev], + dma16[dev], + SB_HW_ALS100, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100, + mpu_port[dev], 0, + mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); + } + + if (fm_port[dev] > 0) { + if (snd_opl3_create(card, + fm_port[dev], fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", + fm_port[dev], fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "ALS100"); + strcpy(card->shortname, "Avance Logic ALS100"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, chip->name, chip->port, + irq[dev], dma8[dev], dma16[dev]); + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_als100_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_als100_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_als100_isapnp_cards[dev] = card; + snd_als100_isapnp_id[dev] = id; + res = snd_card_als100_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_als100_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_als100_pnpids, snd_als100_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no ALS100 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_als100_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_als100_cards[dev]); +} + +module_init(alsa_card_als100_init) +module_exit(alsa_card_als100_exit) + +#ifndef MODULE + +/* format is: snd-als100=enable,index,id,port, + mpu_port,fm_port,irq,mpu_irq, + dma8,dma16 */ + +static int __init alsa_card_als100_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2 && + get_option(&str,&dma16[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-als100=", alsa_card_als100_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/azt2320.c linux/sound/isa/azt2320.c --- linux-2.4.21-rc1.orig/sound/isa/azt2320.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/azt2320.c 2003-03-01 12:04:28.000000000 -0700 @@ -0,0 +1,444 @@ + +/* + card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + This driver should provide support for most Aztech AZT2320 based cards. + Several AZT2316 chips are also supported/tested, but autoprobe doesn't + work: all module option have to be set. + + No docs available for us at Aztech headquarters !!! Unbelievable ... + No other help obtained. + + Thanks to Rainer Wiesner for the WSS + activation method (full-duplex audio!). +*/ + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t cs4231_t + +#define PFX "azt2320: " + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Aztech Systems AZT2320"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Aztech Systems,PRO16V}," + "{Aztech Systems,AZT2320}," + "{Aztech Systems,AZT3300}," + "{Aztech Systems,AZT2320}," + "{Aztech Systems,AZT3000}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for azt2320 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver."); +MODULE_PARM_SYNTAX(wss_port, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); + +struct snd_card_azt2320 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_azt2320_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_azt2320_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_azt2320_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_AZT2320(_va, _vb, _vc, _device, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401), } \ + } + +static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = { + /* PRO16V */ + ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001), + /* Aztech Sound Galaxy 16 */ + ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002), + /* Packard Bell Sound III 336 AM/SP */ + ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001), + /* AT3300 */ + ISAPNP_AZT2320('A','Z','T',0x3002,0x1004,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x3005,0x1003,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x3011,0x1003,0x2001), + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_azt2320_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-azt2320" + + +#ifdef __ISAPNP__ +static int __init snd_card_azt2320_isapnp(int dev, struct snd_card_azt2320 *acard) +{ + const struct isapnp_card_id *id = snd_azt2320_isapnp_id[dev]; + struct isapnp_card *card = snd_azt2320_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], fm_port[dev], 4); + if (wss_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], wss_port[dev], + 4); + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], + 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev], + 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + + if (pdev->activate(pdev) < 0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + port[dev] = pdev->resource[0].start; + fm_port[dev] = pdev->resource[1].start; + wss_port[dev] = pdev->resource[2].start; + dma1[dev] = pdev->dma_resource[0].start; + dma2[dev] = pdev->dma_resource[1].start; + irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + mpu_port[dev] = -1; + return 0; + } + + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], mpu_port[dev], + 2); + if (mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], mpu_irq[dev], + 1); + + if (pdev->activate(pdev) < 0) { + /* not fatal error */ + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + mpu_port[dev] = pdev->resource[0].start; + mpu_irq[dev] = pdev->irq_resource[0].start; + } + + return 0; +} + +static void snd_card_azt2320_deactivate(struct snd_card_azt2320 *acard) +{ + if (acard->dev) + acard->dev->deactivate(acard->dev); + if (acard->devmpu) + acard->devmpu->deactivate(acard->devmpu); +} +#endif /* __ISAPNP__ */ + +/* same of snd_sbdsp_command by Jaroslav Kysela */ +static int __init snd_card_azt2320_command(unsigned long port, unsigned char val) +{ + int i; + unsigned long limit; + + limit = jiffies + HZ / 10; + for (i = 50000; i && (limit - jiffies) > 0; i--) + if (!(inb(port + 0x0c) & 0x80)) { + outb(val, port + 0x0c); + return 0; + } + return -EBUSY; +} + +static int __init snd_card_azt2320_enable_wss(unsigned long port) +{ + int error; + + if ((error = snd_card_azt2320_command(port, 0x09))) + return error; + if ((error = snd_card_azt2320_command(port, 0x00))) + return error; + + mdelay(5); + return 0; +} + +static void snd_card_azt2320_free(snd_card_t *card) +{ + struct snd_card_azt2320 *acard = (struct snd_card_azt2320 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_azt2320_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_azt2320_probe(int dev) +{ + int error; + snd_card_t *card; + struct snd_card_azt2320 *acard; + cs4231_t *chip; + opl3_t *opl3; + + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_azt2320))) == NULL) + return -ENOMEM; + acard = (struct snd_card_azt2320 *)card->private_data; + card->private_free = snd_card_azt2320_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_azt2320_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#endif /* __ISAPNP__ */ + + if ((error = snd_card_azt2320_enable_wss(port[dev]))) { + snd_card_free(card); + return error; + } + + if ((error = snd_cs4231_create(card, wss_port[dev], -1, + irq[dev], + dma1[dev], + dma2[dev], + CS4231_HW_DETECT, 0, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if (mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320, + mpu_port[dev], 0, + mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", + mpu_port[dev]); + } + + if (fm_port[dev] > 0) { + if (snd_opl3_create(card, + fm_port[dev], fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", + fm_port[dev], fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "AZT2320"); + strcpy(card->shortname, "Aztech AZT2320"); + sprintf(card->longname, "%s soundcard, WSS at 0x%lx, irq %i, dma %i&%i", + card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_azt2320_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_azt2320_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_azt2320_isapnp_cards[dev] = card; + snd_azt2320_isapnp_id[dev] = id; + res = snd_card_azt2320_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_azt2320_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_azt2320_pnpids, snd_azt2320_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no AZT2320 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_azt2320_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_azt2320_cards[dev]); +} + +module_init(alsa_card_azt2320_init) +module_exit(alsa_card_azt2320_exit) + +#ifndef MODULE + +/* format is: snd-azt2320=enable,index,id,port, + wss_port,mpu_port,fm_port, + irq,mpu_irq,dma1,dma2 */ + +static int __init alsa_card_azt2320_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&wss_port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-azt2320=", alsa_card_azt2320_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/cmi8330.c linux/sound/isa/cmi8330.c --- linux-2.4.21-rc1.orig/sound/isa/cmi8330.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cmi8330.c 2003-03-04 12:43:20.000000000 -0700 @@ -0,0 +1,683 @@ +/* + * Driver for C-Media's CMI8330 soundcards. + * Copyright (c) by George Talusan + * http://www.undergrad.math.uwaterloo.ca/~gstalusa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * NOTES + * + * The extended registers contain mixer settings which are largely + * untapped for the time being. + * + * MPU401 and SPDIF are not supported yet. I don't have the hardware + * to aid in coding and testing, so I won't bother. + * + * To quickly load the module, + * + * modprobe -a snd-cmi8330 sbport=0x220 sbirq=5 sbdma8=1 + * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0 + * + * This card has two mixers and two PCM devices. I've cheesed it such + * that recording and playback can be done through the same device. + * The driver "magically" routes the capturing to the AD1848 codec, + * and playback to the SB16 codec. This allows for full-duplex mode + * to some extent. + * The utilities in alsa-utils are aware of both devices, so passing + * the appropriate parameters to amixer and alsactl will give you + * full control over both mixers. + */ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#define SNDRV_GET_ID +#include + +/* + */ +/* #define ENABLE_SB_MIXER */ +#define PLAYBACK_ON_SB + +/* + */ +MODULE_AUTHOR("George Talusan "); +MODULE_DESCRIPTION("C-Media CMI8330"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; +#ifdef __ISAPNP__ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for CMI8330 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +#endif + +MODULE_PARM(sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(sbport, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); +MODULE_PARM(sbirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(sbirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{5},dialog:list"); +MODULE_PARM(sbdma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(sbdma8, SNDRV_DMA8_DESC ",prefers:{1}"); +MODULE_PARM(sbdma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(sbdma16, SNDRV_ENABLED ",allows:{{5},{7}},prefers:{5},dialog:list"); + +MODULE_PARM(wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80,0xf40,0xc0}},prefers:{0x530},base:16,dialog:list"); +MODULE_PARM(wssirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(wssirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{11},dialog:list"); +MODULE_PARM(wssdma, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(wssdma, SNDRV_DMA8_DESC ",prefers:{0}"); + +#define CMI8330_RMUX3D 16 +#define CMI8330_MUTEMUX 17 +#define CMI8330_OUTPUTVOL 18 +#define CMI8330_MASTVOL 19 +#define CMI8330_LINVOL 20 +#define CMI8330_CDINVOL 21 +#define CMI8330_WAVVOL 22 +#define CMI8330_RECMUX 23 +#define CMI8330_WAVGAIN 24 +#define CMI8330_LINGAIN 25 +#define CMI8330_CDINGAIN 26 + +static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = +{ + 0x40, /* 16 - recording mux (SB-mixer-enabled) */ +#ifdef ENABLE_SB_MIXER + 0x40, /* 17 - mute mux (Mode2) */ +#else + 0x0, /* 17 - mute mux */ +#endif + 0x0, /* 18 - vol */ + 0x0, /* 19 - master volume */ + 0x0, /* 20 - line-in volume */ + 0x0, /* 21 - cd-in volume */ + 0x0, /* 22 - wave volume */ + 0x0, /* 23 - mute/rec mux */ + 0x0, /* 24 - wave rec gain */ + 0x0, /* 25 - line-in rec gain */ + 0x0 /* 26 - cd-in rec gain */ +}; + +typedef int (*snd_pcm_open_callback_t)(snd_pcm_substream_t *); + +struct snd_cmi8330 { +#ifdef __ISAPNP__ + struct isapnp_dev *cap; + struct isapnp_dev *play; +#endif + snd_card_t *card; + ad1848_t *wss; + sb_t *sb; + + snd_pcm_t *pcm; + struct snd_cmi8330_stream { + snd_pcm_ops_t ops; + snd_pcm_open_callback_t open; + void *private_data; /* sb or wss */ + } streams[2]; +}; + +static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_cmi8330_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_cmi8330_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_CMI8330(_va, _vb, _vc, _device, _audio1, _audio2) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID('@', '@', '@', _audio1), \ + ISAPNP_DEVICE_ID('@', 'X', '@', _audio2), } \ + } + +static struct isapnp_card_id snd_cmi8330_pnpids[] __devinitdata = +{ + ISAPNP_CMI8330('C','M','I',0x0001,0x0001,0x0001), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_cmi8330_pnpids); + +#endif + + +static struct ad1848_mix_elem snd_cmi8330_controls[] __initdata = { +AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), +AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), +AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), +AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), +AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), +AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), +AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), +AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), +AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), +AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), +AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), +AD1848_DOUBLE("CD Capture Volume", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), +AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0), +AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0), +AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0), +AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0), +AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), +AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), +AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), +AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), +AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1), +AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0), +AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1), +AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1), +AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1), +}; + +#ifdef ENABLE_SB_MIXER +static struct sbmix_elem cmi8330_sb_mixers[] __initdata = { +SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), +SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), +SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), +SB_DOUBLE("SB PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), +SB_DOUBLE("SB Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), +SB_DOUBLE("SB CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), +SB_DOUBLE("SB CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), +SB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), +SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), +SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1), +SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), +SB_SINGLE("SB PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3), +SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), +SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), +SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1), +}; + +static unsigned char cmi8330_sb_init_values[][2] __initdata = { + { SB_DSP4_MASTER_DEV + 0, 0 }, + { SB_DSP4_MASTER_DEV + 1, 0 }, + { SB_DSP4_PCM_DEV + 0, 0 }, + { SB_DSP4_PCM_DEV + 1, 0 }, + { SB_DSP4_SYNTH_DEV + 0, 0 }, + { SB_DSP4_SYNTH_DEV + 1, 0 }, + { SB_DSP4_INPUT_LEFT, 0 }, + { SB_DSP4_INPUT_RIGHT, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DSP4_SPEAKER_DEV, 0 }, +}; + + +static int __init cmi8330_add_sb_mixers(sb_t *chip) +{ + int idx, err; + unsigned long flags; + + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */ + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + /* mute and zero volume channels */ + for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_init_values); idx++) { + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0], + cmi8330_sb_init_values[idx][1]); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + } + + for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_mixers); idx++) { + if ((err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx])) < 0) + return err; + } + return 0; +} +#endif + +static int __init snd_cmi8330_mixer(snd_card_t *card, struct snd_cmi8330 *acard) +{ + unsigned int idx; + int err; + + strcpy(card->mixername, "CMI8330/C3D"); + + for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { + if ((err = snd_ad1848_add_ctl_elem(acard->wss, &snd_cmi8330_controls[idx])) < 0) + return err; + } + +#ifdef ENABLE_SB_MIXER + if ((err = cmi8330_add_sb_mixers(acard->sb)) < 0) + return err; +#endif + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_cmi8330_isapnp(int dev, struct snd_cmi8330 *acard) +{ + const struct isapnp_card_id *id = snd_cmi8330_isapnp_id[dev]; + struct isapnp_card *card = snd_cmi8330_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->cap = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->cap->active) { + acard->cap = NULL; + return -EBUSY; + } + acard->play = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->play->active) { + acard->cap = acard->play = NULL; + return -EBUSY; + } + + pdev = acard->cap; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + /* allocate AD1848 resources */ + if (wssport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], wssport[dev], 8); + if (wssdma[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], wssdma[dev], 1); + if (wssirq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], wssirq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("(AD1848) PnP configure failure\n"); + return -EBUSY; + } + wssport[dev] = pdev->resource[0].start; + wssdma[dev] = pdev->dma_resource[0].start; + wssirq[dev] = pdev->irq_resource[0].start; + + /* allocate SB16 resources */ + pdev = acard->play; + if (pdev->prepare(pdev)<0) { + acard->cap->deactivate(acard->cap); + return -EAGAIN; + } + if (sbport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], sbport[dev], 16); + if (sbdma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], sbdma8[dev], 1); + if (sbdma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], sbdma16[dev], 1); + if (sbirq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], sbirq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("CMI8330/C3D (SB16) PnP configure failure\n"); + acard->cap->deactivate(acard->cap); + return -EBUSY; + } + sbport[dev] = pdev->resource[0].start; + sbdma8[dev] = pdev->dma_resource[0].start; + sbdma16[dev] = pdev->dma_resource[1].start; + sbirq[dev] = pdev->irq_resource[0].start; + + return 0; +} + +static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard) +{ + if (acard->cap) { + acard->cap->deactivate(acard->cap); + acard->cap = NULL; + } + if (acard->play) { + acard->play->deactivate(acard->play); + acard->play = NULL; + } +} +#endif + +/* + * PCM interface + * + * since we call the different chip interfaces for playback and capture + * directions, we need a trick. + * + * - copy the ops for each direction into a local record. + * - replace the open callback with the new one, which replaces the + * substream->private_data with the corresponding chip instance + * and calls again the original open callback of the chip. + * + */ + +#ifdef PLAYBACK_ON_SB +#define CMI_SB_STREAM SNDRV_PCM_STREAM_PLAYBACK +#define CMI_AD_STREAM SNDRV_PCM_STREAM_CAPTURE +#else +#define CMI_SB_STREAM SNDRV_PCM_STREAM_CAPTURE +#define CMI_AD_STREAM SNDRV_PCM_STREAM_PLAYBACK +#endif + +static int snd_cmi8330_playback_open(snd_pcm_substream_t * substream) +{ + struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + + /* replace the private_data and call the original open callback */ + substream->private_data = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].private_data; + return chip->streams[SNDRV_PCM_STREAM_PLAYBACK].open(substream); +} + +static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) +{ + struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + + /* replace the private_data and call the original open callback */ + substream->private_data = chip->streams[SNDRV_PCM_STREAM_CAPTURE].private_data; + return chip->streams[SNDRV_PCM_STREAM_CAPTURE].open(substream); +} + +static void snd_cmi8330_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init snd_cmi8330_pcm(snd_card_t *card, struct snd_cmi8330 *chip) +{ + snd_pcm_t *pcm; + const snd_pcm_ops_t *ops; + int err; + static snd_pcm_open_callback_t cmi_open_callbacks[2] = { + snd_cmi8330_playback_open, + snd_cmi8330_capture_open + }; + + if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0) + return err; + strcpy(pcm->name, "CMI8330"); + pcm->private_data = chip; + pcm->private_free = snd_cmi8330_pcm_free; + + /* SB16 */ + ops = snd_sb16dsp_get_pcm_ops(CMI_SB_STREAM); + chip->streams[CMI_SB_STREAM].ops = *ops; + chip->streams[CMI_SB_STREAM].open = ops->open; + chip->streams[CMI_SB_STREAM].ops.open = cmi_open_callbacks[CMI_SB_STREAM]; + chip->streams[CMI_SB_STREAM].private_data = chip->sb; + + /* AD1848 */ + ops = snd_ad1848_get_pcm_ops(CMI_AD_STREAM); + chip->streams[CMI_AD_STREAM].ops = *ops; + chip->streams[CMI_AD_STREAM].open = ops->open; + chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM]; + chip->streams[CMI_AD_STREAM].private_data = chip->wss; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + chip->pcm = pcm; + + return 0; +} + + +/* + */ + +static void snd_cmi8330_free(snd_card_t *card) +{ + struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_cmi8330_deactivate(acard); +#endif + } +} + +static int __init snd_cmi8330_probe(int dev) +{ + snd_card_t *card; + struct snd_cmi8330 *acard; + unsigned long flags; + int i, err; + +#ifdef __ISAPNP__ + if (!isapnp[dev]) { +#endif + if (wssport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify wssport\n"); + return -EINVAL; + } + if (sbport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify sbport\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_cmi8330)); + if (card == NULL) { + snd_printk("could not get a new card\n"); + return -ENOMEM; + } + acard = (struct snd_cmi8330 *)card->private_data; + acard->card = card; + card->private_free = snd_cmi8330_free; + +#ifdef __ISAPNP__ + if (isapnp[dev] && (err = snd_cmi8330_isapnp(dev, acard)) < 0) { + snd_printk("PnP detection failed\n"); + snd_card_free(card); + return err; + } +#endif + + if ((err = snd_ad1848_create(card, + wssport[dev] + 4, + wssirq[dev], + wssdma[dev], + AD1848_HW_DETECT, + &acard->wss)) < 0) { + snd_printk("(AD1848) device busy??\n"); + snd_card_free(card); + return err; + } + if (acard->wss->hardware != AD1848_HW_CMI8330) { + snd_printk("(AD1848) not found during probe\n"); + snd_card_free(card); + return -ENODEV; + } + + if ((err = snd_sbdsp_create(card, sbport[dev], + sbirq[dev], + snd_sb16dsp_interrupt, + sbdma8[dev], + sbdma16[dev], + SB_HW_AUTO, &acard->sb)) < 0) { + snd_printk("(SB16) device busy??\n"); + snd_card_free(card); + return err; + } + if (acard->sb->hardware != SB_HW_16) { + snd_printk("(SB16) not found during probe\n"); + snd_card_free(card); + return -ENODEV; + } + + spin_lock_irqsave(&acard->wss->reg_lock, flags); + snd_ad1848_out(acard->wss, AD1848_MISC_INFO, 0x40); /* switch on MODE2 */ + for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) + snd_ad1848_out(acard->wss, i, snd_cmi8330_image[i - CMI8330_RMUX3D]); + spin_unlock_irqrestore(&acard->wss->reg_lock, flags); + + if ((err = snd_cmi8330_mixer(card, acard)) < 0) { + snd_printk("failed to create mixers\n"); + snd_card_free(card); + return err; + } + + if ((err = snd_cmi8330_pcm(card, acard)) < 0) { + snd_printk("failed to create pcms\n"); + snd_card_free(card); + return err; + } + + strcpy(card->driver, "CMI8330/C3D"); + strcpy(card->shortname, "C-Media CMI8330/C3D"); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + card->shortname, + acard->wss->port, + wssirq[dev], + wssdma[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + snd_cmi8330_cards[dev] = card; + return 0; +} + +static void __exit alsa_card_cmi8330_exit(void) +{ + int i; + + for (i = 0; i < SNDRV_CARDS; i++) + snd_card_free(snd_cmi8330_cards[i]); +} + +#ifdef __ISAPNP__ +static int __init snd_cmi8330_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || !isapnp[dev]) + continue; + snd_cmi8330_isapnp_cards[dev] = card; + snd_cmi8330_isapnp_id[dev] = id; + res = snd_cmi8330_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_cmi8330_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (snd_cmi8330_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_cmi8330_pnpids, snd_cmi8330_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "CMI8330 not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +module_init(alsa_card_cmi8330_init) +module_exit(alsa_card_cmi8330_exit) + +#ifndef MODULE + +/* format is: snd-cmi8330=enable,index,id,isapnp, + sbport,sbirq, + sbdma8,sbdma16, + wssport,wssirq, + wssdma */ + +static int __init alsa_card_cmi8330_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&sbport[nr_dev]) == 2 && + get_option(&str,&sbirq[nr_dev]) == 2 && + get_option(&str,&sbdma8[nr_dev]) == 2 && + get_option(&str,&sbdma16[nr_dev]) == 2 && + get_option(&str,(int *)&wssport[nr_dev]) == 2 && + get_option(&str,&wssirq[nr_dev]) == 2 && + get_option(&str,&wssdma[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-cmi8330=", alsa_card_cmi8330_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/cs423x/Makefile linux/sound/isa/cs423x/Makefile --- linux-2.4.21-rc1.orig/sound/isa/cs423x/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cs423x/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,50 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _cs423x.o + +list-multi := snd-cs4231-lib.o snd-cs4236-lib.o \ + snd-cs4231.o snd-cs4232.o snd-cs4236.o + +export-objs := cs4231_lib.o cs4236_lib.o + +snd-cs4231-lib-objs := cs4231_lib.o +snd-cs4236-lib-objs := cs4236_lib.o +snd-cs4231-objs := cs4231.o +snd-cs4232-objs := cs4232.o +snd-cs4236-objs := cs4236.o +snd-pc98-cs4232-objs := pc98.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o +obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o +obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o +obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o +obj-$(CONFIG_SND_PC98_CS4232) += snd-pc98-cs4232.o snd-cs4231-lib.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-cs4231-lib.o: $(snd-cs4231-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-lib-objs) + +snd-cs4236-lib.o: $(snd-cs4236-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-lib-objs) + +snd-cs4231.o: $(snd-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-objs) + +snd-cs4232.o: $(snd-cs4232-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4232-objs) + +snd-cs4236.o: $(snd-cs4236-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4231.c linux/sound/isa/cs423x/cs4231.c --- linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4231.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cs423x/cs4231.c 2002-10-21 12:28:21.000000000 -0600 @@ -0,0 +1,209 @@ +/* + * Generic driver for CS4231 chips + * Copyright (c) by Jaroslav Kysela + * Originally the CS4232/CS4232A driver, modified for use on CS4231 by + * Tugrul Galatali + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t cs4231_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic CS4231"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Crystal Semiconductors,CS4231}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for CS4231 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for CS4231 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable CS4231 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for CS4231 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for CS4231 driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for CS4231 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for CS4231 driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for CS4231 driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for CS4231 driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); + +static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_cs4231_probe(int dev) +{ + snd_card_t *card; + struct snd_card_cs4231 *acard; + snd_pcm_t *pcm = NULL; + cs4231_t *chip; + int err; + + if (port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify port\n"); + return -EINVAL; + } + if (irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify irq\n"); + return -EINVAL; + } + if (dma1[dev] == SNDRV_AUTO_DMA) { + snd_printk("specify dma1\n"); + return -EINVAL; + } + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_card_cs4231 *)card->private_data; + if (mpu_port[dev] < 0) + mpu_port[dev] = SNDRV_AUTO_PORT; + if ((err = snd_cs4231_create(card, port[dev], -1, + irq[dev], + dma1[dev], + dma2[dev], + CS4231_HW_DETECT, + 0, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (mpu_irq[dev] >= 0 && mpu_irq[dev] != SNDRV_AUTO_IRQ) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, + mpu_port[dev], 0, + mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR "cs4231: MPU401 not detected\n"); + } + strcpy(card->driver, "CS4231"); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + pcm->name, chip->port, irq[dev], dma1[dev]); + if (dma2[dev] >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4231_cards[dev] = card; + return 0; +} + +static int __init alsa_card_cs4231_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (snd_card_cs4231_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "CS4231 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_cs4231_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_cs4231_cards[idx]); +} + +module_init(alsa_card_cs4231_init) +module_exit(alsa_card_cs4231_exit) + +#ifndef MODULE + +/* format is: snd-cs4231=enable,index,id, + port,mpu_port,irq,mpu_irq, + dma1,dma2 */ + +static int __init alsa_card_cs4231_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs4231=", alsa_card_cs4231_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4231_lib.c linux/sound/isa/cs423x/cs4231_lib.c --- linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4231_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cs423x/cs4231_lib.c 2003-02-15 08:01:38.000000000 -0700 @@ -0,0 +1,1966 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips + * + * Bugs: + * - sometimes record brokes playback with WSS portion of + * Yamaha OPL3-SA3 chip + * - CS4231 (GUS MAX) - still trouble with occasional noises + * - broken initialization? + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips"); +MODULE_LICENSE("GPL"); + +#define chip_t cs4231_t + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | CS4231_XTAL2, + /* 6620 */ 0x0E | CS4231_XTAL2, + /* 8000 */ 0x00 | CS4231_XTAL1, + /* 9600 */ 0x0E | CS4231_XTAL1, + /* 11025 */ 0x02 | CS4231_XTAL2, + /* 16000 */ 0x02 | CS4231_XTAL1, + /* 18900 */ 0x04 | CS4231_XTAL2, + /* 22050 */ 0x06 | CS4231_XTAL2, + /* 27042 */ 0x04 | CS4231_XTAL1, + /* 32000 */ 0x06 | CS4231_XTAL1, + /* 33075 */ 0x0C | CS4231_XTAL2, + /* 37800 */ 0x08 | CS4231_XTAL2, + /* 44100 */ 0x0A | CS4231_XTAL2, + /* 48000 */ 0x0C | CS4231_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = 14, + .list = rates, + .mask = 0, +}; + +static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); +} + +static unsigned char snd_cs4231_original_image[32] = +{ + 0x00, /* 00/00 - lic */ + 0x00, /* 01/01 - ric */ + 0x9f, /* 02/02 - la1ic */ + 0x9f, /* 03/03 - ra1ic */ + 0x9f, /* 04/04 - la2ic */ + 0x9f, /* 05/05 - ra2ic */ + 0xbf, /* 06/06 - loc */ + 0xbf, /* 07/07 - roc */ + 0x20, /* 08/08 - pdfr */ + CS4231_AUTOCALIB, /* 09/09 - ic */ + 0x00, /* 0a/10 - pc */ + 0x00, /* 0b/11 - ti */ + CS4231_MODE2, /* 0c/12 - mi */ + 0xfc, /* 0d/13 - lbc */ + 0x00, /* 0e/14 - pbru */ + 0x00, /* 0f/15 - pbrl */ + 0x80, /* 10/16 - afei */ + 0x01, /* 11/17 - afeii */ + 0x9f, /* 12/18 - llic */ + 0x9f, /* 13/19 - rlic */ + 0x00, /* 14/20 - tlb */ + 0x00, /* 15/21 - thb */ + 0x00, /* 16/22 - la3mic/reserved */ + 0x00, /* 17/23 - ra3mic/reserved */ + 0x00, /* 18/24 - afs */ + 0x00, /* 19/25 - lamoc/version */ + 0xcf, /* 1a/26 - mioc */ + 0x00, /* 1b/27 - ramoc/reserved */ + 0x20, /* 1c/28 - cdfr */ + 0x00, /* 1d/29 - res4 */ + 0x00, /* 1e/30 - cbru */ + 0x00, /* 1f/31 - cbrl */ +}; + +/* + * Basic I/O functions + */ + +#if !defined(EBUS_SUPPORT) && !defined(SBUS_SUPPORT) +#define __CS4231_INLINE__ inline +#else +#define __CS4231_INLINE__ /* nothing */ +#endif + +static __CS4231_INLINE__ void cs4231_outb(cs4231_t *chip, u8 offset, u8 val) +{ +#ifdef EBUS_SUPPORT + if (chip->ebus->flag) { + writeb(val, chip->port + (offset << 2)); + } else { +#endif +#ifdef SBUS_SUPPORT + sbus_writeb(val, chip->port + (offset << 2)); +#endif +#ifdef EBUS_SUPPORT + } +#endif +#ifdef LEGACY_SUPPORT + outb(val, chip->port + offset); +#endif +} + +static __CS4231_INLINE__ u8 cs4231_inb(cs4231_t *chip, u8 offset) +{ +#ifdef EBUS_SUPPORT + if (chip->ebus_flag) { + return readb(chip->port + (offset << 2)); + } else { +#endif +#ifdef SBUS_SUPPORT + return sbus_readb(chip->port + (offset << 2)); +#endif +#ifdef EBUS_SUPPORT + } +#endif +#ifdef LEGACY_SUPPORT + return inb(chip->port + offset); +#endif +} + +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + int timeout; + unsigned char tmp; + + for (timeout = 250; + timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) + snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + if (chip->calibrate_mute) { + chip->image[reg] &= mask; + chip->image[reg] |= value; + } else { + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); + mb(); + tmp = (chip->image[reg] & mask) | value; + cs4231_outb(chip, CS4231P(REG), tmp); + chip->image[reg] = tmp; + mb(); + } +} + +static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); + timeout--) + udelay(10); + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); + cs4231_outb(chip, CS4231P(REG), value); + mb(); +} + +void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) + snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); + cs4231_outb(chip, CS4231P(REG), value); + chip->image[reg] = value; + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) + snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); +#endif + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); + mb(); + return cs4231_inb(chip, CS4231P(REG)); +} + +void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val) +{ + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); + cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01)); + cs4231_outb(chip, CS4231P(REG), val); + chip->eimage[CS4236_REG(reg)] = val; +#if 0 + printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val); +#endif +} + +unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg) +{ + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); + cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01)); +#if 1 + return cs4231_inb(chip, CS4231P(REG)); +#else + { + unsigned char res; + res = cs4231_inb(chip, CS4231P(REG)); + printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res); + return res; + } +#endif +} + +#ifdef CONFIG_SND_DEBUG + +void snd_cs4231_debug(cs4231_t *chip) +{ + printk("CS4231 REGS: INDEX = 0x%02x ", cs4231_inb(chip, CS4231P(REGSEL))); + printk(" STATUS = 0x%02x\n", cs4231_inb(chip, CS4231P(STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00)); + printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10)); + printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01)); + printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11)); + printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02)); + printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12)); + printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03)); + printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13)); + printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04)); + printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14)); + printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05)); + printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15)); + printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06)); + printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16)); + printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07)); + printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); + printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08)); + printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18)); + printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09)); + printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19)); + printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a)); + printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); + printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b)); + printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); + printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c)); + printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); + printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d)); + printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); + printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e)); + printk(" 0x1e: ply lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); + printk(" 0x0f: rec upr count = 0x%02x ", snd_cs4231_in(chip, 0x0f)); + printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); +} + +#endif + +/* + * CS4231 detection / MCE routines + */ + +static void snd_cs4231_busy_wait(cs4231_t *chip) +{ + int timeout; + + /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ + for (timeout = 5; timeout > 0; timeout--) + cs4231_inb(chip, CS4231P(REGSEL)); + /* end of cleanup sequence */ + for (timeout = 250; + timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); + timeout--) + udelay(10); +} + +void snd_cs4231_mce_up(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + + for (timeout = 250; timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + chip->mce_bit |= CS4231_MCE; + timeout = cs4231_inb(chip, CS4231P(REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & CS4231_MCE)) + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +void snd_cs4231_mce_down(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + snd_cs4231_busy_wait(chip); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL)); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + chip->mce_bit &= ~CS4231_MCE; + timeout = cs4231_inb(chip, CS4231P(REGSEL)); + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0 || + !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { + return; + } + snd_cs4231_busy_wait(chip); + + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--) + udelay(10); + if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { + snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + timeout = HZ / 4 / 2; + time = 2; + while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + if (time > 0) + continue; + time = 2; + if (--timeout < 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + timeout = HZ / 10 / 2; + time = 2; + while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + if (time > 0) + continue; + time = 2; + if (--timeout < 0) { + snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); + return; + } + } +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL))); +#endif +} + +static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) +{ + switch (format & 0xe0) { + case CS4231_LINEAR_16: + case CS4231_LINEAR_16_BIG: + size >>= 1; + break; + case CS4231_ADPCM_16: + return size >> 2; + } + if (format & CS4231_STEREO) + size >>= 1; + return size; +} + +static int snd_cs4231_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + unsigned int what; + snd_pcm_substream_t *s; + int do_start; + +#if 0 + printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, cs4231_inb(chip, CS4231P(STATUS))); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + do_start = 1; break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + do_start = 0; break; + default: + return -EINVAL; + } + + what = 0; + s = substream; + do { + if (s == chip->playback_substream) { + what |= CS4231_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= CS4231_RECORD_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&chip->reg_lock); + if (do_start) { + chip->image[CS4231_IFACE_CTRL] |= what; + if (chip->trigger) + chip->trigger(chip, what, 1); + } else { + chip->image[CS4231_IFACE_CTRL] &= ~what; + if (chip->trigger) + chip->trigger(chip, what, 0); + } + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock(&chip->reg_lock); +#if 0 + snd_cs4231_debug(chip); +#endif + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_cs4231_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + // snd_BUG(); + return freq_bits[13]; +} + +static unsigned char snd_cs4231_get_format(cs4231_t *chip, + int format, + int channels) +{ + unsigned char rformat; + + rformat = CS4231_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break; + case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break; + case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break; + } + if (channels > 1) + rformat |= CS4231_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + if (!mute) { + snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]); + } + snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); + if (chip->hardware == CS4231_HW_INTERWAVE) { + snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]); + snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]); + } + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4231_playback_format(cs4231_t *chip, + snd_pcm_hw_params_t *params, + unsigned char pdfr) +{ + unsigned long flags; + int full_calib = 1; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + if (chip->hardware == CS4231_HW_CS4231A || + (chip->hardware & CS4231_HW_CS4232_MASK)) { + spin_lock_irqsave(&chip->reg_lock, flags); + if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); + udelay(100); /* Fixes audible clicks at least on GUS MAX */ + full_calib = 0; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + if (full_calib) { + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? + (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : + pdfr); + } else { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + } + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +static void snd_cs4231_capture_format(cs4231_t *chip, + snd_pcm_hw_params_t *params, + unsigned char cdfr) +{ + unsigned long flags; + int full_calib = 1; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + if (chip->hardware == CS4231_HW_CS4231A || + (chip->hardware & CS4231_HW_CS4232_MASK)) { + spin_lock_irqsave(&chip->reg_lock, flags); + if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */ + (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); + full_calib = 0; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + if (full_calib) { + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->hardware != CS4231_HW_INTERWAVE) { + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | + (cdfr & 0x0f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + } + } + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + } + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +/* + * Timer interface + */ + +static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + if (chip->hardware & CS4231_HW_CS4236B_MASK) + return 14467; + else + return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; +} + +static int snd_cs4231_timer_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned int ticks; + cs4231_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + ticks = timer->sticks; + if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || + (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || + (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { + snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8)); + snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4231_timer_stop(snd_timer_t * timer) +{ + unsigned long flags; + cs4231_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4231_init(cs4231_t *chip) +{ + unsigned long flags; + + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUGq_MCE + snd_printk("init: (1)\n"); +#endif + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | + CS4231_CALIB_MODE); + chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (2)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); +#endif + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (4)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (5)\n"); +#endif +} + +static int snd_cs4231_open(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if ((chip->mode & mode) || + ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) { + up(&chip->open_mutex); + return -EAGAIN; + } + if (chip->mode & CS4231_MODE_OPEN) { + chip->mode |= mode; + up(&chip->open_mutex); + return 0; + } + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ + cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ + chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; + snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + return 0; +} + +static void snd_cs4231_close(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + chip->mode &= ~mode; + if (chip->mode & CS4231_MODE_OPEN) { + up(&chip->open_mutex); + return; + } + snd_cs4231_calibrate_mute(chip, 1); + + /* disable IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ + cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ + chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; + snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + + /* now disable record & playback */ + + if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + } + + /* clear IRQ again */ + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ + cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_cs4231_calibrate_mute(chip, 0); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * timer open/close + */ + +static int snd_cs4231_timer_open(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_open(chip, CS4231_MODE_TIMER); + return 0; +} + +static int snd_cs4231_timer_close(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_close(chip, CS4231_MODE_TIMER); + return 0; +} + +static struct _snd_timer_hardware snd_cs4231_timer_table = +{ + .flags = SNDRV_TIMER_HW_AUTO, + .resolution = 9945, + .ticks = 65535, + .open = snd_cs4231_timer_open, + .close = snd_cs4231_timer_close, + .c_resolution = snd_cs4231_timer_resolution, + .start = snd_cs4231_timer_start, + .stop = snd_cs4231_timer_stop, +}; + +/* + * ok.. exported functions.. + */ + +static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_pdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + chip->set_playback_format(chip, hw_params, new_pdfr); + return 0; +} + +static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +#ifdef LEGACY_SUPPORT +static int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->p_dma_size = size; + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + snd_cs4231_debug(chip); +#endif + return 0; +} +#endif /* LEGACY_SUPPORT */ + +static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_cdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + chip->set_capture_format(chip, hw_params, new_cdfr); + return 0; +} + +static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +#ifdef LEGACY_SUPPORT +static int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->c_dma_size = size; + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1; + if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); + } else { + snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8)); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} +#endif + +static void snd_cs4231_overrange(cs4231_t *chip) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&chip->reg_lock, flags); + res = snd_cs4231_in(chip, CS4231_TEST_INIT); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */ + chip->capture_substream->runtime->overrange++; +} + +void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return); + unsigned char status; + + status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); + if (status & CS4231_TIMER_IRQ) { + if (chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + } + if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { + if (status & CS4231_PLAYBACK_IRQ) { + if (chip->mode & CS4231_MODE_PLAY) + snd_pcm_period_elapsed(chip->playback_substream); + if (chip->mode & CS4231_MODE_RECORD) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + } + } else { + if (status & CS4231_PLAYBACK_IRQ) + snd_pcm_period_elapsed(chip->playback_substream); + if (status & CS4231_RECORD_IRQ) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + } + + spin_lock(&chip->reg_lock); + snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); + spin_unlock(&chip->reg_lock); +} + +#ifdef LEGACY_SUPPORT +static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) + return 0; + ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) + return 0; + ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} +#endif /* LEGACY_SUPPORT */ + +/* + + */ + +int snd_cs4231_probe(cs4231_t *chip) +{ + unsigned long flags; + int i, id, rev; + unsigned char *ptr; + unsigned int hw; + +#if 0 + snd_cs4231_debug(chip); +#endif + id = 0; + for (i = 0; i < 50; i++) { + mb(); + if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) + udelay(2000); + else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); + id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (id == 0x0a) + break; /* this is valid value */ + } + } + snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); + if (id != 0x0a) + return -ENODEV; /* no valid device found */ + + if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { + rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7; + snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); + if (rev == 0x80) { + unsigned char tmp = snd_cs4231_in(chip, 23); + snd_cs4231_out(chip, 23, ~tmp); + if (snd_cs4231_in(chip, 23) != tmp) + chip->hardware = CS4231_HW_AD1845; + else + chip->hardware = CS4231_HW_CS4231; + } else if (rev == 0xa0) { + chip->hardware = CS4231_HW_CS4231A; + } else if (rev == 0xa2) { + chip->hardware = CS4231_HW_CS4232; + } else if (rev == 0xb2) { + chip->hardware = CS4231_HW_CS4232A; + } else if (rev == 0x83) { + chip->hardware = CS4231_HW_CS4236; + } else if (rev == 0x03) { + chip->hardware = CS4231_HW_CS4236B; + } else { + snd_printk("unknown CS chip with version 0x%x\n", rev); + return -ENODEV; /* unknown CS4231 chip? */ + } + } + spin_lock_irqsave(&chip->reg_lock, flags); + cs4231_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */ + cs4231_outb(chip, CS4231P(STATUS), 0); + mb(); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->image[CS4231_MISC_INFO] = CS4231_MODE2; + switch (chip->hardware) { + case CS4231_HW_INTERWAVE: + chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; + break; + case CS4231_HW_CS4235: + case CS4231_HW_CS4236B: + case CS4231_HW_CS4237B: + case CS4231_HW_CS4238B: + case CS4231_HW_CS4239: + if (hw == CS4231_HW_DETECT3) + chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; + else + chip->hardware = CS4231_HW_CS4236; + break; + } + + chip->image[CS4231_IFACE_CTRL] = + (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | + (chip->single_dma ? CS4231_SINGLE_DMA : 0); + chip->image[CS4231_ALT_FEATURE_1] = 0x80; + chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; + ptr = (unsigned char *) &chip->image; + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */ + snd_cs4231_out(chip, i, *ptr++); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_up(chip); + snd_cs4231_mce_down(chip); + + mdelay(2); + + /* ok.. try check hardware version for CS4236+ chips */ + if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { + if (chip->hardware == CS4231_HW_CS4236B) { + rev = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); + id = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_cs4236_ext_out(chip, CS4236_VERSION, rev); + snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); + if ((id & 0x1f) == 0x1d) { /* CS4235 */ + chip->hardware = CS4231_HW_CS4235; + switch (id >> 5) { + case 4: + case 5: + case 6: + break; + default: + snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */ + switch (id >> 5) { + case 4: + case 5: + case 6: + case 7: + chip->hardware = CS4231_HW_CS4236B; + break; + default: + snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x08) { /* CS4237B */ + chip->hardware = CS4231_HW_CS4237B; + switch (id >> 5) { + case 4: + case 5: + case 6: + case 7: + break; + default: + snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x09) { /* CS4238B */ + chip->hardware = CS4231_HW_CS4238B; + switch (id >> 5) { + case 5: + case 6: + case 7: + break; + default: + snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x1e) { /* CS4239 */ + chip->hardware = CS4231_HW_CS4239; + switch (id >> 5) { + case 4: + case 5: + case 6: + break; + default: + snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); + } + } else { + snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); + } + } + } + return 0; /* all things are ok.. */ +} + +/* + + */ + +static snd_pcm_hardware_t snd_cs4231_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_cs4231_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + + */ + +static int snd_cs4231_playback_open(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_playback; + + /* hardware bug in InterWave chipset */ + if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3) + runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; + + /* hardware limitation of cheap chips */ + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + +#ifdef LEGACY_SUPPORT + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + + if (chip->claim_dma) { + if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) + return err; + } +#endif + + if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) { +#ifdef LEGACY_SUPPORT + if (chip->release_dma) + chip->release_dma(chip, chip->dma_private_data, chip->dma1); +#endif + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->playback_substream = substream; +#if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) + chip->p_periods_sent = 0; +#endif + snd_pcm_set_sync(substream); + chip->rate_constraint(runtime); + return 0; +} + +static int snd_cs4231_capture_open(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_capture; + + /* hardware limitation of cheap chips */ + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + +#ifdef LEGACY_SUPPORT + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + + if (chip->claim_dma) { + if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) + return err; + } +#endif + + if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) { +#ifdef LEGACY_SUPPORT + if (chip->release_dma) + chip->release_dma(chip, chip->dma_private_data, chip->dma2); +#endif + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->capture_substream = substream; +#if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) + chip->c_periods_sent = 0; +#endif + snd_pcm_set_sync(substream); + chip->rate_constraint(runtime); + return 0; +} + +static int snd_cs4231_playback_close(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_PLAY); + return 0; +} + +static int snd_cs4231_capture_close(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_RECORD); + return 0; +} + +#ifdef CONFIG_PM + +static void snd_cs4231_suspend(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) + chip->image[reg] = snd_cs4231_in(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4231_resume(cs4231_t *chip) +{ + int reg; + unsigned long flags; + int timeout; + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) { + switch (reg) { + case CS4231_VERSION: + break; + default: + snd_cs4231_out(chip, reg, chip->image[reg]); + break; + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + snd_cs4231_mce_down(chip); +#else + /* The following is a workaround to avoid freeze after resume on TP600E. + This is the first half of copy of snd_cs4231_mce_down(), but doesn't + include rescheduling. -- iwai + */ + snd_cs4231_busy_wait(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->mce_bit &= ~CS4231_MCE; + timeout = cs4231_inb(chip, CS4231P(REGSEL)); + cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (timeout == 0x80) + snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0 || + !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { + return; + } + snd_cs4231_busy_wait(chip); +#endif +} + +static int snd_cs4231_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + if (chip->suspend) + (*chip->suspend)(chip); + break; + case PM_RESUME: + if (chip->resume) + (*chip->resume)(chip); + break; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#ifdef LEGACY_SUPPORT + +static int snd_cs4231_free(cs4231_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_cport) { + release_resource(chip->res_cport); + kfree_nocheck(chip->res_cport); + } + if (chip->irq >= 0) { + disable_irq(chip->irq); + if (!(chip->hwshare & CS4231_HWSHARE_IRQ)) + free_irq(chip->irq, (void *) chip); + } + if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) { + snd_dma_disable(chip->dma1); + free_dma(chip->dma1); + } + if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) { + snd_dma_disable(chip->dma2); + free_dma(chip->dma2); + } +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->timer) + snd_device_free(chip->card, chip->timer); + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs4231_dev_free(snd_device_t *device) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + return snd_cs4231_free(chip); +} + +#endif /* LEGACY_SUPPORT */ + +const char *snd_cs4231_chip_id(cs4231_t *chip) +{ + switch (chip->hardware) { + case CS4231_HW_CS4231: return "CS4231"; + case CS4231_HW_CS4231A: return "CS4231A"; + case CS4231_HW_CS4232: return "CS4232"; + case CS4231_HW_CS4232A: return "CS4232A"; + case CS4231_HW_CS4235: return "CS4235"; + case CS4231_HW_CS4236: return "CS4236"; + case CS4231_HW_CS4236B: return "CS4236B"; + case CS4231_HW_CS4237B: return "CS4237B"; + case CS4231_HW_CS4238B: return "CS4238B"; + case CS4231_HW_CS4239: return "CS4239"; + case CS4231_HW_INTERWAVE: return "AMD InterWave"; + case CS4231_HW_OPL3SA2: return chip->card->shortname; + case CS4231_HW_AD1845: return "AD1845"; + default: return "???"; + } +} + +static int snd_cs4231_new(snd_card_t * card, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + cs4231_t *chip; + + *rchip = NULL; + chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->hardware = hardware; + chip->hwshare = hwshare; + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->rate_constraint = snd_cs4231_xrate; + chip->set_playback_format = snd_cs4231_playback_format; + chip->set_capture_format = snd_cs4231_capture_format; + memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); + + *rchip = chip; + return 0; +} + +#ifdef LEGACY_SUPPORT + +int snd_cs4231_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + static snd_device_ops_t ops = { + .dev_free = snd_cs4231_dev_free, + }; + cs4231_t *chip; + int err; + + err = snd_cs4231_new(card, hardware, hwshare, &chip); + if (err < 0) + return err; + + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + + if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->port = port; + if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) { + snd_cs4231_free(chip); + return -ENODEV; + } + chip->cport = cport; + if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->dma1 = dma1; + if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) { + snd_cs4231_free(chip); + return -EBUSY; + } + if (dma1 == dma2 || dma2 < 0) { + chip->single_dma = 1; + chip->dma2 = chip->dma1; + } else + chip->dma2 = dma2; + + /* global setup */ + if (snd_cs4231_probe(chip) < 0) { + snd_cs4231_free(chip); + return -ENODEV; + } + snd_cs4231_init(chip); + + if (chip->hardware & CS4231_HW_CS4232_MASK) { + if (chip->res_cport == NULL) + snd_printk("CS4232 control port features are not accessible\n"); + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs4231_free(chip); + return err; + } + +#ifdef CONFIG_PM + /* Power Management */ + chip->suspend = snd_cs4231_suspend; + chip->resume = snd_cs4231_resume; + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_cs4231_pm_callback); + if (chip->pm_dev) + chip->pm_dev->data = chip; +#endif + + *rchip = chip; + return 0; +} + +#endif /* LEGACY_SUPPORT */ + +static snd_pcm_ops_t snd_cs4231_playback_ops = { + .open = snd_cs4231_playback_open, + .close = snd_cs4231_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs4231_playback_hw_params, + .hw_free = snd_cs4231_playback_hw_free, + .prepare = snd_cs4231_playback_prepare, + .trigger = snd_cs4231_trigger, + .pointer = snd_cs4231_playback_pointer, +}; + +static snd_pcm_ops_t snd_cs4231_capture_ops = { + .open = snd_cs4231_capture_open, + .close = snd_cs4231_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs4231_capture_hw_params, + .hw_free = snd_cs4231_capture_hw_free, + .prepare = snd_cs4231_capture_prepare, + .trigger = snd_cs4231_trigger, + .pointer = snd_cs4231_capture_pointer, +}; + +static void snd_cs4231_pcm_free(snd_pcm_t *pcm) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_cs4231_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0) + return err; + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_cs4231_pcm_free; + pcm->info_flags = 0; + if (chip->single_dma) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + if (chip->hardware != CS4231_HW_INTERWAVE) + pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; + strcpy(pcm->name, snd_cs4231_chip_id(chip)); + +#ifdef LEGACY_SUPPORT + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); +#else +# ifdef EBUS_SUPPORT + if (chip->ebus_flag) { + snd_pcm_lib_preallocate_pci_pages_for_all(chip->dev_u.pdev, pcm, + 64*1024, 128*1024); + } else { +# endif +# ifdef SBUS_SUPPORT + snd_pcm_lib_preallocate_sbus_pages_for_all(chip->dev_u.sdev, pcm, + 64*1024, 128*1024); +# endif +# ifdef EBUS_SUPPORT + } +# endif +#endif + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_cs4231_timer_free(snd_timer_t *timer) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_cs4231_timer(cs4231_t *chip, int device, snd_timer_t **rtimer) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int err; + + /* Timer initialization */ + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) + return err; + strcpy(timer->name, snd_cs4231_chip_id(chip)); + timer->private_data = chip; + timer->private_free = snd_cs4231_timer_free; + timer->hw = snd_cs4231_timer_table; + chip->timer = timer; + if (rtimer) + *rtimer = timer; + return 0; +} + +/* + * MIXER part + */ + +static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line", "Aux", "Mic", "Mix" + }; + static char *opl3sa_texts[4] = { + "Line", "CD", "Mic", "Mix" + }; + static char *gusmax_texts[4] = { + "Line", "Synth", "Mic", "Mix" + }; + char **ptexts = texts; + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + + snd_assert(chip->card != NULL, return -EINVAL); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + if (!strcmp(chip->card->driver, "GUS MAX")) + ptexts = gusmax_texts; + switch (chip->hardware) { + case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break; + case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break; + } + strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_cs4231_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4231_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->reg_lock, flags); + left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; + right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; + change = left != chip->image[CS4231_LEFT_INPUT] || + right != chip->image[CS4231_RIGHT_INPUT]; + snd_cs4231_out(chip, CS4231_LEFT_INPUT, left); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_cs4231_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4231_out(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4231_controls[] = { +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1), +CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_cs4231_info_mux, + .get = snd_cs4231_get_mux, + .put = snd_cs4231_put_mux, +}, +CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1) +}; + +int snd_cs4231_mixer(cs4231_t *chip) +{ + snd_card_t *card; + unsigned int idx; + int err; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < CS4231_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_cs4231_out); +EXPORT_SYMBOL(snd_cs4231_in); +EXPORT_SYMBOL(snd_cs4231_outm); +EXPORT_SYMBOL(snd_cs4236_ext_out); +EXPORT_SYMBOL(snd_cs4236_ext_in); +EXPORT_SYMBOL(snd_cs4231_mce_up); +EXPORT_SYMBOL(snd_cs4231_mce_down); +EXPORT_SYMBOL(snd_cs4231_interrupt); +EXPORT_SYMBOL(snd_cs4231_chip_id); +EXPORT_SYMBOL(snd_cs4231_create); +EXPORT_SYMBOL(snd_cs4231_pcm); +EXPORT_SYMBOL(snd_cs4231_mixer); +EXPORT_SYMBOL(snd_cs4231_timer); +EXPORT_SYMBOL(snd_cs4231_info_single); +EXPORT_SYMBOL(snd_cs4231_get_single); +EXPORT_SYMBOL(snd_cs4231_put_single); +EXPORT_SYMBOL(snd_cs4231_info_double); +EXPORT_SYMBOL(snd_cs4231_get_double); +EXPORT_SYMBOL(snd_cs4231_put_double); + +/* + * INIT part + */ + +static int __init alsa_cs4231_init(void) +{ + return 0; +} + +static void __exit alsa_cs4231_exit(void) +{ +} + +module_init(alsa_cs4231_init) +module_exit(alsa_cs4231_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4232.c linux/sound/isa/cs423x/cs4232.c --- linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4232.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cs423x/cs4232.c 2001-12-18 07:52:00.000000000 -0700 @@ -0,0 +1,2 @@ +#define CS4232 +#include "cs4236.c" diff -urN linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4236.c linux/sound/isa/cs423x/cs4236.c --- linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4236.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cs423x/cs4236.c 2003-02-15 07:52:40.000000000 -0700 @@ -0,0 +1,655 @@ +/* + * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t cs4231_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifdef CS4232 +MODULE_DESCRIPTION("Cirrus Logic CS4232"); +MODULE_DEVICES("{{Turtle Beach,TBS-2000}," + "{Turtle Beach,Tropez Plus}," + "{SIC CrystalWave 32}," + "{Hewlett Packard,Omnibook 5500}," + "{TerraTec,Maestro 32/96}," + "{Philips,PCA70PS}}"); +#else +MODULE_DESCRIPTION("Cirrus Logic CS4235-9"); +MODULE_DEVICES("{{Crystal Semiconductors,CS4235}," + "{Crystal Semiconductors,CS4236}," + "{Crystal Semiconductors,CS4237}," + "{Crystal Semiconductors,CS4238}," + "{Crystal Semiconductors,CS4239}," + "{Acer,AW37}," + "{Acer,AW35/Pro}," + "{Crystal,3D}," + "{Crystal Computer,TidalWave128}," + "{Dell,Optiplex GX1}," + "{Dell,Workstation 400 sound}," + "{EliteGroup,P5TX-LA sound}," + "{Gallant,SC-70P}," + "{Gateway,E1000 Onboard CS4236B}," + "{Genius,Sound Maker 3DJ}," + "{Hewlett Packard,HP6330 sound}," + "{IBM,PC 300PL sound}," + "{IBM,Aptiva 2137 E24}," + "{IBM,IntelliStation M Pro}," + "{Intel,Marlin Spike Mobo CS4235}," + "{Intel PR440FX Onboard}," + "{Guillemot,MaxiSound 16 PnP}," + "{NewClear,3D}," + "{TerraTec,AudioSystem EWS64L/XL}," + "{Typhoon Soundsystem,CS4236B}," + "{Turtle Beach,Malibu}," + "{Unknown,Digital PC 5000 Onboard}}"); +#endif + +#ifdef CS4232 +#define IDENT "CS4232" +#else +#define IDENT "CS4236+" +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(cport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(cport, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional)."); +MODULE_PARM_SYNTAX(sb_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); + +struct snd_card_cs4236 { + struct resource *res_sb_port; +#ifdef __ISAPNP__ + struct isapnp_dev *wss; + struct isapnp_dev *ctrl; + struct isapnp_dev *mpu; +#endif +}; + +static snd_card_t *snd_cs4236_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_cs4236_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_cs4236_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \ + } +#define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ + ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \ + } +#define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \ + } + + +#ifdef CS4232 +static struct isapnp_card_id snd_card_pnpids[] __devinitdata = { + /* Philips PCA70PS */ + ISAPNP_CS4232_1('C','S','C',0x0d32,0x0000,0x0010,0xb006), + /* TerraTec Maestro 32/96 (CS4232) */ + ISAPNP_CS4232('C','S','C',0x1a32,0x0000,0x0010,0x0003), + /* HP Omnibook 5500 onboard */ + ISAPNP_CS4232('C','S','C',0x4232,0x0000,0x0002,0x0003), + /* Unnamed CS4236 card (Made in Taiwan) */ + ISAPNP_CS4232('C','S','C',0x4236,0x0000,0x0010,0x0003), + /* Turtle Beach TBS-2000 (CS4232) */ + ISAPNP_CS4232('C','S','C',0x7532,0x0000,0x0010,0xb006), + /* Turtle Beach Tropez Plus (CS4232) */ + ISAPNP_CS4232_1('C','S','C',0x7632,0x0000,0x0010,0xb006), + /* SIC CrystalWave 32 (CS4232) */ + ISAPNP_CS4232('C','S','C',0xf032,0x0000,0x0010,0x0003), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; +#else /* CS4236 */ +static struct isapnp_card_id snd_card_pnpids[] __devinitdata = { + /* Intel Marlin Spike Motherboard - CS4235 */ + ISAPNP_CS4232('C','S','C',0x0225,0x0000,0x0010,0x0003), + /* Intel Marlin Spike Motherboard (#2) - CS4235 */ + ISAPNP_CS4232('C','S','C',0x0225,0x0100,0x0110,0x0103), + /* Genius Sound Maker 3DJ - CS4237B */ + ISAPNP_CS4232('C','S','C',0x0437,0x0000,0x0010,0x0003), + /* Digital PC 5000 Onboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0x0735,0x0000,0x0010), + /* some uknown CS4236B */ + ISAPNP_CS4232('C','S','C',0x0b35,0x0000,0x0010,0x0003), + /* Intel PR440FX Onboard sound */ + ISAPNP_CS4232('C','S','C',0x0b36,0x0000,0x0010,0x0003), + /* CS4235 on mainboard without MPU */ + ISAPNP_CS4232_WOMPU('C','S','C',0x1425,0x0100,0x0110), + /* Gateway E1000 Onboard CS4236B */ + ISAPNP_CS4232('C','S','C',0x1335,0x0000,0x0010,0x0003), + /* HP 6330 Onboard sound */ + ISAPNP_CS4232('C','S','C',0x1525,0x0100,0x0110,0x0103), + /* Crystal Computer TidalWave128 */ + ISAPNP_CS4232('C','S','C',0x1e37,0x0000,0x0010,0x0003), + /* ACER AW37 - CS4235 */ + ISAPNP_CS4232('C','S','C',0x4236,0x0000,0x0010,0x0003), + /* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */ + ISAPNP_CS4232('C','S','C',0x4237,0x0000,0x0010,0x0003), + /* Crystal 3D - CS4237B */ + ISAPNP_CS4232('C','S','C',0x4336,0x0000,0x0010,0x0003), + /* Typhoon Soundsystem PnP - CS4236B */ + ISAPNP_CS4232('C','S','C',0x4536,0x0000,0x0010,0x0003), + /* Crystal CX4235-XQ3 EP - CS4235 */ + ISAPNP_CS4232('C','S','C',0x4625,0x0100,0x0110,0x0103), + /* TerraTec AudioSystem EWS64XL - CS4236B */ + ISAPNP_CS4232('C','S','C',0xa836,0xa800,0xa810,0xa803), + /* TerraTec AudioSystem EWS64XL - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0xa836,0xa800,0xa810), + /* Crystal Semiconductors CS4237B */ + ISAPNP_CS4232('C','S','C',0x4637,0x0000,0x0010,0x0003), + /* NewClear 3D - CX4237B-XQ3 */ + ISAPNP_CS4232('C','S','C',0x4837,0x0000,0x0010,0x0003), + /* Dell Optiplex GX1 - CS4236B */ + ISAPNP_CS4232('C','S','C',0x6835,0x0000,0x0010,0x0003), + /* Dell P410 motherboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0x6835,0x0000,0x0010), + /* Dell Workstation 400 Onboard - CS4236B */ + ISAPNP_CS4232('C','S','C',0x6836,0x0000,0x0010,0x0003), + /* Turtle Beach Malibu - CS4237B */ + ISAPNP_CS4232('C','S','C',0x7537,0x0000,0x0010,0x0003), + /* CS4235 - onboard */ + ISAPNP_CS4232('C','S','C',0x8025,0x0100,0x0110,0x0103), + /* IBM PC 300PL Onboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe836,0x0000,0x0010), + /* IBM Aptiva 2137 E24 Onboard - CS4237B */ + ISAPNP_CS4232('C','S','C',0x8037,0x0000,0x0010,0x0003), + /* IBM IntelliStation M Pro motherboard */ + ISAPNP_CS4232_WOMPU('C','S','C',0xc835,0x0000,0x0010), + /* Guillemot MaxiSound 16 PnP - CS4236B */ + ISAPNP_CS4232('C','S','C',0x9836,0x0000,0x0010,0x0003), + /* Gallant SC-70P */ + ISAPNP_CS4232('C','S','C',0x9837,0x0000,0x0010,0x0003), + /* ACER AW37/Pro - CS4235 */ + ISAPNP_CS4232('C','S','C',0xd925,0x0000,0x0010,0x0003), + /* ACER AW35/Pro - CS4237B */ + ISAPNP_CS4232('C','S','C',0xd937,0x0000,0x0010,0x0003), + /* CS4235 without MPU401 */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe825,0x0100,0x0110), + /* IBM IntelliStation M Pro 6898 11U - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe835,0x0000,0x0010), + /* Some noname CS4236 based card */ + ISAPNP_CS4232('C','S','C',0xe936,0x0000,0x0010,0x0003), + /* CS4236B */ + ISAPNP_CS4232('C','S','C',0xf235,0x0000,0x0010,0x0003), + /* CS4236B */ + ISAPNP_CS4232('C','S','C',0xf238,0x0000,0x0010,0x0003), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; +#endif + +ISAPNP_CARD_TABLE(snd_card_pnpids); + +static int __init snd_card_cs4236_isapnp(int dev, struct snd_card_cs4236 *acard) +{ + const struct isapnp_card_id *id = snd_cs4236_isapnp_id[dev]; + struct isapnp_card *card = snd_cs4236_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->wss->active) { + acard->wss = NULL; + return -EBUSY; + } + acard->ctrl = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->ctrl->active) { + acard->wss = acard->ctrl = NULL; + return -EBUSY; + } + if (id->devs[2].vendor && id->devs[2].function) { + acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->mpu->active) { + acard->wss = acard->ctrl = acard->mpu = NULL; + return -EBUSY; + } + } + + /* WSS initialization */ + pdev = acard->wss; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 4); + if (fm_port[dev] != SNDRV_AUTO_PORT && fm_port[dev] >= 0) + isapnp_resource_change(&pdev->resource[1], fm_port[dev], 4); + if (sb_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], sb_port[dev], 16); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev] < 0 ? 4 : dma2[dev], 1); + if (pdev->activate(pdev)<0) { + printk(KERN_ERR IDENT " isapnp configure failed for WSS (out of resources?)\n"); + return -EBUSY; + } + port[dev] = pdev->resource[0].start; + if (fm_port[dev] >= 0) + fm_port[dev] = pdev->resource[1].start; + sb_port[dev] = pdev->resource[2].start; + irq[dev] = pdev->irq_resource[0].start; + dma1[dev] = pdev->dma_resource[0].start; + dma2[dev] = pdev->dma_resource[1].start == 4 ? -1 : (int)pdev->dma_resource[1].start; + snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n", + port[dev], fm_port[dev], sb_port[dev]); + snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n", + irq[dev], dma1[dev], dma2[dev]); + /* CTRL initialization */ + if (acard->ctrl && cport[dev] >= 0) { + pdev = acard->ctrl; + if (pdev->prepare(pdev) < 0) { + acard->wss->deactivate(acard->wss); + return -EAGAIN; + } + if (cport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], cport[dev], 8); + if (pdev->activate(pdev)<0) { + printk(KERN_ERR IDENT " isapnp configure failed for control (out of resources?)\n"); + acard->wss->deactivate(acard->wss); + return -EBUSY; + } + cport[dev] = pdev->resource[0].start; + snd_printdd("isapnp CTRL: control port=0x%lx\n", cport[dev]); + } + /* MPU initialization */ + if (acard->mpu && mpu_port[dev] >= 0) { + pdev = acard->mpu; + if (pdev->prepare(pdev) < 0) { + acard->wss->deactivate(acard->wss); + acard->ctrl->deactivate(acard->ctrl); + return -EAGAIN; + } + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], mpu_port[dev], 2); + if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0) + isapnp_resource_change(&pdev->irq_resource[0], mpu_irq[dev], 1); + if (pdev->activate(pdev)<0) { + mpu_port[dev] = SNDRV_AUTO_PORT; + mpu_irq[dev] = SNDRV_AUTO_IRQ; + printk(KERN_ERR IDENT " isapnp configure failed for MPU (out of resources?)\n"); + } else { + mpu_port[dev] = pdev->resource[0].start; + if ((pdev->irq_resource[0].flags & IORESOURCE_IRQ) && + mpu_irq[dev] >= 0) { + mpu_irq[dev] = pdev->irq_resource[0].start; + } else { + mpu_irq[dev] = -1; /* disable interrupt */ + } + } + snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]); + } + return 0; +} + +static void snd_card_cs4236_deactivate(struct snd_card_cs4236 *acard) +{ + if (acard->wss) { + acard->wss->deactivate(acard->wss); + acard->wss = NULL; + } + if (acard->ctrl) { + acard->ctrl->deactivate(acard->ctrl); + acard->ctrl = NULL; + } + if (acard->mpu) { + acard->mpu->deactivate(acard->mpu); + acard->mpu = NULL; + } +} +#endif + +static void snd_card_cs4236_free(snd_card_t *card) +{ + struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_cs4236_deactivate(acard); +#endif + if (acard->res_sb_port) { + release_resource(acard->res_sb_port); + kfree_nocheck(acard->res_sb_port); + } + } +} + +static int __init snd_card_cs4236_probe(int dev) +{ + snd_card_t *card; + struct snd_card_cs4236 *acard; + snd_pcm_t *pcm = NULL; + cs4231_t *chip; + opl3_t *opl3; + int err; + +#ifdef __ISAPNP__ + if (!isapnp[dev]) { +#endif + if (port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify port\n"); + return -EINVAL; + } + if (cport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify cport\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_cs4236)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_card_cs4236 *)card->private_data; + card->private_free = snd_card_cs4236_free; +#ifdef __ISAPNP__ + if (isapnp[dev] && (err = snd_card_cs4236_isapnp(dev, acard))<0) { + printk(KERN_ERR "isapnp detection failed and probing for " IDENT " is not supported\n"); + snd_card_free(card); + return -ENXIO; + } +#endif + if (mpu_port[dev] < 0) + mpu_port[dev] = SNDRV_AUTO_PORT; + if (fm_port[dev] < 0) + fm_port[dev] = SNDRV_AUTO_PORT; + if (sb_port[dev] < 0) + sb_port[dev] = SNDRV_AUTO_PORT; + if (sb_port[dev] != SNDRV_AUTO_PORT) + if ((acard->res_sb_port = request_region(sb_port[dev], 16, IDENT " SB")) == NULL) { + printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]); + snd_card_free(card); + return -ENOMEM; + } + +#ifdef CS4232 + if ((err = snd_cs4231_create(card, + port[dev], + cport[dev], + irq[dev], + dma1[dev], + dma2[dev], + CS4231_HW_DETECT, + 0, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + +#else /* CS4236 */ + if ((err = snd_cs4236_create(card, + port[dev], + cport[dev], + irq[dev], + dma1[dev], + dma2[dev], + CS4231_HW_DETECT, + 0, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4236_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } +#endif + + if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (fm_port[dev] != SNDRV_AUTO_PORT) { + if (snd_opl3_create(card, + fm_port[dev], fm_port[dev] + 2, + OPL3_HW_OPL3_CS, 0, &opl3) < 0) { + printk(KERN_ERR IDENT ": OPL3 not detected\n"); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + } + + if (mpu_port[dev] != SNDRV_AUTO_PORT) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, + mpu_port[dev], 0, + mpu_irq[dev], + mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) + printk(KERN_ERR IDENT ": MPU401 not detected\n"); + } + strcpy(card->driver, pcm->name); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", + pcm->name, + chip->port, + irq[dev], + dma1[dev]); + if (dma1[dev] >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4236_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_cs4236_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_cs4236_isapnp_cards[dev] = card; + snd_cs4236_isapnp_id[dev] = id; + res = snd_card_cs4236_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_cs423x_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (snd_card_cs4236_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_card_pnpids, snd_cs4236_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + printk(KERN_ERR IDENT " soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_cs423x_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_cs4236_cards[idx]); +} + +module_init(alsa_card_cs423x_init) +module_exit(alsa_card_cs423x_exit) + +#ifndef MODULE + +/* format is: snd-cs4232=enable,index,id,isapnp,port, + cport,mpu_port,fm_port,sb_port, + irq,mpu_irq,dma1,dma2 */ +/* format is: snd-cs4236=enable,index,id,isapnp,port, + cport,mpu_port,fm_port,sb_port, + irq,mpu_irq,dma1,dma2 */ + +static int __init alsa_card_cs423x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&cport[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&sb_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +#ifdef CS4232 +__setup("snd-cs4232=", alsa_card_cs423x_setup); +#else /* CS4236 */ +__setup("snd-cs4236=", alsa_card_cs423x_setup); +#endif + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4236_lib.c linux/sound/isa/cs423x/cs4236_lib.c --- linux-2.4.21-rc1.orig/sound/isa/cs423x/cs4236_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/cs423x/cs4236_lib.c 2003-02-15 07:52:40.000000000 -0700 @@ -0,0 +1,984 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of CS4235/4236B/4237B/4238B/4239 chips + * + * Note: + * ----- + * + * Bugs: + * ----- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Indirect control registers (CS4236B+) + * + * C0 + * D8: WSS reset (all chips) + * + * C1 (all chips except CS4236) + * D7-D5: version + * D4-D0: chip id + * 11101 - CS4235 + * 01011 - CS4236B + * 01000 - CS4237B + * 01001 - CS4238B + * 11110 - CS4239 + * + * C2 + * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) + * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) + * + * C3 + * D7: 3D Enable (CS4237B) + * D6: 3D Mono Enable (CS4237B) + * D5: 3D Serial Output (CS4237B,CS4238B) + * D4: 3D Enable (CS4235,CS4238B,CS4239) + * + * C4 + * D7: consumer serial port enable (CS4237B,CS4238B) + * D6: channels status block reset (CS4237B,CS4238B) + * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) + * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B) + * + * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) + * D7-D6: first two bits of category code + * D5: lock + * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) + * D2: copy/copyright (0 = copy inhibited) + * D1: 0 = digital audio / 1 = non-digital audio + * + * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) + * D7-D6: sample frequency (0 = 44.1kHz) + * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) + * D4-D0: category code (upper bits) + * + * C7 reserved (must write 0) + * + * C8 wavetable control + * D7: volume control interrupt enable (CS4235,CS4239) + * D6: hardware volume control format (CS4235,CS4239) + * D3: wavetable serial port enable (all chips) + * D2: DSP serial port switch (all chips) + * D1: disable MCLK (all chips) + * D0: force BRESET low (all chips) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips"); +MODULE_LICENSE("GPL"); + +#define chip_t cs4231_t + +/* + * + */ + +static unsigned char snd_cs4236_ext_map[18] = { + /* CS4236_LEFT_LINE */ 0xff, + /* CS4236_RIGHT_LINE */ 0xff, + /* CS4236_LEFT_MIC */ 0xdf, + /* CS4236_RIGHT_MIC */ 0xdf, + /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, + /* CS4236_RIGHT_MIX_CTRL */ 0xe0, + /* CS4236_LEFT_FM */ 0xbf, + /* CS4236_RIGHT_FM */ 0xbf, + /* CS4236_LEFT_DSP */ 0xbf, + /* CS4236_RIGHT_DSP */ 0xbf, + /* CS4236_RIGHT_LOOPBACK */ 0xbf, + /* CS4236_DAC_MUTE */ 0xe0, + /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ + /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ + /* CS4236_LEFT_MASTER */ 0xbf, + /* CS4236_RIGHT_MASTER */ 0xbf, + /* CS4236_LEFT_WAVE */ 0xbf, + /* CS4236_RIGHT_WAVE */ 0xbf +}; + +/* + * + */ + +static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val) +{ + outb(reg, chip->cport + 3); + outb(chip->cimage[reg] = val, chip->cport + 4); +} + +static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg) +{ + outb(reg, chip->cport + 3); + return inb(chip->cport + 4); +} + +/* + * PCM + */ + +#define CLOCKS 8 + +static ratnum_t clocks[CLOCKS] = { + { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 }, + { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 }, + { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 }, + { .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 }, + { .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 }, + { .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 }, + { .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 }, + { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + .nrats = CLOCKS, + .rats = clocks, +}; + +static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); +} + +static unsigned char divisor_to_rate_register(unsigned int divisor) +{ + switch (divisor) { + case 353: return 1; + case 529: return 2; + case 617: return 3; + case 1058: return 4; + case 1764: return 5; + case 2117: return 6; + case 2558: return 7; + default: + snd_runtime_check(divisor >= 21 && divisor <= 192, return 192); + return divisor; + } +} + +static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr) +{ + unsigned long flags; + unsigned char rate = divisor_to_rate_register(params->rate_den); + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set fast playback format change and clean playback FIFO */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10); + snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr) +{ + unsigned long flags; + unsigned char rate = divisor_to_rate_register(params->rate_den); + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set fast capture format change and clean capture FIFO */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20); + snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +#ifdef CONFIG_PM + +static void snd_cs4236_suspend(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) + chip->image[reg] = snd_cs4231_in(chip, reg); + for (reg = 0; reg < 18; reg++) + chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); + for (reg = 2; reg < 9; reg++) + chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4236_resume(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) { + switch (reg) { + case CS4236_EXT_REG: + case CS4231_VERSION: + case 27: /* why? CS4235 - master left */ + case 29: /* why? CS4235 - master right */ + break; + default: + snd_cs4231_out(chip, reg, chip->image[reg]); + break; + } + } + for (reg = 0; reg < 18; reg++) + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); + for (reg = 2; reg < 9; reg++) { + switch (reg) { + case 7: + break; + default: + snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); +} + +#endif /* CONFIG_PM */ + +int snd_cs4236_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + cs4231_t *chip; + unsigned char ver1, ver2; + unsigned int reg; + int err; + + *rchip = NULL; + if (hardware == CS4231_HW_DETECT) + hardware = CS4231_HW_DETECT3; + if (cport < 0x100) { + snd_printk("please, specify control port for CS4236+ chips\n"); + return -ENODEV; + } + if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0) + return err; + + if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) { + snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%lx\n",chip->hardware); + snd_device_free(card, chip); + return -ENODEV; + } +#if 0 + { + int idx; + for (idx = 0; idx < 8; idx++) + snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx)); + for (idx = 0; idx < 9; idx++) + snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx)); + } +#endif + ver1 = snd_cs4236_ctrl_in(chip, 1); + ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2); + if (ver1 != ver2) { + snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport); + snd_device_free(card, chip); + return -ENODEV; + } + snd_cs4236_ctrl_out(chip, 0, 0x00); + snd_cs4236_ctrl_out(chip, 2, 0xff); + snd_cs4236_ctrl_out(chip, 3, 0x00); + snd_cs4236_ctrl_out(chip, 4, 0x80); + snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE); + snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); + snd_cs4236_ctrl_out(chip, 7, 0x00); + /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */ + /* is working with this setup, other hardware should have */ + /* different signal paths and this value should be selectable */ + /* in the future */ + snd_cs4236_ctrl_out(chip, 8, 0x8c); + chip->rate_constraint = snd_cs4236_xrate; + chip->set_playback_format = snd_cs4236_playback_format; + chip->set_capture_format = snd_cs4236_capture_format; +#ifdef CONFIG_PM + chip->suspend = snd_cs4236_suspend; + chip->resume = snd_cs4236_resume; +#endif + + /* initialize extended registers */ + for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]); + + /* initialize compatible but more featured registers */ + snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40); + snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); + snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); + snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); + snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); + snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); + snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff); + snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); + switch (chip->hardware) { + case CS4231_HW_CS4235: + case CS4231_HW_CS4239: + snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff); + snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff); + break; + } + + *rchip = chip; + return 0; +} + +int snd_cs4236_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0) + return err; + pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER + */ + +#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_single, \ + .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_cs4236_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_cs4236_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val; + change = val != chip->eimage[CS4236_REG(reg)]; + snd_cs4236_ext_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_single, \ + .get = snd_cs4236_get_singlec, .put = snd_cs4236_put_singlec, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_cs4236_put_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->cimage[reg] & ~(mask << shift)) | val; + change = val != chip->cimage[reg]; + snd_cs4236_ctrl_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_double, \ + .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_cs4236_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_cs4236_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1; + val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; + change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)]; + snd_cs4236_ext_out(chip, left_reg, val1); + snd_cs4236_ext_out(chip, right_reg, val2); + } else { + val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != chip->eimage[CS4236_REG(left_reg)]; + snd_cs4236_ext_out(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_double, \ + .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_cs4236_put_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4236_ext_out(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_MASTER_DIGITAL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_double, \ + .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \ + .private_value = 71 << 24 } + +static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol) +{ + return (vol < 64) ? 63 - vol : 64 + (71 - vol); +} + +static int snd_cs4236_get_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f); + ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4236_put_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1, val2; + + val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f); + val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f); + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1; + val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2; + change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)]; + snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1); + snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4235_OUTPUT_ACCU(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_double, \ + .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \ + .private_value = 3 << 24 } + +static inline int snd_cs4235_mixer_output_accu_get_volume(int vol) +{ + switch ((vol >> 5) & 3) { + case 0: return 1; + case 1: return 3; + case 2: return 2; + case 3: return 0; + } + return 3; +} + +static inline int snd_cs4235_mixer_output_accu_set_volume(int vol) +{ + switch (vol & 3) { + case 0: return 3 << 5; + case 1: return 0 << 5; + case 2: return 2 << 5; + case 3: return 1 << 5; + } + return 1 << 5; +} + +static int snd_cs4235_get_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]); + ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4235_put_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1, val2; + + val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]); + val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]); + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1; + val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2; + change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; + snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1); + snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_CONTROLS (sizeof(snd_cs4236_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_controls[] = { + +CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_MASTER_DIGITAL("Master Digital Volume", 0), + +CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), +CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1), + +CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), +CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1), + +CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), +CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1), + +CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), + +CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1), +CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), + +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), + +CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), + +CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), +CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), + +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), + +CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1) +}; + +#define CS4235_CONTROLS (sizeof(snd_cs4235_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4235_controls[] = { + +CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), +CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1), + +CS4235_OUTPUT_ACCU("Playback Volume", 0), + +CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_MASTER_DIGITAL("Master Digital Volume", 0), + +CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), + +CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), + +CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), + +CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), + +CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1), +CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0), + +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), + +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), + +CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), + +CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), + +CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), +}; + +#define CS4236_IEC958_ENABLE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4236_info_single, \ + .get = snd_cs4236_get_iec958_switch, .put = snd_cs4236_put_iec958_switch, \ + .private_value = 1 << 16 } + +static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0; +#if 0 + printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", + snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), + snd_cs4236_ctrl_in(chip, 3), + snd_cs4236_ctrl_in(chip, 4), + snd_cs4236_ctrl_in(chip, 5), + snd_cs4236_ctrl_in(chip, 6), + snd_cs4236_ctrl_in(chip, 8)); +#endif + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4236_put_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short enable, val; + + enable = ucontrol->value.integer.value[0] & 1; + + down(&chip->mce_mutex); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1); + change = val != chip->image[CS4231_ALT_FEATURE_1]; + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val); + val = snd_cs4236_ctrl_in(chip, 4) | 0xc0; + snd_cs4236_ctrl_out(chip, 4, val); + udelay(100); + val &= ~0x40; + snd_cs4236_ctrl_out(chip, 4, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + up(&chip->mce_mutex); + +#if 0 + printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", + snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), + snd_cs4236_ctrl_in(chip, 3), + snd_cs4236_ctrl_in(chip, 4), + snd_cs4236_ctrl_in(chip, 5), + snd_cs4236_ctrl_in(chip, 6), + snd_cs4236_ctrl_in(chip, 8)); +#endif + return change; +} + +#define CS4236_IEC958_CONTROLS (sizeof(snd_cs4236_iec958_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_iec958_controls[] = { +CS4236_IEC958_ENABLE("IEC958 Output Enable", 0), +CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0), +CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0), +CS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0), +CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0), +CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0) +}; + +#define CS4236_3D_CONTROLS_CS4235 (sizeof(snd_cs4236_3d_controls_cs4235)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4235[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1) +}; + +#define CS4236_3D_CONTROLS_CS4237 (sizeof(snd_cs4236_3d_controls_cs4237)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4237[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), +CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1), +CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0), +CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) +}; + +#define CS4236_3D_CONTROLS_CS4238 (sizeof(snd_cs4236_3d_controls_cs4238)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4238[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), +CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1), +CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) +}; + +int snd_cs4236_mixer(cs4231_t *chip) +{ + snd_card_t *card; + unsigned int idx, count; + int err; + snd_kcontrol_new_t *kcontrol; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + card = chip->card; + strcpy(card->mixername, snd_cs4231_chip_id(chip)); + + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) { + for (idx = 0; idx < CS4235_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0) + return err; + } + } else { + for (idx = 0; idx < CS4236_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0) + return err; + } + } + switch (chip->hardware) { + case CS4231_HW_CS4235: + case CS4231_HW_CS4239: + count = CS4236_3D_CONTROLS_CS4235; + kcontrol = snd_cs4236_3d_controls_cs4235; + break; + case CS4231_HW_CS4237B: + count = CS4236_3D_CONTROLS_CS4237; + kcontrol = snd_cs4236_3d_controls_cs4237; + break; + case CS4231_HW_CS4238B: + count = CS4236_3D_CONTROLS_CS4238; + kcontrol = snd_cs4236_3d_controls_cs4238; + break; + default: + count = 0; + kcontrol = NULL; + } + for (idx = 0; idx < count; idx++, kcontrol++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0) + return err; + } + if (chip->hardware == CS4231_HW_CS4237B || + chip->hardware == CS4231_HW_CS4238B) { + for (idx = 0; idx < CS4236_IEC958_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0) + return err; + } + } + return 0; +} + +EXPORT_SYMBOL(snd_cs4236_create); +EXPORT_SYMBOL(snd_cs4236_pcm); +EXPORT_SYMBOL(snd_cs4236_mixer); + +/* + * INIT part + */ + +static int __init alsa_cs4236_init(void) +{ + return 0; +} + +static void __exit alsa_cs4236_exit(void) +{ +} + +module_init(alsa_cs4236_init) +module_exit(alsa_cs4236_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/dt019x.c linux/sound/isa/dt019x.c --- linux-2.4.21-rc1.orig/sound/isa/dt019x.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/dt019x.c 2003-03-01 12:04:28.000000000 -0700 @@ -0,0 +1,407 @@ + +/* + dt019x.c - driver for Diamond Technologies DT-0197H based soundcards. + Copyright (C) 1999, 2002 by Massimo Piccioni + + Generalised for soundcards based on DT-0196 and ALS-007 chips + by Jonathan Woithe : June 2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t sb_t + +#define PFX "dt019x: " + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Diamond Technologies DT-019X / Avance Logic ALS-007"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Diamond Technologies DT-019X}," + "{Avance Logic ALS-007}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for DT-019X based soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for dt019x driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for dt019x driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for dt019x driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for dt019x driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for dt019x driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for dt019x driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); + +struct snd_card_dt019x { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; + struct isapnp_dev *devopl; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_dt019x_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_dt019x_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_dt019x_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_dt019x_pnpids[] __devinitdata = { + /* DT197A30 */ + { + ISAPNP_CARD_ID('R','W','B',0x1688), + .devs = { ISAPNP_DEVICE_ID('@','@','@',0x0001), + ISAPNP_DEVICE_ID('@','X','@',0x0001), + ISAPNP_DEVICE_ID('@','H','@',0x0001) } + }, + /* DT0196 / ALS-007 */ + { + ISAPNP_CARD_ID('A','L','S',0x0007), + .devs = { ISAPNP_DEVICE_ID('@','@','@',0x0001), + ISAPNP_DEVICE_ID('@','X','@',0x0001), + ISAPNP_DEVICE_ID('@','H','@',0x0001) } + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_dt019x_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-dt019x" + + +#ifdef __ISAPNP__ +static int __init snd_card_dt019x_isapnp(int dev, struct snd_card_dt019x *acard) +{ + const struct isapnp_card_id *id = snd_dt019x_isapnp_id[dev]; + struct isapnp_card *card = snd_dt019x_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->devopl->active) { + acard->dev = acard->devmpu = acard->devopl = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (!pdev || pdev->prepare(pdev)<0) + return -EAGAIN; + + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + if (dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma8[dev], + 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "DT-019X AUDIO isapnp configure failure\n"); + return -EBUSY; + } + port[dev] = pdev->resource[0].start; + dma8[dev] = pdev->dma_resource[0].start; + irq[dev] = pdev->irq_resource[0].start; + snd_printdd("dt019x: found audio interface: port=0x%lx, irq=0x%lx, dma=0x%lx\n", + port[dev],irq[dev],dma8[dev]); + + pdev = acard->devmpu; + if (!pdev || pdev->prepare(pdev)<0) + return 0; + + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], mpu_port[dev], + 2); + if (mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], mpu_irq[dev], + 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "DT-019X MPU-401 isapnp configure failure\n"); + mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + mpu_port[dev] = pdev->resource[0].start; + mpu_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("dt019x: found MPU-401: port=0x%lx, irq=0x%lx\n", + mpu_port[dev],mpu_irq[dev]); + } + + pdev = acard->devopl; + if (!pdev || pdev->prepare(pdev)<0) + return 0; + + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], fm_port[dev], 4); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "DT-019X OPL3 isapnp configure failure\n"); + fm_port[dev] = -1; + acard->devopl = NULL; + } else { + fm_port[dev] = pdev->resource[0].start; + snd_printdd("dt019x: found OPL3 synth: port=0x%lx\n",fm_port[dev]); + } + + return 0; +} + +static void snd_card_dt019x_deactivate(struct snd_card_dt019x *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } + if (acard->devopl) { + acard->devopl->deactivate(acard->devopl); + acard->devopl = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_dt019x_free(snd_card_t *card) +{ + struct snd_card_dt019x *acard = (struct snd_card_dt019x *)card->private_data; + + if (acard != NULL) { +#ifdef __ISAPNP__ + snd_card_dt019x_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_dt019x_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_dt019x *acard; + opl3_t *opl3; + + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_dt019x))) == NULL) + return -ENOMEM; + acard = (struct snd_card_dt019x *)card->private_data; + card->private_free = snd_card_dt019x_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_dt019x_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + printk(KERN_ERR PFX "you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, port[dev], + irq[dev], + snd_sb16dsp_interrupt, + dma8[dev], + -1, + SB_HW_DT019X, + &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, +/* MPU401_HW_SB,*/ + MPU401_HW_MPU401, + mpu_port[dev], 0, + mpu_irq[dev], + SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", + mpu_port[dev]); + } + + if (fm_port[dev] > 0) { + if (snd_opl3_create(card, + fm_port[dev], + fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx ?\n", + fm_port[dev], fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "DT-019X"); + strcpy(card->shortname, "Diamond Tech. DT-019X"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, chip->name, chip->port, + irq[dev], dma8[dev]); + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_dt019x_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_dt019x_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_dt019x_isapnp_cards[dev] = card; + snd_dt019x_isapnp_id[dev] = id; + res = snd_card_dt019x_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_dt019x_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_dt019x_pnpids, snd_dt019x_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no DT-019X / ALS-007 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_dt019x_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_dt019x_cards[dev]); +} + +module_init(alsa_card_dt019x_init) +module_exit(alsa_card_dt019x_exit) + +#ifndef MODULE + +/* format is: snd-dt019x=enable,index,id, + port,mpu_port,fm_port, + irq,mpu_irq,dma8,dma8_size */ + +static int __init alsa_card_dt019x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-dt019x=", alsa_card_dt019x_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/es1688/Makefile linux/sound/isa/es1688/Makefile --- linux-2.4.21-rc1.orig/sound/isa/es1688/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/es1688/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,25 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _es1688.o + +list-multi := snd-es1688-lib.o snd-es1688.o + +export-objs := es1688_lib.o + +snd-es1688-lib-objs := es1688_lib.o +snd-es1688-objs := es1688.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-es1688-lib.o + +include $(TOPDIR)/Rules.make + +snd-es1688-lib.o: $(snd-es1688-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-lib-objs) + +snd-es1688.o: $(snd-es1688-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/es1688/es1688.c linux/sound/isa/es1688/es1688.c --- linux-2.4.21-rc1.orig/sound/isa/es1688/es1688.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/es1688/es1688.c 2002-10-21 12:28:22.000000000 -0600 @@ -0,0 +1,240 @@ +/* + * Driver for generic ESS AudioDrive ESx688 soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define chip_t es1688_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ESS ESx688 AudioDrive"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," + "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," + "{ESS,ES688 AudioDrive,pnp:ESS6881}," + "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ESx688 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ESx688 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ESx688 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for ESx688 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ESx688 driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for ESx688 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ESx688 driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for ESx688 driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); + +static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_audiodrive_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dmas[] = {1, 3, 0, -1}; + int xirq, xdma, xmpu_irq; + snd_card_t *card; + es1688_t *chip; + opl3_t *opl3; + snd_pcm_t *pcm; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + xirq = irq[dev]; + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + xmpu_irq = mpu_irq[dev]; + xdma = dma8[dev]; + if (xdma == SNDRV_AUTO_DMA) { + if ((xdma = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA\n"); + return -EBUSY; + } + } + + if ((err = snd_es1688_create(card, port[dev], mpu_port[dev], + xirq, xmpu_irq, xdma, + ES1688_HW_AUTO, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { + printk(KERN_ERR "es1688: opl3 not detected at 0x%lx\n", chip->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (xmpu_irq >= 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, + chip->mpu_port, 0, + xmpu_irq, + SA_INTERRUPT, + NULL)) < 0) { + snd_card_free(card); + return err; + } + } + strcpy(card->driver, "ES1688"); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, xirq, xdma); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_audiodrive_cards[dev] = card; + return 0; + +} + +static int __init snd_audiodrive_legacy_auto_probe(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; + port[dev] = xport; + res = snd_audiodrive_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_es1688_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards = 0; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_audiodrive_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "ESS AudioDrive ES1688 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_es1688_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_audiodrive_cards[idx]); +} + +module_init(alsa_card_es1688_init) +module_exit(alsa_card_es1688_exit) + +#ifndef MODULE + +/* format is: snd-es1688=enable,index,id, + port,mpu_port, + irq,mpu_irq, + dma8 */ + +static int __init alsa_card_es1688_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1688=", alsa_card_es1688_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/es1688/es1688_lib.c linux/sound/isa/es1688/es1688_lib.c --- linux-2.4.21-rc1.orig/sound/isa/es1688/es1688_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/es1688/es1688_lib.c 2003-01-31 08:19:50.000000000 -0700 @@ -0,0 +1,1063 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of ESS ES1688/688/488 chip + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ESS ESx688 lowlevel module"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); + +#define chip_t es1688_t + +static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val) +{ + int i; + + for (i = 10000; i; i--) + if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { + outb(val, ES1688P(chip, COMMAND)); + return 1; + } +#ifdef CONFIG_SND_DEBUG + printk("snd_es1688_dsp_command: timeout (0x%x)\n", val); +#endif + return 0; +} + +static int snd_es1688_dsp_get_byte(es1688_t *chip) +{ + int i; + + for (i = 1000; i; i--) + if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) + return inb(ES1688P(chip, READ)); + snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); + return -ENODEV; +} + +static int snd_es1688_write(es1688_t *chip, + unsigned char reg, unsigned char data) +{ + if (!snd_es1688_dsp_command(chip, reg)) + return 0; + return snd_es1688_dsp_command(chip, data); +} + +int snd_es1688_read(es1688_t *chip, unsigned char reg) +{ + /* Read a byte from an extended mode register of ES1688 */ + if (!snd_es1688_dsp_command(chip, 0xc0)) + return -1; + if (!snd_es1688_dsp_command(chip, reg)) + return -1; + return snd_es1688_dsp_get_byte(chip); +} + +void snd_es1688_mixer_write(es1688_t *chip, + unsigned char reg, unsigned char data) +{ + outb(reg, ES1688P(chip, MIXER_ADDR)); + udelay(10); + outb(data, ES1688P(chip, MIXER_DATA)); + udelay(10); +} + +unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg) +{ + unsigned char result; + + outb(reg, ES1688P(chip, MIXER_ADDR)); + udelay(10); + result = inb(ES1688P(chip, MIXER_DATA)); + udelay(10); + return result; +} + +static int snd_es1688_reset(es1688_t *chip) +{ + int i; + + outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ + udelay(10); + outb(0, ES1688P(chip, RESET)); + udelay(30); + for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); + if (inb(ES1688P(chip, READ)) != 0xaa) { + snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); + return -ENODEV; + } + snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ + return 0; +} + +static int snd_es1688_probe(es1688_t *chip) +{ + unsigned long flags; + unsigned short major, minor, hw; + int i; + + /* + * initialization sequence + */ + + spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ + + if (snd_es1688_reset(chip) < 0) { + snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + snd_es1688_dsp_command(chip, 0xe7); /* return identification */ + + for (i = 1000, major = minor = 0; i; i--) { + if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { + if (major == 0) { + major = inb(ES1688P(chip, READ)); + } else { + minor = inb(ES1688P(chip, READ)); + } + } + } + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); + + chip->version = (major << 8) | minor; + if (!chip->version) + return -ENODEV; /* probably SB */ + + hw = ES1688_HW_AUTO; + switch (chip->version & 0xfff0) { + case 0x4880: + snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port); + return -ENODEV; + case 0x6880: + hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688; + break; + default: + snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version); + return -ENODEV; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ + snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* enable joystick, but disable OPL3 */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_es1688_mixer_write(chip, 0x40, 0x01); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + return 0; +} + +static int snd_es1688_init(es1688_t * chip, int enable) +{ + static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; + unsigned long flags; + int cfg, irq_bits, dma, dma_bits, tmp, tmp1; + + /* ok.. setup MPU-401 port and joystick and OPL3 */ + cfg = 0x01; /* enable joystick, but disable OPL3 */ + if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { + tmp = (chip->mpu_port & 0x0f0) >> 4; + if (tmp <= 3) { + switch (chip->mpu_irq) { + case 9: + tmp1 = 4; + break; + case 5: + tmp1 = 5; + break; + case 7: + tmp1 = 6; + break; + case 10: + tmp1 = 7; + break; + default: + tmp1 = 0; + } + if (tmp1) { + cfg |= (tmp << 3) | (tmp1 << 5); + } + } + } +#if 0 + snd_printk("mpu cfg = 0x%x\n", cfg); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_mixer_write(chip, 0x40, cfg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_read(chip, 0xb1); + snd_es1688_read(chip, 0xb2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (enable) { + cfg = 0xf0; /* enable only DMA counter interrupt */ + irq_bits = irqs[chip->irq & 0x0f]; + if (irq_bits < 0) { + snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq); +#if 0 + irq_bits = 0; + cfg = 0x10; +#endif + return -EINVAL; + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + cfg = 0xf0; /* extended mode DMA enable */ + dma = chip->dma8; + if (dma > 3 || dma == 2) { + snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma); +#if 0 + dma_bits = 0; + cfg = 0x00; /* disable all DMA */ +#endif + return -EINVAL; + } else { + dma_bits = dma; + if (dma != 3) + dma_bits++; + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + } else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ + snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_read(chip, 0xb1); + snd_es1688_read(chip, 0xb2); + snd_es1688_reset(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +/* + + */ + +static ratnum_t clocks[2] = { + { + .num = 795444, + .den_min = 1, + .den_max = 128, + .den_step = 1, + }, + { + .num = 397722, + .den_min = 1, + .den_max = 128, + .den_step = 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + .nrats = 2, + .rats = clocks, +}; + +static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int bits, divider; + + if (runtime->rate_num == clocks[0].num) + bits = 256 - runtime->rate_den; + else + bits = 128 - runtime->rate_den; + /* set filter register */ + divider = 256 - 7160000*20/(8*82*runtime->rate); + /* write result to hardware */ + snd_es1688_write(chip, 0xa1, bits); + snd_es1688_write(chip, 0xa2, divider); +} + +static int snd_es1688_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value) +{ + int val; + + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + value = 0x00; + } else if (cmd != SNDRV_PCM_TRIGGER_START) { + return -EINVAL; + } + spin_lock(&chip->reg_lock); + chip->trigger_value = value; + val = snd_es1688_read(chip, 0xb8); + if ((val < 0) || (val & 0x0f) == value) { + spin_unlock(&chip->reg_lock); + return -EINVAL; /* something is wrong */ + } +#if 0 + printk("trigger: val = 0x%x, value = 0x%x\n", val, value); + printk("trigger: pointer = 0x%x\n", snd_dma_pointer(chip->dma8, chip->dma_size)); +#endif + snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); + spin_unlock(&chip->reg_lock); + return 0; +} + +static int snd_es1688_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_es1688_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_reset(chip); + snd_es1688_set_rate(chip, substream); + snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ + snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); + snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ + if (runtime->channels == 1) { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit mono */ + snd_es1688_write(chip, 0xb6, 0x80); + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0xd0); + } else { + /* 16. bit mono */ + snd_es1688_write(chip, 0xb6, 0x00); + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xf4); + } + } else { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit stereo */ + snd_es1688_write(chip, 0xb6, 0x80); + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0x98); + } else { + /* 16. bit stereo */ + snd_es1688_write(chip, 0xb6, 0x00); + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xbc); + } + } + snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); + snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); + snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + count = -count; + snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xa4, (unsigned char) count); + snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + return snd_es1688_trigger(chip, cmd, 0x05); +} + +static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_reset(chip); + snd_es1688_set_rate(chip, substream); + snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); + snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ + snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); + snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ + if (runtime->channels == 1) { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit mono */ + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0xd0); + } else { + /* 16. bit mono */ + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xf4); + } + } else { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit stereo */ + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0x98); + } else { + /* 16. bit stereo */ + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xbc); + } + } + snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); + snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + count = -count; + snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xa4, (unsigned char) count); + snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + return snd_es1688_trigger(chip, cmd, 0x0f); +} + +void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1688_t *chip = snd_magic_cast(es1688_t, dev_id, return); + + if (chip->trigger_value == 0x05) /* ok.. playback is active */ + snd_pcm_period_elapsed(chip->playback_substream); + if (chip->trigger_value == 0x0f) /* ok.. capture is active */ + snd_pcm_period_elapsed(chip->capture_substream); + + inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ +} + +static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->trigger_value != 0x05) + return 0; + ptr = snd_dma_pointer(chip->dma8, chip->dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->trigger_value != 0x0f) + return 0; + ptr = snd_dma_pointer(chip->dma8, chip->dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_es1688_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_es1688_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + + */ + +static int snd_es1688_playback_open(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->capture_substream != NULL) + return -EAGAIN; + chip->playback_substream = substream; + runtime->hw = snd_es1688_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_es1688_capture_open(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->playback_substream != NULL) + return -EAGAIN; + chip->capture_substream = substream; + runtime->hw = snd_es1688_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_es1688_playback_close(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + return 0; +} + +static int snd_es1688_capture_close(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + return 0; +} + +static int snd_es1688_free(es1688_t *chip) +{ + if (chip->res_port) { + snd_es1688_init(chip, 0); + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma8 >= 0) { + disable_dma(chip->dma8); + free_dma(chip->dma8); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1688_dev_free(snd_device_t *device) +{ + es1688_t *chip = snd_magic_cast(es1688_t, device->device_data, return -ENXIO); + return snd_es1688_free(chip); +} + +static const char *snd_es1688_chip_id(es1688_t *chip) +{ + static char tmp[16]; + sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); + return tmp; +} + +int snd_es1688_create(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + int irq, + int mpu_irq, + int dma8, + unsigned short hardware, + es1688_t **rchip) +{ + static snd_device_ops_t ops = { + .dev_free = snd_es1688_dev_free, + }; + + es1688_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(es1688_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma8 = -1; + + if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) { + snd_es1688_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) { + snd_es1688_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma8, "ES1688")) { + snd_es1688_free(chip); + return -EBUSY; + } + chip->dma8 = dma8; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + chip->card = card; + chip->port = port; + mpu_port &= ~0x000f; + if (mpu_port < 0x300 || mpu_port > 0x330) + mpu_port = 0; + chip->mpu_port = mpu_port; + chip->mpu_irq = mpu_irq; + chip->hardware = hardware; + + if ((err = snd_es1688_probe(chip)) < 0) { + snd_es1688_free(chip); + return err; + } + if ((err = snd_es1688_init(chip, 1)) < 0) { + snd_es1688_free(chip); + return err; + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1688_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_es1688_playback_ops = { + .open = snd_es1688_playback_open, + .close = snd_es1688_playback_close, + .ioctl = snd_es1688_ioctl, + .hw_params = snd_es1688_hw_params, + .hw_free = snd_es1688_hw_free, + .prepare = snd_es1688_playback_prepare, + .trigger = snd_es1688_playback_trigger, + .pointer = snd_es1688_playback_pointer, +}; + +static snd_pcm_ops_t snd_es1688_capture_ops = { + .open = snd_es1688_capture_open, + .close = snd_es1688_capture_close, + .ioctl = snd_es1688_ioctl, + .hw_params = snd_es1688_hw_params, + .hw_free = snd_es1688_hw_free, + .prepare = snd_es1688_capture_prepare, + .trigger = snd_es1688_capture_trigger, + .pointer = snd_es1688_capture_pointer, +}; + +static void snd_es1688_pcm_free(snd_pcm_t *pcm) +{ + es1688_t *chip = snd_magic_cast(es1688_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_es1688_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + sprintf(pcm->name, snd_es1688_chip_id(chip)); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[9] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; + return 0; +} + +static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval, nval; + int change; + + if (ucontrol->value.enumerated.item[0] > 8) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); + nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); + change = nval != oval; + if (change) + snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_es1688_info_single, \ + .get = snd_es1688_get_single, .put = snd_es1688_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char oval, nval; + + nval = (ucontrol->value.integer.value[0] & mask); + if (invert) + nval = mask - nval; + nval <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_es1688_mixer_read(chip, reg); + nval = (oval & ~(mask << shift)) | nval; + change = nval != oval; + if (change) + snd_es1688_mixer_write(chip, reg, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_es1688_info_double, \ + .get = snd_es1688_get_double, .put = snd_es1688_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg < 0xa0) + left = snd_es1688_mixer_read(chip, left_reg); + else + left = snd_es1688_read(chip, left_reg); + if (left_reg != right_reg) { + if (right_reg < 0xa0) + right = snd_es1688_mixer_read(chip, right_reg); + else + right = snd_es1688_read(chip, right_reg); + } else + right = left; + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + if (left_reg < 0xa0) + oval1 = snd_es1688_mixer_read(chip, left_reg); + else + oval1 = snd_es1688_read(chip, left_reg); + if (right_reg < 0xa0) + oval2 = snd_es1688_mixer_read(chip, right_reg); + else + oval2 = snd_es1688_read(chip, right_reg); + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + if (change) { + if (left_reg < 0xa0) + snd_es1688_mixer_write(chip, left_reg, val1); + else + snd_es1688_write(chip, left_reg, val1); + if (right_reg < 0xa0) + snd_es1688_mixer_write(chip, right_reg, val1); + else + snd_es1688_write(chip, right_reg, val1); + } + } else { + if (left_reg < 0xa0) + oval1 = snd_es1688_mixer_read(chip, left_reg); + else + oval1 = snd_es1688_read(chip, left_reg); + val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval1; + if (change) { + if (left_reg < 0xa0) + snd_es1688_mixer_write(chip, left_reg, val1); + else + snd_es1688_write(chip, left_reg, val1); + } + + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_CONTROLS (sizeof(snd_es1688_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_es1688_controls[] = { +ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), +ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), +ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), +ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_es1688_info_mux, + .get = snd_es1688_get_mux, + .put = snd_es1688_put_mux, +}, +}; + +#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) + +static unsigned char snd_es1688_init_table[][2] = { + { ES1688_MASTER_DEV, 0 }, + { ES1688_PCM_DEV, 0 }, + { ES1688_LINE_DEV, 0 }, + { ES1688_CD_DEV, 0 }, + { ES1688_FM_DEV, 0 }, + { ES1688_MIC_DEV, 0 }, + { ES1688_AUX_DEV, 0 }, + { ES1688_SPEAKER_DEV, 0 }, + { ES1688_RECLEV_DEV, 0 }, + { ES1688_REC_DEV, 0x17 } +}; + +int snd_es1688_mixer(es1688_t *chip) +{ + snd_card_t *card; + unsigned int idx; + int err; + unsigned char reg, val; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_es1688_chip_id(chip)); + + for (idx = 0; idx < ES1688_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) + return err; + } + for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { + reg = snd_es1688_init_table[idx][0]; + val = snd_es1688_init_table[idx][1]; + if (reg < 0xa0) + snd_es1688_mixer_write(chip, reg, val); + else + snd_es1688_write(chip, reg, val); + } + return 0; +} + +EXPORT_SYMBOL(snd_es1688_mixer_write); +EXPORT_SYMBOL(snd_es1688_mixer_read); +EXPORT_SYMBOL(snd_es1688_interrupt); +EXPORT_SYMBOL(snd_es1688_create); +EXPORT_SYMBOL(snd_es1688_pcm); +EXPORT_SYMBOL(snd_es1688_mixer); + +/* + * INIT part + */ + +static int __init alsa_es1688_init(void) +{ + return 0; +} + +static void __exit alsa_es1688_exit(void) +{ +} + +module_init(alsa_es1688_init) +module_exit(alsa_es1688_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/es18xx.c linux/sound/isa/es18xx.c --- linux-2.4.21-rc1.orig/sound/isa/es18xx.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/es18xx.c 2003-03-01 12:04:28.000000000 -0700 @@ -0,0 +1,2308 @@ +/* + * Driver for generic ESS AudioDrive ES18xx soundcards + * Copyright (c) by Christian Fischbach + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* GENERAL NOTES: + * + * BUGS: + * - There are pops (we can't delay in trigger function, cause midlevel + * often need to trigger down and then up very quickly). + * Any ideas? + * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. + */ + +/* + * ES1868 NOTES: + * - The chip has one half duplex pcm (with very limited full duplex support). + * + * - Duplex stereophonic sound is impossible. + * - Record and playback must share the same frequency rate. + * + * - The driver use dma2 for playback and dma1 for capture. + */ + +/* + * ES1869 NOTES: + * + * - there are a first full duplex pcm and a second playback only pcm + * (incompatible with first pcm capture) + * + * - there is support for the capture volume and ESS Spatializer 3D effect. + * + * - contrarily to some pages in DS_1869.PDF the rates can be set + * independently. + * + * BUGS: + * + * - There is a major trouble I noted: + * + * using both channel for playback stereo 16 bit samples at 44100 Hz + * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. + * + * The same happens using Audio1 for captureing. + * + * The Windows driver does not suffer of this (although it use Audio1 + * only for captureing). I'm unable to discover why. + * + */ + + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define PFX "es18xx: " + +struct _snd_es18xx { + unsigned long port; /* port of ESS chip */ + unsigned long mpu_port; /* MPU-401 port of ESS chip */ + unsigned long fm_port; /* FM port */ + unsigned long ctrl_port; /* Control port of ESS chip */ + struct resource *res_port; + struct resource *res_mpu_port; + struct resource *res_ctrl_port; + int irq; /* IRQ number of ESS chip */ + int dma1; /* DMA1 */ + int dma2; /* DMA2 */ + unsigned short version; /* version of ESS chip */ + int caps; /* Chip capabilities */ + unsigned short audio2_vol; /* volume level of audio2 */ + + unsigned short active; /* active channel mask */ + unsigned int dma1_size; + unsigned int dma2_size; + unsigned int dma1_shift; + unsigned int dma2_shift; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_a_substream; + snd_pcm_substream_t *capture_a_substream; + snd_pcm_substream_t *playback_b_substream; + + snd_rawmidi_t *rmidi; + + snd_kcontrol_t *hw_volume; + snd_kcontrol_t *hw_switch; + snd_kcontrol_t *master_volume; + snd_kcontrol_t *master_switch; + + spinlock_t reg_lock; + spinlock_t mixer_lock; + spinlock_t ctrl_lock; +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + unsigned char pm_reg; +#endif +}; + +#define AUDIO1_IRQ 0x01 +#define AUDIO2_IRQ 0x02 +#define HWV_IRQ 0x04 +#define MPU_IRQ 0x08 + +#define ES18XX_PCM2 0x0001 /* Has two useable PCM */ +#define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */ +#define ES18XX_RECMIX 0x0004 /* Has record mixer */ +#define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */ +#define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ +#define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ +#define ES18XX_AUXB 0x0040 /* AuxB mixer control */ +#define ES18XX_HWV 0x0080 /* Has hardware volume */ +#define ES18XX_MONO 0x0100 /* Mono_in mixer control */ +#define ES18XX_I2S 0x0200 /* I2S mixer control */ +#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ +#define ES18XX_CONTROL 0x0800 /* Has control ports */ + +/* Power Management */ +#define ES18XX_PM 0x07 +#define ES18XX_PM_GPO0 0x01 +#define ES18XX_PM_GPO1 0x02 +#define ES18XX_PM_PDR 0x04 +#define ES18XX_PM_ANA 0x08 +#define ES18XX_PM_FM 0x020 +#define ES18XX_PM_SUS 0x080 + +typedef struct _snd_es18xx es18xx_t; + +#define chip_t es18xx_t + +/* Lowlevel */ + +#define DAC1 0x01 +#define ADC1 0x02 +#define DAC2 0x04 +#define MILLISECOND 10000 + +static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val) +{ + int i; + + for(i = MILLISECOND; i; i--) + if ((inb(chip->port + 0x0C) & 0x80) == 0) { + outb(val, chip->port + 0x0C); + return 0; + } + snd_printk("dsp_command: timeout (0x%x)\n", val); + return -EINVAL; +} + +static int snd_es18xx_dsp_get_byte(es18xx_t *chip) +{ + int i; + + for(i = MILLISECOND/10; i; i--) + if (inb(chip->port + 0x0C) & 0x40) + return inb(chip->port + 0x0A); + snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A)); + return -ENODEV; +} + +#undef REG_DEBUG + +static int snd_es18xx_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, data); + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x set to %02x\n", reg, data); +#endif + return ret; +} + +static int snd_es18xx_read(es18xx_t *chip, unsigned char reg) +{ + unsigned long flags; + int ret, data; + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, 0xC0); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + data = snd_es18xx_dsp_get_byte(chip); + ret = data; +#ifdef REG_DEBUG + snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret); +#endif + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +/* Return old value */ +static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + int ret; + unsigned char old, new, oval; + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, 0xC0); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_get_byte(chip); + if (ret < 0) { + goto end; + } + old = ret; + oval = old & mask; + if (val != oval) { + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + new = (old & ~mask) | (val & mask); + ret = snd_es18xx_dsp_command(chip, new); + if (ret < 0) + goto end; +#ifdef REG_DEBUG + snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret); +#endif + } + ret = oval; + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +inline void snd_es18xx_mixer_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + outb(data, chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x set to %02x\n", reg, data); +#endif +} + +inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg) +{ + unsigned long flags; + int data; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + data = inb(chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x now is %02x\n", reg, data); +#endif + return data; +} + +/* Return old value */ +static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + unsigned char old, new, oval; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + old = inb(chip->port + 0x05); + oval = old & mask; + if (val != oval) { + new = (old & ~mask) | (val & mask); + outb(new, chip->port + 0x05); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return oval; +} + +static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg, + unsigned char mask) +{ + int old, expected, new; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + old = inb(chip->port + 0x05); + expected = old ^ mask; + outb(expected, chip->port + 0x05); + new = inb(chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new); +#endif + return expected == new; +} + + +static int snd_es18xx_reset(es18xx_t *chip) +{ + int i; + outb(0x03, chip->port + 0x06); + inb(chip->port + 0x06); + outb(0x00, chip->port + 0x06); + for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); + if (inb(chip->port + 0x0A) != 0xAA) + return -1; + return 0; +} + +static int snd_es18xx_reset_fifo(es18xx_t *chip) +{ + outb(0x02, chip->port + 0x06); + inb(chip->port + 0x06); + outb(0x00, chip->port + 0x06); + return 0; +} + +static ratnum_t new_clocks[2] = { + { + .num = 793800, + .den_min = 1, + .den_max = 128, + .den_step = 1, + }, + { + .num = 768000, + .den_min = 1, + .den_max = 128, + .den_step = 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = { + .nrats = 2, + .rats = new_clocks, +}; + +static ratnum_t old_clocks[2] = { + { + .num = 795444, + .den_min = 1, + .den_max = 128, + .den_step = 1, + }, + { + .num = 397722, + .den_min = 1, + .den_max = 128, + .den_step = 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks = { + .nrats = 2, + .rats = old_clocks, +}; + + +static void snd_es18xx_rate_set(es18xx_t *chip, + snd_pcm_substream_t *substream, + int mode) +{ + unsigned int bits, div0; + snd_pcm_runtime_t *runtime = substream->runtime; + if (chip->caps & ES18XX_NEW_RATE) { + if (runtime->rate_num == new_clocks[0].num) + bits = 128 - runtime->rate_den; + else + bits = 256 - runtime->rate_den; + } else { + if (runtime->rate_num == old_clocks[0].num) + bits = 256 - runtime->rate_den; + else + bits = 128 - runtime->rate_den; + } + + /* set filter register */ + div0 = 256 - 7160000*20/(8*82*runtime->rate); + + if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { + snd_es18xx_mixer_write(chip, 0x70, bits); + snd_es18xx_mixer_write(chip, 0x72, div0); + } else { + snd_es18xx_write(chip, 0xA1, bits); + snd_es18xx_write(chip, 0xA2, div0); + } +} + +static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int shift, err; + + shift = 0; + if (params_channels(hw_params) == 2) + shift++; + if (snd_pcm_format_width(params_format(hw_params)) == 16) + shift++; + + if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { + if ((chip->caps & ES18XX_DUPLEX_MONO) && + (chip->capture_a_substream) && + params_channels(hw_params) != 1) { + _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); + return -EBUSY; + } + chip->dma2_shift = shift; + } else { + chip->dma1_shift = shift; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es18xx_pcm_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_es18xx_playback1_prepare(es18xx_t *chip, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma2_size = size; + + snd_es18xx_rate_set(chip, substream, DAC2); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_mixer_write(chip, 0x74, count & 0xff); + snd_es18xx_mixer_write(chip, 0x76, count >> 8); + + /* Set format */ + snd_es18xx_mixer_bits(chip, 0x7A, 0x07, + ((runtime->channels == 1) ? 0x00 : 0x02) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04)); + + /* Set DMA controller */ + snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_playback1_trigger(es18xx_t *chip, + snd_pcm_substream_t * substream, + int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (chip->active & DAC2) + return 0; + chip->active |= DAC2; + /* Start DMA */ + if (chip->dma2 >= 4) + snd_es18xx_mixer_write(chip, 0x78, 0xb3); + else + snd_es18xx_mixer_write(chip, 0x78, 0x93); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(100000); + if (chip->caps & ES18XX_PCM2) + /* Restore Audio 2 volume */ + snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol); + else + /* Enable PCM output */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (!(chip->active & DAC2)) + return 0; + chip->active &= ~DAC2; + /* Stop DMA */ + snd_es18xx_mixer_write(chip, 0x78, 0x00); +#ifdef AVOID_POPS + udelay(25000); + if (chip->caps & ES18XX_PCM2) + /* Set Audio 2 volume to 0 */ + snd_es18xx_mixer_write(chip, 0x7C, 0); + else + /* Disable PCM output */ + snd_es18xx_dsp_command(chip, 0xD3); +#endif + break; + default: + return -EINVAL; + } + + return 0; +} + +static int snd_es18xx_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int shift, err; + + shift = 0; + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->playback_a_substream && + params_channels(hw_params) != 1) { + _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); + return -EBUSY; + } + if (params_channels(hw_params) == 2) + shift++; + if (snd_pcm_format_width(params_format(hw_params)) == 16) + shift++; + chip->dma1_shift = shift; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es18xx_capture_prepare(snd_pcm_substream_t *substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + + snd_es18xx_reset_fifo(chip); + + /* Set stereo/mono */ + snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); + + snd_es18xx_rate_set(chip, substream, ADC1); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_write(chip, 0xA4, count & 0xff); + snd_es18xx_write(chip, 0xA5, count >> 8); + +#ifdef AVOID_POPS + udelay(100000); +#endif + + /* Set format */ + snd_es18xx_write(chip, 0xB7, + snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); + snd_es18xx_write(chip, 0xB7, 0x90 | + ((runtime->channels == 1) ? 0x40 : 0x08) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); + + /* Set DMA controler */ + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (chip->active & ADC1) + return 0; + chip->active |= ADC1; + /* Start DMA */ + snd_es18xx_write(chip, 0xB8, 0x0f); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (!(chip->active & ADC1)) + return 0; + chip->active &= ~ADC1; + /* Stop DMA */ + snd_es18xx_write(chip, 0xB8, 0x00); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int snd_es18xx_playback2_prepare(es18xx_t *chip, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + + snd_es18xx_reset_fifo(chip); + + /* Set stereo/mono */ + snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); + + snd_es18xx_rate_set(chip, substream, DAC1); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_write(chip, 0xA4, count & 0xff); + snd_es18xx_write(chip, 0xA5, count >> 8); + + /* Set format */ + snd_es18xx_write(chip, 0xB6, + snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00); + snd_es18xx_write(chip, 0xB7, + snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); + snd_es18xx_write(chip, 0xB7, 0x90 | + (runtime->channels == 1 ? 0x40 : 0x08) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); + + /* Set DMA controler */ + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_playback2_trigger(es18xx_t *chip, + snd_pcm_substream_t *substream, + int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (chip->active & DAC1) + return 0; + chip->active |= DAC1; + /* Start DMA */ + snd_es18xx_write(chip, 0xB8, 0x05); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(100000); + /* Enable Audio 1 */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (!(chip->active & DAC1)) + return 0; + chip->active &= ~DAC1; + /* Stop DMA */ + snd_es18xx_write(chip, 0xB8, 0x00); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(25000); + /* Disable Audio 1 */ + snd_es18xx_dsp_command(chip, 0xD3); +#endif + break; + default: + return -EINVAL; + } + + return 0; +} + +static int snd_es18xx_playback_prepare(snd_pcm_substream_t *substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) + return snd_es18xx_playback1_prepare(chip, substream); + else + return snd_es18xx_playback2_prepare(chip, substream); +} + +static int snd_es18xx_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) + return snd_es18xx_playback1_trigger(chip, substream, cmd); + else + return snd_es18xx_playback2_trigger(chip, substream, cmd); +} + +static void snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, dev_id, return); + unsigned char status; + + + if (chip->caps & ES18XX_CONTROL) { + /* Read Interrupt status */ + status = inb(chip->ctrl_port + 6); + } else { + /* Read Interrupt status */ + status = snd_es18xx_mixer_read(chip, 0x7f) >> 4; + } +#if 0 + else { + status = 0; + if (inb(chip->port + 0x0C) & 0x01) + status |= AUDIO1_IRQ; + if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80) + status |= AUDIO2_IRQ; + if ((chip->caps & ES18XX_HWV) && + snd_es18xx_mixer_read(chip, 0x64) & 0x10) + status |= HWV_IRQ; + } +#endif + + /* Audio 1 & Audio 2 */ + if (status & AUDIO2_IRQ) { + if (chip->active & DAC2) + snd_pcm_period_elapsed(chip->playback_a_substream); + /* ack interrupt */ + snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00); + } + if (status & AUDIO1_IRQ) { + /* ok.. capture is active */ + if (chip->active & ADC1) + snd_pcm_period_elapsed(chip->capture_a_substream); + /* ok.. playback2 is active */ + else if (chip->active & DAC1) + snd_pcm_period_elapsed(chip->playback_b_substream); + /* ack interrupt */ + inb(chip->port + 0x0E); + } + + /* MPU */ + if ((status & MPU_IRQ) && chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + + /* Hardware volume */ + if (status & HWV_IRQ) { + int split = snd_es18xx_mixer_read(chip, 0x64) & 0x80; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); + if (!split) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + /* ack interrupt */ + snd_es18xx_mixer_write(chip, 0x66, 0x00); + } + +} + +static snd_pcm_uframes_t snd_es18xx_playback_pointer(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int pos; + + if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { + if (!(chip->active & DAC2)) + return 0; + pos = snd_dma_pointer(chip->dma2, chip->dma2_size); + return pos >> chip->dma2_shift; + } else { + if (!(chip->active & DAC1)) + return 0; + pos = snd_dma_pointer(chip->dma1, chip->dma1_size); + return pos >> chip->dma1_shift; + } +} + +static snd_pcm_uframes_t snd_es18xx_capture_pointer(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int pos; + + if (!(chip->active & ADC1)) + return 0; + pos = snd_dma_pointer(chip->dma1, chip->dma1_size); + return pos >> chip->dma1_shift; +} + +static snd_pcm_hardware_t snd_es18xx_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_es18xx_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_es18xx_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->capture_a_substream && + chip->capture_a_substream->runtime->channels != 1) + return -EAGAIN; + chip->playback_a_substream = substream; + } else if (substream->number <= 1) { + if (chip->capture_a_substream) + return -EAGAIN; + chip->playback_b_substream = substream; + } else { + snd_BUG(); + return -EINVAL; + } + substream->runtime->hw = snd_es18xx_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); + return 0; +} + +static int snd_es18xx_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (chip->playback_b_substream) + return -EAGAIN; + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->playback_a_substream && + chip->playback_a_substream->runtime->channels != 1) + return -EAGAIN; + chip->capture_a_substream = substream; + substream->runtime->hw = snd_es18xx_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); + return 0; +} + +static int snd_es18xx_playback_close(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) + chip->playback_a_substream = NULL; + else + chip->playback_b_substream = NULL; + + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_es18xx_capture_close(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_a_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +/* + * MIXER part + */ + +static int snd_es18xx_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es18xx_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es18xx_mixer_read(chip, 0x1c) & 0x07; + return 0; +} + +static int snd_es18xx_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = ucontrol->value.enumerated.item[0]; + + if (val > 7) + return -EINVAL; + return snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val; +} + +static int snd_es18xx_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es18xx_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = snd_es18xx_mixer_read(chip, 0x50); + ucontrol->value.integer.value[0] = !!(val & 8); + return 0; +} + +static int snd_es18xx_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char oval, nval; + int change; + nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; + oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c; + change = nval != oval; + if (change) { + snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04); + snd_es18xx_mixer_write(chip, 0x50, nval); + } + return change; +} + +static int snd_es18xx_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 63; + return 0; +} + +static int snd_es18xx_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f; + ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f; + return 0; +} + +static int snd_es18xx_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es18xx_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40); + ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40); + return 0; +} + +static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_volume = NULL; + chip->master_switch = NULL; + chip->hw_volume = NULL; + chip->hw_switch = NULL; +} + +static int snd_es18xx_reg_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg < 0xa0) + return snd_es18xx_mixer_bits(chip, reg, mask, val); + else + return snd_es18xx_bits(chip, reg, mask, val); +} + +static int snd_es18xx_reg_read(es18xx_t *chip, unsigned char reg) +{ + if (reg < 0xa0) + return snd_es18xx_mixer_read(chip, reg); + else + return snd_es18xx_read(chip, reg); +} + +#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_es18xx_info_single, \ + .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es18xx_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int val; + + val = snd_es18xx_reg_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es18xx_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + mask <<= shift; + val <<= shift; + return snd_es18xx_reg_bits(chip, reg, mask, val) != val; +} + +#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_es18xx_info_double, \ + .get = snd_es18xx_get_double, .put = snd_es18xx_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es18xx_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + left = snd_es18xx_reg_read(chip, left_reg); + if (left_reg != right_reg) + right = snd_es18xx_reg_read(chip, right_reg); + else + right = left; + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es18xx_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, mask1, mask2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + mask1 = mask << shift_left; + mask2 = mask << shift_right; + if (left_reg != right_reg) { + change = 0; + if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1) + change = 1; + if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2) + change = 1; + } else { + change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, + val1 | val2) != (val1 | val2)); + } + return change; +} + +static snd_kcontrol_new_t snd_es18xx_base_controls[] = { +ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), +ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), +ES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), +ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), +ES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), +ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0), +ES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), +ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_es18xx_info_mux, + .get = snd_es18xx_get_mux, + .put = snd_es18xx_put_mux, +} +}; + +static snd_kcontrol_new_t snd_es18xx_mono_in_control = +ES18XX_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0); + +static snd_kcontrol_new_t snd_es18xx_recmix_controls[] = { +ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), +ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), +ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), +ES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), +ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), +ES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), +ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0) +}; + +static snd_kcontrol_new_t snd_es18xx_pcm1_controls[] = { +ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0), +}; + +static snd_kcontrol_new_t snd_es18xx_pcm2_controls[] = { +ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), +ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0) +}; + +static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = { +ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "3D Control - Switch", + .info = snd_es18xx_info_spatializer_enable, + .get = snd_es18xx_get_spatializer_enable, + .put = snd_es18xx_put_spatializer_enable, +} +}; + +static snd_kcontrol_new_t snd_es18xx_micpre1_control = +ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0); + +static snd_kcontrol_new_t snd_es18xx_micpre2_control = +ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0); + +static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Hardware Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_es18xx_info_hw_volume, + .get = snd_es18xx_get_hw_volume, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Hardware Master Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_es18xx_info_hw_switch, + .get = snd_es18xx_get_hw_switch, +}, +ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), +}; + +#if 0 +static int __init snd_es18xx_config_read(es18xx_t *chip, unsigned char reg) +{ + int data; + unsigned long flags; + spin_lock_irqsave(&chip->ctrl_lock, flags); + outb(reg, chip->ctrl_port); + data = inb(chip->ctrl_port + 1); + spin_unlock_irqrestore(&chip->ctrl_lock, flags); + return data; +} +#endif + +static void __init snd_es18xx_config_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + /* No need for spinlocks, this function is used only in + otherwise protected init code */ + outb(reg, chip->ctrl_port); + outb(data, chip->ctrl_port + 1); +#ifdef REG_DEBUG + snd_printk("Config reg %02x set to %02x\n", reg, data); +#endif +} + +static int __init snd_es18xx_initialize(es18xx_t *chip) +{ + int mask = 0; + + /* enable extended mode */ + snd_es18xx_dsp_command(chip, 0xC6); + /* Reset mixer registers */ + snd_es18xx_mixer_write(chip, 0x00, 0x00); + + /* Audio 1 DMA demand mode (4 bytes/request) */ + snd_es18xx_write(chip, 0xB9, 2); + if (chip->caps & ES18XX_CONTROL) { + /* Hardware volume IRQ */ + snd_es18xx_config_write(chip, 0x27, chip->irq); + if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { + /* FM I/O */ + snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8); + snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff); + } + if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { + /* MPU-401 I/O */ + snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8); + snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff); + /* MPU-401 IRQ */ + snd_es18xx_config_write(chip, 0x28, chip->irq); + } + /* Audio1 IRQ */ + snd_es18xx_config_write(chip, 0x70, chip->irq); + /* Audio2 IRQ */ + snd_es18xx_config_write(chip, 0x72, chip->irq); + /* Audio1 DMA */ + snd_es18xx_config_write(chip, 0x74, chip->dma1); + /* Audio2 DMA */ + snd_es18xx_config_write(chip, 0x75, chip->dma2); + + /* Enable Audio 1 IRQ */ + snd_es18xx_write(chip, 0xB1, 0x50); + /* Enable Audio 2 IRQ */ + snd_es18xx_mixer_write(chip, 0x7A, 0x40); + /* Enable Audio 1 DMA */ + snd_es18xx_write(chip, 0xB2, 0x50); + /* Enable MPU and hardware volume interrupt */ + snd_es18xx_mixer_write(chip, 0x64, 0x42); + } + else { + int irqmask, dma1mask, dma2mask; + switch (chip->irq) { + case 2: + case 9: + irqmask = 0; + break; + case 5: + irqmask = 1; + break; + case 7: + irqmask = 2; + break; + case 10: + irqmask = 3; + break; + default: + snd_printk("invalid irq %d\n", chip->irq); + return -ENODEV; + } + switch (chip->dma1) { + case 0: + dma1mask = 1; + break; + case 1: + dma1mask = 2; + break; + case 3: + dma1mask = 3; + break; + default: + snd_printk("invalid dma1 %d\n", chip->dma1); + return -ENODEV; + } + switch (chip->dma2) { + case 0: + dma2mask = 0; + break; + case 1: + dma2mask = 1; + break; + case 3: + dma2mask = 2; + break; + case 5: + dma2mask = 3; + break; + default: + snd_printk("invalid dma2 %d\n", chip->dma2); + return -ENODEV; + } + + /* Enable and set Audio 1 IRQ */ + snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); + /* Enable and set Audio 1 DMA */ + snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); + /* Set Audio 2 DMA */ + snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); + /* Enable Audio 2 IRQ and DMA + Set capture mixer input */ + snd_es18xx_mixer_write(chip, 0x7A, 0x68); + /* Enable and set hardware volume interrupt */ + snd_es18xx_mixer_write(chip, 0x64, 0x06); + if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { + /* MPU401 share irq with audio + Joystick enabled + FM enabled */ + snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1); + } + snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); + } + if (chip->caps & ES18XX_NEW_RATE) { + /* Change behaviour of register A1 + 4x oversampling + 2nd channel DAC asynchronous */ + snd_es18xx_mixer_write(chip, 0x71, 0x32); + } + if (!(chip->caps & ES18XX_PCM2)) { + /* Enable DMA FIFO */ + snd_es18xx_write(chip, 0xB7, 0x80); + } + if (chip->caps & ES18XX_SPATIALIZER) { + /* Set spatializer parameters to recommended values */ + snd_es18xx_mixer_write(chip, 0x54, 0x8f); + snd_es18xx_mixer_write(chip, 0x56, 0x95); + snd_es18xx_mixer_write(chip, 0x58, 0x94); + snd_es18xx_mixer_write(chip, 0x5a, 0x80); + } + /* Mute input source */ + if (chip->caps & ES18XX_MUTEREC) + mask = 0x10; + if (chip->caps & ES18XX_RECMIX) + snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); + else { + snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); + snd_es18xx_write(chip, 0xb4, 0x00); + } +#ifndef AVOID_POPS + /* Enable PCM output */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + + return 0; +} + +static int __init snd_es18xx_identify(es18xx_t *chip) +{ + int hi,lo; + + /* reset */ + if (snd_es18xx_reset(chip) < 0) { + snd_printk("reset at 0x%lx failed!!!\n", chip->port); + return -ENODEV; + } + + snd_es18xx_dsp_command(chip, 0xe7); + hi = snd_es18xx_dsp_get_byte(chip); + if (hi < 0) { + return hi; + } + lo = snd_es18xx_dsp_get_byte(chip); + if ((lo & 0xf0) != 0x80) { + return -ENODEV; + } + if (hi == 0x48) { + chip->version = 0x488; + return 0; + } + if (hi != 0x68) { + return -ENODEV; + } + if ((lo & 0x0f) < 8) { + chip->version = 0x688; + return 0; + } + + outb(0x40, chip->port + 0x04); + hi = inb(chip->port + 0x05); + lo = inb(chip->port + 0x05); + if (hi != lo) { + chip->version = hi << 8 | lo; + chip->ctrl_port = inb(chip->port + 0x05) << 8; + chip->ctrl_port += inb(chip->port + 0x05); + + if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) + return -EBUSY; + + return 0; + } + + /* If has Hardware volume */ + if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { + /* If has Audio2 */ + if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { + /* If has volume count */ + if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { + chip->version = 0x1887; + } else { + chip->version = 0x1888; + } + } else { + chip->version = 0x1788; + } + } + else + chip->version = 0x1688; + return 0; +} + +static int __init snd_es18xx_probe(es18xx_t *chip) +{ + if (snd_es18xx_identify(chip) < 0) { + printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); + return -ENODEV; + } + + switch (chip->version) { + case 0x1868: + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1869: + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1878: + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1879: + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1887: + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; + break; + case 0x1888: + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; + break; + default: + snd_printk("[0x%lx] unsupported chip ES%x\n", + chip->port, chip->version); + return -ENODEV; + } + + snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); + + if (chip->dma1 == chip->dma2) + chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME); + + return snd_es18xx_initialize(chip); +} + +static snd_pcm_ops_t snd_es18xx_playback_ops = { + .open = snd_es18xx_playback_open, + .close = snd_es18xx_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es18xx_playback_hw_params, + .hw_free = snd_es18xx_pcm_hw_free, + .prepare = snd_es18xx_playback_prepare, + .trigger = snd_es18xx_playback_trigger, + .pointer = snd_es18xx_playback_pointer, +}; + +static snd_pcm_ops_t snd_es18xx_capture_ops = { + .open = snd_es18xx_capture_open, + .close = snd_es18xx_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es18xx_capture_hw_params, + .hw_free = snd_es18xx_pcm_hw_free, + .prepare = snd_es18xx_capture_prepare, + .trigger = snd_es18xx_capture_trigger, + .pointer = snd_es18xx_capture_pointer, +}; + +static void snd_es18xx_pcm_free(snd_pcm_t *pcm) +{ + es18xx_t *codec = snd_magic_cast(es18xx_t, pcm->private_data, return); + codec->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __init snd_es18xx_pcm(es18xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + char str[16]; + int err; + + if (rpcm) + *rpcm = NULL; + sprintf(str, "ES%x", chip->version); + if (chip->caps & ES18XX_PCM2) { + err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm); + } else { + err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm); + } + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_es18xx_pcm_free; + pcm->info_flags = 0; + if (chip->caps & ES18XX_DUPLEX_SAME) + pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; + if (! (chip->caps & ES18XX_PCM2)) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* Power Management support functions */ +#ifdef CONFIG_PM +static void snd_es18xx_suspend(es18xx_t *chip) +{ + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + snd_pcm_suspend_all(chip->pcm); + + /* power down */ + chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); + chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void snd_es18xx_resume(es18xx_t *chip) +{ + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + /* restore PM register, we won't wake till (not 0x07) i/o activity though */ + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +/* callback for control API */ +static int snd_es18xx_set_power_state(snd_card_t *card, unsigned int power_state) +{ + es18xx_t *chip = (es18xx_t *) card->power_state_private_data; + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_es18xx_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_es18xx_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_es18xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + snd_es18xx_suspend(chip); + break; + case PM_RESUME: + snd_es18xx_resume(chip); + break; + } + return 0; +} +#endif /* CONFIG_PM */ + +static int snd_es18xx_free(es18xx_t *chip) +{ +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_ctrl_port) { + release_resource(chip->res_ctrl_port); + kfree_nocheck(chip->res_ctrl_port); + } + if (chip->res_mpu_port) { + release_resource(chip->res_mpu_port); + kfree_nocheck(chip->res_mpu_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma1 >= 0) { + disable_dma(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0 && chip->dma1 != chip->dma2) { + disable_dma(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_es18xx_dev_free(snd_device_t *device) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, device->device_data, return -ENXIO); + return snd_es18xx_free(chip); +} + +static int __init snd_es18xx_new_device(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + unsigned long fm_port, + int irq, int dma1, int dma2, + es18xx_t ** rchip) +{ + es18xx_t *chip; + static snd_device_ops_t ops = { + .dev_free = snd_es18xx_dev_free, + }; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(es18xx_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + spin_lock_init(&chip->ctrl_lock); + chip->card = card; + chip->port = port; + chip->mpu_port = mpu_port; + chip->fm_port = fm_port; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + chip->audio2_vol = 0x00; + chip->active = 0; + + if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); + return -EBUSY; + } + + if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); + return -EBUSY; + } + chip->irq = irq; + + if (request_dma(dma1, "ES18xx DMA 1")) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1); + return -EBUSY; + } + chip->dma1 = dma1; + + if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2); + return -EBUSY; + } + chip->dma2 = dma2; + + if (snd_es18xx_probe(chip) < 0) { + snd_es18xx_free(chip); + return -ENODEV; + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es18xx_free(chip); + return err; + } + *rchip = chip; + return 0; +} + +static int __init snd_es18xx_mixer(es18xx_t *chip) +{ + snd_card_t *card; + int err; + unsigned int idx; + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip); + if (chip->caps & ES18XX_HWV) { + switch (idx) { + case 0: + chip->master_volume = kctl; + kctl->private_free = snd_es18xx_hwv_free; + break; + case 1: + chip->master_switch = kctl; + kctl->private_free = snd_es18xx_hwv_free; + break; + } + } + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + if (chip->caps & ES18XX_PCM2) { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0) + return err; + } + } else { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0) + return err; + } + } + + if (chip->caps & ES18XX_MONO) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_mono_in_control, chip))) < 0) + return err; + } + if (chip->caps & ES18XX_RECMIX) { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0) + return err; + } + } + switch (chip->version) { + default: + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0) + return err; + break; + case 0x1869: + case 0x1879: + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0) + return err; + break; + } + if (chip->caps & ES18XX_SPATIALIZER) { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0) + return err; + } + } + if (chip->caps & ES18XX_HWV) { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_hw_volume_controls); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip); + if (idx == 0) + chip->hw_volume = kctl; + else + chip->hw_switch = kctl; + kctl->private_free = snd_es18xx_hwv_free; + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + + } + } + return 0; +} + + +/* Card level */ + +MODULE_AUTHOR("Christian Fischbach , Abramo Bagnara "); +MODULE_DESCRIPTION("ESS ES18xx AudioDrive"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES1868 PnP AudioDrive}," + "{ESS,ES1869 PnP AudioDrive}," + "{ESS,ES1878 PnP AudioDrive}," + "{ESS,ES1879 PnP AudioDrive}," + "{ESS,ES1887 PnP AudioDrive}," + "{ESS,ES1888 PnP AudioDrive}," + "{ESS,ES1887 AudioDrive}," + "{ESS,ES1888 AudioDrive}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ +#ifndef __ISAPNP__ +static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +#else +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#endif +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ES18xx soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ES18xx soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ES18xx soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for ES18xx driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x330,0x30},{0x800,0xffe,0x2}},prefers:{0x330,0x300},base:16,dialog:combo"); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x800,0xffc,0x4}},prefers:{0x388},base:16,dialog:combo"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for ES18xx driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC ",prefers:{5}"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA8_DESC ",prefers:{1}"); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_ENABLED ",allows:{{0},{1},{3},{5}},dialog:list,prefers:{0}"); + +struct snd_audiodrive { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devc; +#endif +}; + +static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_audiodrive_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_audiodrive_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_ES18XX(_va, _vb, _vc, _device, _audio, _control) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _control) } \ + } + +static struct isapnp_card_id snd_audiodrive_pnpids[] __devinitdata = { + /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x1868,0x0000), + /* ESS 1868 (integrated on Maxisound Cards) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x8601,0x8600), + /* ESS 1868 (integrated on Maxisound Cards) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x8611,0x8610), + /* ESS ES1869 Plug and Play AudioDrive */ + ISAPNP_ES18XX('E','S','S',0x0003,0x1869,0x0006), + /* ESS 1869 */ + ISAPNP_ES18XX('E','S','S',0x1869,0x1869,0x0006), + /* ESS 1878 */ + ISAPNP_ES18XX('E','S','S',0x1878,0x1878,0x0004), + /* ESS 1879 */ + ISAPNP_ES18XX('E','S','S',0x1879,0x1879,0x0009), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_audiodrive_pnpids); + +static int __init snd_audiodrive_isapnp(int dev, struct snd_audiodrive *acard) +{ + const struct isapnp_card_id *id = snd_audiodrive_isapnp_id[dev]; + struct isapnp_card *card = snd_audiodrive_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devc->active) { + acard->dev = acard->devc = NULL; + return -EBUSY; + } + /* Control port initialization */ + if (acard->devc->prepare(acard->devc)<0) + return -EAGAIN; + if (acard->devc->activate(acard->devc)<0) { + printk(KERN_ERR PFX "isapnp control configure failure (out of resources?)\n"); + return -EAGAIN; + } + snd_printdd("isapnp: port=0x%lx\n", acard->devc->resource[0].start); + /* PnP initialization */ + pdev = acard->dev; + if (pdev->prepare(pdev)<0) { + acard->devc->deactivate(acard->devc); + return -EAGAIN; + } + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], fm_port[dev], 4); + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], mpu_port[dev], 2); + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev], 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n"); + acard->devc->deactivate(acard->devc); + return -EBUSY; + } + /* ok. hack using Vendor-Defined Card-Level registers */ + /* skip csn and logdev initialization - already done in isapnp_configure */ + isapnp_cfg_begin(pdev->bus->number, pdev->devfn); + isapnp_write_byte(0x27, pdev->irq_resource[0].start); /* Hardware Volume IRQ Number */ + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_write_byte(0x28, pdev->irq); /* MPU-401 IRQ Number */ + isapnp_write_byte(0x72, pdev->irq_resource[0].start); /* second IRQ */ + isapnp_cfg_end(); + port[dev] = pdev->resource[0].start; + fm_port[dev] = pdev->resource[1].start; + mpu_port[dev] = pdev->resource[2].start; + dma1[dev] = pdev->dma_resource[0].start; + dma2[dev] = pdev->dma_resource[1].start; + irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]); + snd_printdd("isapnp ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); + return 0; +} + +static void snd_audiodrive_deactivate(struct snd_audiodrive *acard) +{ + if (acard->devc) { + acard->devc->deactivate(acard->devc); + acard->devc = NULL; + } + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_audiodrive_free(snd_card_t *card) +{ + struct snd_audiodrive *acard = (struct snd_audiodrive *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_audiodrive_deactivate(acard); +#endif + } +} + +static int __init snd_audiodrive_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1}; + static int possible_dmas[] = {1, 0, 3, 5, -1}; + int xirq, xdma1, xdma2; + snd_card_t *card; + struct snd_audiodrive *acard; + snd_rawmidi_t *rmidi = NULL; + es18xx_t *chip; + opl3_t *opl3; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_audiodrive)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_audiodrive *)card->private_data; + card->private_free = snd_audiodrive_free; +#ifdef __ISAPNP__ + if (isapnp[dev] && (err = snd_audiodrive_isapnp(dev, acard)) < 0) { + snd_card_free(card); + return err; + } +#endif + + xirq = irq[dev]; + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + xdma1 = dma1[dev]; + if (xdma1 == SNDRV_AUTO_DMA) { + if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + xdma2 = dma2[dev]; + if (xdma2 == SNDRV_AUTO_DMA) { + if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_es18xx_new_device(card, + port[dev], + mpu_port[dev], + fm_port[dev], + xirq, xdma1, xdma2, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es18xx_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { + if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { + printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->fm_port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + } + + if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, + chip->mpu_port, 0, + xirq, 0, + &rmidi)) < 0) { + snd_card_free(card); + return err; + } + chip->rmidi = rmidi; + } + +#ifdef CONFIG_PM + /* Power Management */ + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_es18xx_pm_callback); + if (chip->pm_dev) { + chip->pm_dev->data = chip; + /* set control api callback */ + card->set_power_state = snd_es18xx_set_power_state; + card->power_state_private_data = chip; + } +#endif + sprintf(card->driver, "ES%x", chip->version); + sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version); + if (xdma1 != xdma2) + sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d", + card->shortname, + chip->port, + xirq, xdma1, xdma2); + else + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + card->shortname, + chip->port, + xirq, xdma1); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_audiodrive_cards[dev] = card; + return 0; +} + +static int __init snd_audiodrive_probe_legacy_port(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + port[dev] = xport; + res = snd_audiodrive_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + + +#ifdef __ISAPNP__ +static int __init snd_audiodrive_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || !isapnp[dev]) + continue; + snd_audiodrive_isapnp_cards[dev] = card; + snd_audiodrive_isapnp_id[dev] = id; + res = snd_audiodrive_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_es18xx_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; + int dev, cards = 0; + + /* legacy non-auto cards at first */ + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (snd_audiodrive_probe(dev) >= 0) + cards++; + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards at last */ + cards += isapnp_probe_cards(snd_audiodrive_pnpids, snd_audiodrive_isapnp_detect); +#endif + if(!cards) { +#ifdef MODULE + printk(KERN_ERR "ESS AudioDrive ES18xx soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_es18xx_exit(void) +{ + int idx; + + for(idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_audiodrive_cards[idx]); +} + +module_init(alsa_card_es18xx_init) +module_exit(alsa_card_es18xx_exit) + +#ifndef MODULE + +/* format is: snd-es18xx=enable,index,id,isapnp, + port,mpu_port,fm_port,irq, + dma1,dma2 */ + +static int __init alsa_card_es18xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-es18xx=", alsa_card_es18xx_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/Makefile linux/sound/isa/gus/Makefile --- linux-2.4.21-rc1.orig/sound/isa/gus/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,64 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _gus.o + +list-multi := snd-gus-lib.o snd-gusclassic.o snd-gusextreme.o \ + snd-gusmax.o snd-interwave.o snd-interwave-stb.o + +export-objs := gus_main.o gus_volume.o + +snd-gus-lib-objs := gus_main.o \ + gus_io.o gus_irq.o gus_timer.o \ + gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \ + gus_pcm.o gus_mixer.o \ + gus_uart.o \ + gus_reset.o +snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o + +snd-gusclassic-objs := gusclassic.o +snd-gusextreme-objs := gusextreme.o +snd-gusmax-objs := gusmax.o +snd-interwave-objs := interwave.o +snd-interwave-stb-objs := interwave-stb.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o +obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o +obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_GUSCLASSIC) += snd-gus-synth.o + obj-$(CONFIG_SND_GUSMAX) += snd-gus-synth.o + obj-$(CONFIG_SND_GUSEXTREME) += snd-gus-synth.o + obj-$(CONFIG_SND_INTERWAVE) += snd-gus-synth.o + obj-$(CONFIG_SND_INTERWAVE_STB) += snd-gus-synth.o +endif + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-gus-lib.o: $(snd-gus-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-lib-objs) + +snd-gus-synth.o: $(snd-gus-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-synth-objs) + +snd-gusclassic.o: $(snd-gusclassic-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusclassic-objs) + +snd-gusextreme.o: $(snd-gusextreme-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusextreme-objs) + +snd-gusmax.o: $(snd-gusmax-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusmax-objs) + +snd-interwave.o: $(snd-interwave-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-objs) + +snd-interwave-stb.o: $(snd-interwave-stb-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-stb-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_dma.c linux/sound/isa/gus/gus_dma.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_dma.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_dma.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,244 @@ +/* + * Routines for GF1 DMA control + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +void snd_gf1_dma_ack(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00); + snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_dma_program(snd_gus_card_t * gus, + unsigned int addr, + unsigned long buf_addr, + unsigned int count, + unsigned int cmd) +{ + unsigned long flags; + unsigned int address; + unsigned char dma_cmd; + unsigned int address_high; + + // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count); + + if (gus->gf1.dma1 > 3) { + if (gus->gf1.enh_mode) { + address = addr >> 1; + } else { + if (addr & 0x1f) { + snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr); + return; + } + address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1); + } + } else { + address = addr; + } + + dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd; +#if 0 + dma_cmd |= 0x08; +#endif + if (dma_cmd & SNDRV_GF1_DMA_16BIT) { + count++; + count &= ~1; /* align */ + } + if (gus->gf1.dma1 > 3) { + dma_cmd |= SNDRV_GF1_DMA_WIDTH16; + count++; + count &= ~1; /* align */ + } + snd_gf1_dma_ack(gus); + snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE); +#if 0 + snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + if (gus->gf1.enh_mode) { + address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f); + snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high); + } else + snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + /* PCM block have bigger priority than synthesizer one */ + if (gus->gf1.dma_data_pcm) { + block = gus->gf1.dma_data_pcm; + if (gus->gf1.dma_data_pcm_last == block) { + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = NULL; + } else { + gus->gf1.dma_data_pcm = block->next; + } + } else if (gus->gf1.dma_data_synth) { + block = gus->gf1.dma_data_synth; + if (gus->gf1.dma_data_synth_last == block) { + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = NULL; + } else { + gus->gf1.dma_data_synth = block->next; + } + } else { + block = NULL; + } + if (block) { + gus->gf1.dma_ack = block->ack; + gus->gf1.dma_private_data = block->private_data; + } + return block; +} + + +static void snd_gf1_dma_interrupt(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + snd_gf1_dma_ack(gus); + if (gus->gf1.dma_ack) + gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); + spin_lock(&gus->dma_lock); + if (gus->gf1.dma_data_pcm == NULL && + gus->gf1.dma_data_synth == NULL) { + gus->gf1.dma_ack = NULL; + gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; + spin_unlock(&gus->dma_lock); + return; + } + block = snd_gf1_dma_next_block(gus); + spin_unlock(&gus->dma_lock); + snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); + kfree(block); +#if 0 + printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd); +#endif +} + +int snd_gf1_dma_init(snd_gus_card_t * gus) +{ + down(&gus->dma_mutex); + gus->gf1.dma_shared++; + if (gus->gf1.dma_shared > 1) { + up(&gus->dma_mutex); + return 0; + } + gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt; + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = NULL; + up(&gus->dma_mutex); + return 0; +} + +int snd_gf1_dma_done(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + down(&gus->dma_mutex); + gus->gf1.dma_shared--; + if (!gus->gf1.dma_shared) { + snd_dma_disable(gus->gf1.dma1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE); + snd_gf1_dma_ack(gus); + while ((block = gus->gf1.dma_data_pcm)) { + gus->gf1.dma_data_pcm = block->next; + kfree(block); + } + while ((block = gus->gf1.dma_data_synth)) { + gus->gf1.dma_data_synth = block->next; + kfree(block); + } + gus->gf1.dma_data_pcm_last = + gus->gf1.dma_data_synth_last = NULL; + } + up(&gus->dma_mutex); + return 0; +} + +int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, + snd_gf1_dma_block_t * __block, + int atomic, + int synth) +{ + unsigned long flags; + snd_gf1_dma_block_t *block; + + block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL); + if (block == NULL) { + snd_printk("gf1: DMA transfer failure; not enough memory\n"); + return -ENOMEM; + } + *block = *__block; + block->next = NULL; +#if 0 + printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd); +#endif +#if 0 + printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last); + printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm); +#endif + spin_lock_irqsave(&gus->dma_lock, flags); + if (synth) { + if (gus->gf1.dma_data_synth_last) { + gus->gf1.dma_data_synth_last->next = block; + gus->gf1.dma_data_synth_last = block; + } else { + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = block; + } + } else { + if (gus->gf1.dma_data_pcm_last) { + gus->gf1.dma_data_pcm_last->next = block; + gus->gf1.dma_data_pcm_last = block; + } else { + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = block; + } + } + if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) { + gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER; + block = snd_gf1_dma_next_block(gus); + spin_unlock_irqrestore(&gus->dma_lock, flags); + if (block == NULL) + return 0; + snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); + kfree(block); + return 0; + } + spin_unlock_irqrestore(&gus->dma_lock, flags); + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_dram.c linux/sound/isa/gus/gus_dram.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_dram.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_dram.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,103 @@ +/* + * Copyright (c) by Jaroslav Kysela + * DRAM access routines + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + + +static int snd_gus_dram_poke(snd_gus_card_t *gus, char *_buffer, + unsigned int address, unsigned int size) +{ + unsigned long flags; + unsigned int size1, size2; + char buffer[512], *pbuffer; + + while (size > 0) { + if (copy_from_user(buffer, _buffer, 512)) + return -EFAULT; + size1 = size > 512 ? 512 : size; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + snd_gf1_dram_addr(gus, address); + outsb(GUSP(gus, DRAM), buffer, size1); + spin_unlock_irqrestore(&gus->reg_lock, flags); + address += size1; + } else { + pbuffer = buffer; + size2 = size1; + while (size2--) + snd_gf1_poke(gus, address++, *pbuffer++); + } + size -= size1; + _buffer += size1; + } + return 0; +} + + +int snd_gus_dram_write(snd_gus_card_t *gus, char *buffer, + unsigned int address, unsigned int size) +{ + return snd_gus_dram_poke(gus, buffer, address, size); +} + +static int snd_gus_dram_peek(snd_gus_card_t *gus, char *_buffer, + unsigned int address, unsigned int size, + int rom) +{ + unsigned long flags; + unsigned int size1, size2; + char buffer[512], *pbuffer; + + while (size > 0) { + size1 = size > 512 ? 512 : size; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01); + snd_gf1_dram_addr(gus, address); + insb(GUSP(gus, DRAM), buffer, size1); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + spin_unlock_irqrestore(&gus->reg_lock, flags); + address += size1; + } else { + pbuffer = buffer; + size2 = size1; + while (size2--) + *pbuffer++ = snd_gf1_peek(gus, address++); + } + if (copy_to_user(_buffer, buffer, size1)) + return -EFAULT; + size -= size1; + _buffer += size1; + } + return 0; +} + +int snd_gus_dram_read(snd_gus_card_t *gus, char *buffer, + unsigned int address, unsigned int size, + int rom) +{ + return snd_gus_dram_peek(gus, buffer, address, size, rom); +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_instr.c linux/sound/isa/gus/gus_instr.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_instr.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_instr.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,173 @@ +/* + * Routines for Gravis UltraSound soundcards - Synthesizer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +/* + * + */ + +int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (wave->format & IWFFFF_WAVE_ROM) + return 0; /* it's probably ok - verify the address? */ + if (wave->format & IWFFFF_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF, + NULL, wave->size, + wave->format & IWFFFF_WAVE_16BIT, 1, + wave->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, + block->ptr, wave->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + wave->address.memory = block->ptr; + return 0; +} + +int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, + wave->format & IWFFFF_WAVE_ROM ? 1 : 0); +} + +int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + if (wave->format & IWFFFF_WAVE_ROM) + return 0; /* it's probably ok - verify the address? */ + return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); +} + +/* + * + */ + +int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (wave->format & GF1_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_GF1, + NULL, wave->size, + wave->format & GF1_WAVE_16BIT, 1, + wave->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, + block->ptr, wave->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + wave->address.memory = block->ptr; + return 0; +} + +int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); +} + +int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); +} + +/* + * + */ + +int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (instr->format & SIMPLE_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE, + NULL, instr->size, + instr->format & SIMPLE_WAVE_16BIT, 1, + instr->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, block->ptr, instr->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + instr->address.memory = block->ptr; + return 0; +} + +int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); +} + +int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_io.c linux/sound/isa/gus/gus_io.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_io.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_io.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,531 @@ +/* + * Copyright (c) by Jaroslav Kysela + * I/O routines for GF1/InterWave synthesizer chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +void snd_gf1_delay(snd_gus_card_t * gus) +{ + int i; + + for (i = 0; i < 6; i++) { + mb(); + inb(GUSP(gus, DRAM)); + } +} + +/* + * ======================================================================= + */ + +/* + * ok.. stop of control registers (wave & ramp) need some special things.. + * big UltraClick (tm) elimination... + */ + +static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned char value; + + outb(reg | 0x80, gus->gf1.reg_regsel); + mb(); + value = inb(gus->gf1.reg_data8); + mb(); + outb(reg, gus->gf1.reg_regsel); + mb(); + outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8); + mb(); +} + +static inline void __snd_gf1_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + outb(data, gus->gf1.reg_data8); + mb(); +} + +static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus, + unsigned char reg) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + return inb(gus->gf1.reg_data8); +} + +static inline void __snd_gf1_write16(snd_gus_card_t * gus, + unsigned char reg, unsigned int data) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) data, gus->gf1.reg_data16); + mb(); +} + +static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus, + unsigned char reg) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + return inw(gus->gf1.reg_data16); +} + +static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus, + unsigned char reg, unsigned char data) +{ + outb(reg, gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); + outb(data, gus->gf1.reg_timerdata); + inb(gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); +} + +static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, int w_16bit) +{ + if (gus->gf1.enh_mode) { + if (w_16bit) + addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f); + __snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03)); + } else if (w_16bit) + addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1); + __snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11)); + __snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5)); +} + +static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) +{ + unsigned int res; + + res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800; + res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff; + if (gus->gf1.enh_mode) { + res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26; + if (w_16bit) + res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f); + } else if (w_16bit) + res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f); + return res; +} + + +/* + * ======================================================================= + */ + +void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + __snd_gf1_ctrl_stop(gus, reg); +} + +void snd_gf1_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + __snd_gf1_write8(gus, reg, data); +} + +unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg) +{ + return __snd_gf1_look8(gus, reg); +} + +void snd_gf1_write16(snd_gus_card_t * gus, + unsigned char reg, + unsigned int data) +{ + __snd_gf1_write16(gus, reg, data); +} + +unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg) +{ + return __snd_gf1_look16(gus, reg); +} + +void snd_gf1_adlib_write(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + __snd_gf1_adlib_write(gus, reg, data); +} + +void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, short w_16bit) +{ + __snd_gf1_write_addr(gus, reg, addr, w_16bit); +} + +unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, + unsigned char reg, + short w_16bit) +{ + return __snd_gf1_read_addr(gus, reg, w_16bit); +} + +/* + + */ + +void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_ctrl_stop(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_i_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write8(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_look8(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_i_write16(snd_gus_card_t * gus, + unsigned char reg, + unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write16(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + unsigned short res; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_look16(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_i_adlib_write(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_adlib_write(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, short w_16bit) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write_addr(gus, reg, addr, w_16bit); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) +{ + unsigned int res; + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_read_addr(gus, reg, w_16bit); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +/* + + */ + +void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr) +{ + outb(0x43, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(0x44, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); +} + +void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(data, gus->gf1.reg_dram); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + res = inb(gus->gf1.reg_dram); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data) +{ + unsigned long flags; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_pokew - GF1!!!\n"); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + mb(); + outw(data, gus->gf1.reg_data16); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr) +{ + unsigned long flags; + unsigned short res; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_peekw - GF1!!!\n"); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + mb(); + res = inw(gus->gf1.reg_data16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, + unsigned short value, unsigned int count) +{ + unsigned long port; + unsigned long flags; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_dram_setmem - GF1!!!\n"); +#endif + addr &= ~1; + count >>= 1; + port = GUSP(gus, GF1DATALOW); + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + while (count--) + outw(value, port); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +/* + + */ + +void snd_gf1_select_active_voices(snd_gus_card_t * gus) +{ + unsigned short voices; + + static unsigned short voices_tbl[32 - 14 + 1] = + { + 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, + 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 + }; + + voices = gus->gf1.active_voices; + if (voices > 32) + voices = 32; + if (voices < 14) + voices = 14; + if (gus->gf1.enh_mode) + voices = 32; + gus->gf1.active_voices = voices; + gus->gf1.playback_freq = + gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14]; + if (!gus->gf1.enh_mode) { + snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1)); + udelay(100); + } +} + +#ifdef CONFIG_SND_DEBUG + +void snd_gf1_print_voice_registers(snd_gus_card_t * gus) +{ + unsigned char mode; + int voice, ctrl; + + voice = gus->gf1.active_voice; + printk(" -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d)); + printk(" -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1)); + printk(" -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4)); + printk(" -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6)); + printk(" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9)); + printk(" -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4)); + if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */ + mode = snd_gf1_i_read8(gus, 0x15); + printk(" -%i- GFA1 mode = 0x%x\n", voice, mode); + if (mode & 0x01) { /* Effect processor */ + printk(" -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4)); + printk(" -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16)); + printk(" -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d)); + printk(" -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14)); + } + if (mode & 0x20) { + printk(" -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4); + printk(" -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4); + printk(" -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4); + printk(" -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4); + } else + printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); + } else + printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); +} + +void snd_gf1_print_global_registers(snd_gus_card_t * gus) +{ + unsigned char global_mode = 0x00; + + printk(" -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES)); + if (gus->interwave) { + global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE); + printk(" -G- GF1 global mode = 0x%x\n", global_mode); + } + if (global_mode & 0x02) /* LFO enabled? */ + printk(" -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE)); + printk(" -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ)); + printk(" -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL)); + printk(" -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW)); + printk(" -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW)); + if (!gus->interwave) + printk(" -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL)); + printk(" -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16)); + if (gus->gf1.enh_mode) { + printk(" -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG)); + printk(" -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL)); + printk(" -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR)); + printk(" -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR)); + printk(" -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE)); + } +} + +void snd_gf1_print_setup_registers(snd_gus_card_t * gus) +{ + printk(" -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG))); + printk(" -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT))); + printk(" -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL))); + printk(" -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA))); + printk(" -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS))); + printk(" -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL)); + printk(" -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2)); + printk(" -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)); + if (gus->interwave) { + printk(" -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY)); + printk(" -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL)); + printk(" -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER)); + printk(" -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B)); + printk(" -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ)); + } +} + +void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit) +{ + if (!w_16bit) { + while (count-- > 0) + printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++)); + } else { + while (count-- > 0) { + printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8)); + addr += 2; + } + } +} + +#endif diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_irq.c linux/sound/isa/gus/gus_irq.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_irq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_irq.c 2003-01-07 03:36:29.000000000 -0700 @@ -0,0 +1,139 @@ +/* + * Routine for IRQ handling from GF1/InterWave chip + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG +#define STAT_ADD(x) ((x)++) +#else +#define STAT_ADD(x) while (0) { ; } +#endif + +void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return); + unsigned char status; + int loop = 100; + + __again: + status = inb(gus->gf1.reg_irqstat); + if (status == 0) + return; + // snd_printk("IRQ: status = 0x%x\n", status); + if (status & 0x02) { + STAT_ADD(gus->gf1.interrupt_stat_midi_in); + gus->gf1.interrupt_handler_midi_in(gus); + } + if (status & 0x01) { + STAT_ADD(gus->gf1.interrupt_stat_midi_out); + gus->gf1.interrupt_handler_midi_out(gus); + } + if (status & (0x20 | 0x40)) { + unsigned int already, _current_; + unsigned char voice_status, voice; + snd_gus_voice_t *pvoice; + + already = 0; + while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) { + voice = voice_status & 0x1f; + _current_ = 1 << voice; + if (already & _current_) + continue; /* multi request */ + already |= _current_; /* mark request */ +#if 0 + printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE))); +#endif + pvoice = &gus->gf1.voices[voice]; + if (pvoice->use) { + if (!(voice_status & 0x80)) { /* voice position IRQ */ + STAT_ADD(pvoice->interrupt_stat_wave); + pvoice->handler_wave(gus, pvoice); + } + if (!(voice_status & 0x40)) { /* volume ramp IRQ */ + STAT_ADD(pvoice->interrupt_stat_volume); + pvoice->handler_volume(gus, pvoice); + } + } else { + STAT_ADD(gus->gf1.interrupt_stat_voice_lost); + snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + } + } + } + if (status & 0x04) { + STAT_ADD(gus->gf1.interrupt_stat_timer1); + gus->gf1.interrupt_handler_timer1(gus); + } + if (status & 0x08) { + STAT_ADD(gus->gf1.interrupt_stat_timer2); + gus->gf1.interrupt_handler_timer2(gus); + } + if (status & 0x80) { + if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) { + STAT_ADD(gus->gf1.interrupt_stat_dma_write); + gus->gf1.interrupt_handler_dma_write(gus); + } + if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) { + STAT_ADD(gus->gf1.interrupt_stat_dma_read); + gus->gf1.interrupt_handler_dma_read(gus); + } + } + if (--loop > 0) + goto __again; +} + +#ifdef CONFIG_SND_DEBUG +static void snd_gus_irq_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_gus_card_t *gus; + snd_gus_voice_t *pvoice; + int idx; + + gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out); + snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in); + snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1); + snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2); + snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write); + snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read); + snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n", + idx, + pvoice->interrupt_stat_wave, + pvoice->interrupt_stat_volume); + } +} + +void snd_gus_irq_profile_init(snd_gus_card_t *gus) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(gus->card, "gusirq", &entry)) + snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read); +} + +#endif diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_lfo.c linux/sound/isa/gus/gus_lfo.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_lfo.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_lfo.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,429 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of LFO generators (tremolo & vibrato) for + * GF1/InterWave chips... + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +/* + * called by engine routines + */ + +static signed char snd_gf1_lfo_compute_value(snd_gus_card_t * gus, + unsigned char *ptr) +{ + unsigned int twaveinc, depth_delta; + signed int result; + unsigned short control, twave, depth, depth_final; + unsigned char *ptr1; + + control = *(unsigned short *) (ptr + 0x00); + ptr1 = ptr + ((control & 0x4000) >> 12); + /* 1. add TWAVEINC to TWAVE and write the result back */ + /* LFO update rate is 689Hz, effect timer is in ms */ + if (gus->gf1.timer_slave) + twaveinc = (689 * gus->gf1.timer_master_gus->gf1.effect_timer) / 1000; + else + twaveinc = (689 * gus->gf1.effect_timer) / 1000; + if (!twaveinc) + twaveinc++; +#if 0 + printk("twaveinc = 0x%x, effect_timer = %i\n", twaveinc, gus->gf1.effect_timer); +#endif + + depth = *(unsigned short *) (ptr1 + 0x0a); + depth_final = *(unsigned char *) (ptr + 0x02) << 5; + if (depth != depth_final) { + depth_delta = ((twaveinc * *(ptr + 0x03)) + *(unsigned short *) (ptr + 0x04)); + *(unsigned short *) (ptr + 0x04) = depth_delta % 8000; + depth_delta /= 8000; + if (depth < depth_final) { + if (depth + depth_delta > depth_final) + depth = depth_final; + else + depth += depth_delta; + } + if (depth > depth_final) { + if (depth - depth_delta < depth_final) + depth = depth_final; + else + depth -= depth_delta; + } + *(unsigned short *) (ptr1 + 0x0a) = depth; + } + twaveinc *= (unsigned int) control & 0x7ff; + twaveinc += *(unsigned short *) (ptr + 0x06); + *(unsigned short *) (ptr + 0x06) = twaveinc % 1000; + + twave = *(unsigned short *) (ptr1 + 0x08); + twave += (unsigned short) (twaveinc / (unsigned int) 1000); + *(unsigned short *) (ptr1 + 0x08) = twave; + + if (!(control & 0x2000)) { + /* 2. if shift is low */ + if (twave & 0x4000) { /* bit 14 high -> invert TWAVE 13-0 */ + twave ^= 0x3fff; + twave &= ~0x4000; + } + /* TWAVE bit 15 is exclusive or'd with the invert bit (12) */ + twave ^= (control & 0x1000) << 3; + } else { + /* 2. if shift is high */ + if (twave & 0x8000) /* bit 15 high -> invert TWAVE 14-0 */ + twave ^= 0x7fff; + /* the invert bit (12) is used as sign bit */ + if (control & 0x1000) + twave |= 0x8000; + else + twave &= ~0x8000; + } + /* 3. multiply the 14-bit LFO waveform magnitude by 13-bit DEPTH */ +#if 0 + printk("c=0x%x,tw=0x%x,to=0x%x,d=0x%x,df=0x%x,di=0x%x,r=0x%x,r1=%i\n", + control, twave, + *(unsigned short *) (ptr1 + 0x08), + depth, depth_final, *(ptr + 0x03), + (twave & 0x7fff) * depth, ((twave & 0x7fff) * depth) >> 21); +#endif + result = (twave & 0x7fff) * depth; + if (result) { + /* shift */ + result >>= 21; + result &= 0x3f; + } + /* add sign */ + if (twave & 0x8000) + result = -result; +#if 0 + printk("lfo final value = %i\n", result); +#endif + return result; +} + +static void snd_gf1_lfo_register_setup(snd_gus_card_t * gus, + snd_gf1_voice_t * voice, + int lfo_type) +{ + unsigned long flags; + + if (gus->gf1.enh_mode) { + CLI(&flags); + gf1_select_voice(gus, voice->number); + if (lfo_type & 1) { + snd_gf1_write8(gus, GF1_VB_FREQUENCY_LFO, voice->lfo_fc); + voice->lfo_fc = 0; + } + if (lfo_type & 2) { + snd_gf1_write8(gus, GF1_VB_VOLUME_LFO, voice->lfo_volume); + voice->lfo_volume = 0; + } + STI(&flags); + } else { + /* + * ok.. with old GF1 chip can be only vibrato emulated... + * volume register can be in volume ramp state, so tremolo isn't simple.. + */ + if (!(lfo_type & 1)) + return; +#if 0 + if (voice->lfo_fc) + printk("setup - %i = %i\n", voice->number, voice->lfo_fc); +#endif + CLI(&flags); + gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, GF1_VW_FREQUENCY, voice->fc_register + voice->lfo_fc); + STI(&flags); + } +} + +void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice) +{ + unsigned char *ptr; + +#if 0 + if (voice->number != 0) + return; +#endif + ptr = gus->gf1.lfos + ((voice->number) << 5); + /* 1. vibrato */ + if (*(unsigned short *) (ptr + 0x00) & 0x8000) + voice->lfo_fc = snd_gf1_lfo_compute_value(gus, ptr); + /* 2. tremolo */ + ptr += 16; + if (*(unsigned short *) (ptr + 0x00) & 0x8000) + voice->lfo_volume = snd_gf1_lfo_compute_value(gus, ptr); + /* 3. register setup */ + snd_gf1_lfo_register_setup(gus, voice, 3); +} + +/* + + */ + +void snd_gf1_lfo_init(snd_gus_card_t * gus) +{ + if (gus->gf1.hw_lfo) { + snd_gf1_i_write16(gus, GF1_GW_LFO_BASE, 0x0000); + snd_gf1_dram_setmem(gus, 0, 0x0000, 1024); + /* now enable LFO */ + snd_gf1_i_write8(gus, GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, GF1_GB_GLOBAL_MODE) | 0x02); + } + if (gus->gf1.sw_lfo) { +#if 1 + gus->gf1.lfos = snd_calloc(1024); + if (!gus->gf1.lfos) +#endif + gus->gf1.sw_lfo = 0; + } +} + +void snd_gf1_lfo_done(snd_gus_card_t * gus) +{ + if (gus->gf1.sw_lfo) { + if (gus->gf1.lfos) { + snd_gf1_free(gus->gf1.lfos, 1024); + gus->gf1.lfos = NULL; + } + } +} + +void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, + struct ULTRA_STRU_IW_LFO_PROGRAM *program) +{ + unsigned int lfo_addr, wave_select; + + wave_select = (program->freq_and_control & 0x4000) >> 12; + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) { +#if 0 + printk("LMCI = 0x%x\n", snd_gf1_i_look8(gus, 0x53)); + printk("lfo_program: lfo_addr=0x%x,wave_sel=0x%x,fac=0x%x,df=0x%x,di=0x%x,twave=0x%x,depth=0x%x\n", + lfo_addr, wave_select, + program->freq_and_control, + program->depth_final, + program->depth_inc, + program->twave, + program->depth); +#endif + snd_gf1_poke(gus, lfo_addr + 0x02, program->depth_final); + snd_gf1_poke(gus, lfo_addr + 0x03, program->depth_inc); + snd_gf1_pokew(gus, lfo_addr + 0x08 + wave_select, program->twave); + snd_gf1_pokew(gus, lfo_addr + 0x0a + wave_select, program->depth); + snd_gf1_pokew(gus, lfo_addr + 0x00, program->freq_and_control); +#if 0 + { + int i = 0; + for (i = 0; i < 16; i++) + printk("%02x:", snd_gf1_peek(gus, lfo_addr + i)); + printk("\n"); + } +#endif + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(ptr + 0x02) = program->depth_final; + *(ptr + 0x03) = program->depth_inc; + *(unsigned short *) (ptr + 0x08 + wave_select) = program->twave; + *(unsigned short *) (ptr + 0x0a + wave_select) = program->depth; + *(unsigned short *) (ptr + 0x00) = program->freq_and_control; + } +} + +void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, snd_gf1_peekw(gus, lfo_addr + 0x00) | 0x8000); + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(unsigned short *) (ptr + 0x00) |= 0x8000; + } +} + +void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, + snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x8000); + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(unsigned short *) (ptr + 0x00) &= ~0x8000; + } +} + +void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, + int lfo_type, int freq) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, + (snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x7ff) | (freq & 0x7ff)); + if (gus->gf1.sw_lfo) { + unsigned long flags; + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + CLI(&flags); + *(unsigned short *) (ptr + 0x00) &= ~0x7ff; + *(unsigned short *) (ptr + 0x00) |= freq & 0x7ff; + STI(&flags); + } +} + +void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, + int lfo_type, int depth) +{ + unsigned long flags; + unsigned int lfo_addr; + unsigned short control = 0; + unsigned char *ptr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + ptr = gus->gf1.lfos + lfo_addr; + if (gus->gf1.hw_lfo) + control = snd_gf1_peekw(gus, lfo_addr + 0x00); + if (gus->gf1.sw_lfo) + control = *(unsigned short *) (ptr + 0x00); + if (depth < 0) { + control |= 0x1000; + depth = -depth; + } else + control &= ~0x1000; + if (gus->gf1.hw_lfo) { + CLI(&flags); + snd_gf1_poke(gus, lfo_addr + 0x02, (unsigned char) depth); + snd_gf1_pokew(gus, lfo_addr + 0x0a + ((control & 0x4000) >> 12), depth << 5); + snd_gf1_pokew(gus, lfo_addr + 0x00, control); + STI(&flags); + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + CLI(&flags); + *(ptr + 0x02) = (unsigned char) depth; + *(unsigned short *) (ptr + 0x0a + ((control & 0x4000) >> 12)) = depth << 5; + *(unsigned short *) (ptr + 0x00) = control; + STI(&flags); + } +} + +void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, + int freq, int current_depth, int depth, int sweep, + int shape) +{ + struct ULTRA_STRU_IW_LFO_PROGRAM program; + + program.freq_and_control = 0x8000 | (freq & 0x7ff); + if (shape & ULTRA_STRU_IW_LFO_SHAPE_POSTRIANGLE) + program.freq_and_control |= 0x2000; + if (depth < 0) { + program.freq_and_control |= 0x1000; + depth = -depth; + } + program.twave = 0; + program.depth = current_depth; + program.depth_final = depth; + if (sweep) { + program.depth_inc = (unsigned char) (((int) ((depth << 5) - current_depth) << 9) / (sweep * 4410L)); + if (!program.depth_inc) + program.depth_inc++; + } else + program.depth = (unsigned short) (depth << 5); + snd_gf1_lfo_program(gus, voice, lfo_type, &program); +} + +void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned long flags; + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) { + snd_gf1_pokew(gus, lfo_addr + 0x00, 0x0000); + CLI(&flags); + gf1_select_voice(gus, voice); + snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0); + STI(&flags); + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + snd_gf1_voice_t *pvoice; + + *(unsigned short *) (ptr + 0x00) = 0; + *(unsigned short *) (ptr + 0x04) = 0; + *(unsigned short *) (ptr + 0x06) = 0; + if (gus->gf1.syn_voices) { + pvoice = gus->gf1.syn_voices + voice; + if (lfo_type == ULTRA_LFO_VIBRATO) + pvoice->lfo_fc = 0; + else + pvoice->lfo_volume = 0; + snd_gf1_lfo_register_setup(gus, pvoice, lfo_type == ULTRA_LFO_VIBRATO ? 1 : 2); + } else if (gus->gf1.enh_mode) { + CLI(&flags); + gf1_select_voice(gus, voice); + snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0); + STI(&flags); + } + } +} + +void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *data) +{ + int lfo_type; + int lfo_command; + int temp1, temp2; + + lfo_type = *data >> 7; + lfo_command = *data & 0x7f; + switch (lfo_command) { + case ULTRA_LFO_SETUP: /* setup */ + temp1 = snd_gf1_get_word(data, 2); + temp2 = snd_gf1_get_word(data, 4); + snd_gf1_lfo_setup(gus, voice, lfo_type, temp1 & 0x7ff, 0, temp2 > 255 ? 255 : temp2, snd_gf1_get_byte(data, 1), (temp1 & 0x2000) >> 13); + break; + case ULTRA_LFO_FREQ: /* freq */ + snd_gf1_lfo_change_depth(gus, voice, lfo_type, snd_gf1_get_word(data, 2)); + break; + case ULTRA_LFO_DEPTH: /* depth */ + snd_gf1_lfo_change_freq(gus, voice, lfo_type, snd_gf1_get_word(data, 2)); + break; + case ULTRA_LFO_ENABLE: /* enable */ + snd_gf1_lfo_enable(gus, voice, lfo_type); + break; + case ULTRA_LFO_DISABLE: /* disable */ + snd_gf1_lfo_disable(gus, voice, lfo_type); + break; + case ULTRA_LFO_SHUTDOWN: /* shutdown */ + snd_gf1_lfo_shutdown(gus, voice, lfo_type); + break; + } +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_main.c linux/sound/isa/gus/gus_main.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_main.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_main.c 2002-12-19 08:59:18.000000000 -0700 @@ -0,0 +1,511 @@ +/* + * Routines for Gravis UltraSound soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards"); +MODULE_LICENSE("GPL"); + +#define chip_t snd_gus_card_t + +static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches); + +int snd_gus_use_inc(snd_gus_card_t * gus) +{ + if (!try_module_get(gus->card->module)) + return 0; + return 1; +} + +void snd_gus_use_dec(snd_gus_card_t * gus) +{ + module_put(gus->card->module); +} + +static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = gus->joystick_dac & 31; + return 0; +} + +static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval; + + nval = ucontrol->value.integer.value[0] & 31; + spin_lock_irqsave(&gus->reg_lock, flags); + change = gus->joystick_dac != nval; + gus->joystick_dac = nval; + snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_gus_joystick_control = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "Joystick Speed", + .info = snd_gus_joystick_info, + .get = snd_gus_joystick_get, + .put = snd_gus_joystick_put +}; + +static void snd_gus_init_control(snd_gus_card_t *gus) +{ + if (!gus->ace_flag) + snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus)); +} + +/* + * + */ + +static int snd_gus_free(snd_gus_card_t *gus) +{ + if (gus->gf1.res_port2 == NULL) + goto __hw_end; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (gus->seq_dev) { + snd_device_free(gus->card, gus->seq_dev); + gus->seq_dev = NULL; + } +#endif + snd_gf1_stop(gus); + snd_gus_init_dma_irq(gus, 0); + __hw_end: + if (gus->gf1.res_port1) { + release_resource(gus->gf1.res_port1); + kfree_nocheck(gus->gf1.res_port1); + } + if (gus->gf1.res_port2) { + release_resource(gus->gf1.res_port2); + kfree_nocheck(gus->gf1.res_port2); + } + if (gus->gf1.irq >= 0) + free_irq(gus->gf1.irq, (void *) gus); + if (gus->gf1.dma1 >= 0) { + disable_dma(gus->gf1.dma1); + free_dma(gus->gf1.dma1); + } + if (!gus->equal_dma && gus->gf1.dma2 >= 0) { + disable_dma(gus->gf1.dma2); + free_dma(gus->gf1.dma2); + } + snd_magic_kfree(gus); + return 0; +} + +static int snd_gus_dev_free(snd_device_t *device) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, device->device_data, return -ENXIO); + return snd_gus_free(gus); +} + +int snd_gus_create(snd_card_t * card, + unsigned long port, + int irq, int dma1, int dma2, + int timer_dev, + int voices, + int pcm_channels, + int effect, + snd_gus_card_t **rgus) +{ + snd_gus_card_t *gus; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_gus_dev_free, + }; + + *rgus = NULL; + gus = snd_magic_kcalloc(snd_gus_card_t, 0, GFP_KERNEL); + if (gus == NULL) + return -ENOMEM; + gus->gf1.irq = -1; + gus->gf1.dma1 = -1; + gus->gf1.dma2 = -1; + gus->card = card; + gus->gf1.port = port; + /* fill register variables for speedup */ + gus->gf1.reg_page = GUSP(gus, GF1PAGE); + gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL); + gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH); + gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW); + gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT); + gus->gf1.reg_dram = GUSP(gus, DRAM); + gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL); + gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA); + /* allocate resources */ + if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) { + snd_gus_free(gus); + return -EBUSY; + } + if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) { + snd_gus_free(gus); + return -EBUSY; + } + if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.irq = irq; + if (request_dma(dma1, "GUS - 1")) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.dma1 = dma1; + if (dma2 >= 0 && dma1 != dma2) { + if (request_dma(dma2, "GUS - 2")) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.dma2 = dma2; + } else { + gus->gf1.dma2 = gus->gf1.dma1; + gus->equal_dma = 1; + } + gus->timer_dev = timer_dev; + if (voices < 14) + voices = 14; + if (voices > 32) + voices = 32; + if (pcm_channels < 0) + pcm_channels = 0; + if (pcm_channels > 8) + pcm_channels = 8; + pcm_channels++; + pcm_channels &= ~1; + gus->gf1.effect = effect ? 1 : 0; + gus->gf1.active_voices = voices; + gus->gf1.pcm_channels = pcm_channels; + gus->gf1.volume_ramp = 25; + gus->gf1.smooth_pan = 1; + spin_lock_init(&gus->reg_lock); + spin_lock_init(&gus->voice_alloc); + spin_lock_init(&gus->active_voice_lock); + spin_lock_init(&gus->event_lock); + spin_lock_init(&gus->dma_lock); + spin_lock_init(&gus->pcm_volume_level_lock); + spin_lock_init(&gus->uart_cmd_lock); + init_MUTEX(&gus->dma_mutex); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) { + snd_gus_free(gus); + return err; + } + *rgus = gus; + return 0; +} + +/* + * Memory detection routine for plain GF1 soundcards + */ + +static int snd_gus_detect_memory(snd_gus_card_t * gus) +{ + int l, idx, local; + unsigned char d; + + snd_gf1_poke(gus, 0L, 0xaa); + snd_gf1_poke(gus, 1L, 0x55); + if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) { + snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port); + return -ENOMEM; + } + for (idx = 1, d = 0xab; idx < 4; idx++, d++) { + local = idx << 18; + snd_gf1_poke(gus, local, d); + snd_gf1_poke(gus, local + 1, d + 1); + if (snd_gf1_peek(gus, local) != d || + snd_gf1_peek(gus, local + 1) != d + 1 || + snd_gf1_peek(gus, 0L) != 0xaa) + break; + } +#if 1 + gus->gf1.memory = idx << 18; +#else + gus->gf1.memory = 256 * 1024; +#endif + for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) { + gus->gf1.mem_alloc.banks_8[l].address = + gus->gf1.mem_alloc.banks_8[l].size = 0; + gus->gf1.mem_alloc.banks_16[l].address = l << 18; + gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0; + } + gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory; + return 0; /* some memory were detected */ +} + +static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches) +{ + snd_card_t *card; + unsigned long flags; + int irq, dma1, dma2; + static unsigned char irqs[16] = + {0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; + static unsigned char dmas[8] = + {6, 1, 0, 2, 0, 3, 4, 5}; + + snd_assert(gus != NULL, return -EINVAL); + card = gus->card; + snd_assert(card != NULL, return -EINVAL); + + gus->mix_cntrl_reg &= 0xf8; + gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */ + if (gus->codec_flag || gus->ess_flag) { + gus->mix_cntrl_reg &= ~1; /* enable LINE IN */ + gus->mix_cntrl_reg |= 4; /* enable MIC */ + } + dma1 = gus->gf1.dma1; + dma1 = dma1 < 0 ? -dma1 : dma1; + dma1 = dmas[dma1 & 7]; + dma2 = gus->gf1.dma2; + dma2 = dma2 < 0 ? -dma2 : dma2; + dma2 = dmas[dma2 & 7]; +#if 0 + printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2); +#endif + dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); + + if ((dma1 & 7) == 0 || (dma2 & 7) == 0) { + snd_printk("Error! DMA isn't defined.\n"); + return -EINVAL; + } + irq = gus->gf1.irq; + irq = irq < 0 ? -irq : irq; + irq = irqs[irq & 0x0f]; + if (irq == 0) { + snd_printk("Error! IRQ isn't defined.\n"); + return -EINVAL; + } + irq |= 0x40; +#if 0 + card->mixer.mix_ctrl_reg |= 0x10; +#endif + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(5, GUSP(gus, REGCNTRLS)); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(0x00, GUSP(gus, IRQDMACNTRLREG)); + outb(0, GUSP(gus, REGCNTRLS)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + udelay(100); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(dma1, GUSP(gus, IRQDMACNTRLREG)); + if (latches) { + outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(irq, GUSP(gus, IRQDMACNTRLREG)); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + + udelay(100); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(dma1, GUSP(gus, IRQDMACNTRLREG)); + if (latches) { + outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(irq, GUSP(gus, IRQDMACNTRLREG)); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + + snd_gf1_delay(gus); + + if (latches) + gus->mix_cntrl_reg |= 0x08; /* enable latches */ + else + gus->mix_cntrl_reg &= ~0x08; /* disable latches */ + spin_lock_irqsave(&gus->reg_lock, flags); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(0, GUSP(gus, GF1PAGE)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + return 0; +} + +static int snd_gus_check_version(snd_gus_card_t * gus) +{ + unsigned long flags; + unsigned char val, rev; + snd_card_t *card; + + card = gus->card; + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x20, GUSP(gus, REGCNTRLS)); + val = inb(GUSP(gus, REGCNTRLS)); + rev = inb(GUSP(gus, BOARDVERSION)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); + strcpy(card->driver, "GUS"); + strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); + if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { + if (rev >= 5 && rev <= 9) { + gus->ics_flag = 1; + if (rev == 5) + gus->ics_flipped = 1; + card->longname[27] = '3'; + card->longname[29] = rev == 5 ? '5' : '7'; + } + if (rev >= 10 && rev != 255) { + if (rev >= 10 && rev <= 11) { + strcpy(card->driver, "GUS MAX"); + strcpy(card->longname, "Gravis UltraSound MAX"); + gus->max_flag = 1; + } else if (rev == 0x30) { + strcpy(card->driver, "GUS ACE"); + strcpy(card->longname, "Gravis UltraSound Ace"); + gus->ace_flag = 1; + } else if (rev == 0x50) { + strcpy(card->driver, "GUS Extreme"); + strcpy(card->longname, "Gravis UltraSound Extreme"); + gus->ess_flag = 1; + } else { + snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val); + snd_printk(" please - report to \n"); + } + } + } + strcpy(card->shortname, card->longname); + gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */ + snd_gus_init_control(gus); + return 0; +} + +static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, seq_dev->private_data, return); + gus->seq_dev = NULL; +} + +int snd_gus_initialize(snd_gus_card_t *gus) +{ + int err; + + if (!gus->interwave) { + if ((err = snd_gus_check_version(gus)) < 0) { + snd_printk("version check failed\n"); + return err; + } + if ((err = snd_gus_detect_memory(gus)) < 0) + return err; + } + if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) + return err; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS, + sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) { + strcpy(gus->seq_dev->name, "GUS"); + *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus; + gus->seq_dev->private_data = gus; + gus->seq_dev->private_free = snd_gus_seq_dev_free; + } +#endif + snd_gf1_start(gus); + gus->initialized = 1; + return 0; +} + + /* gus_io.c */ +EXPORT_SYMBOL(snd_gf1_delay); +EXPORT_SYMBOL(snd_gf1_write8); +EXPORT_SYMBOL(snd_gf1_look8); +EXPORT_SYMBOL(snd_gf1_write16); +EXPORT_SYMBOL(snd_gf1_look16); +EXPORT_SYMBOL(snd_gf1_i_write8); +EXPORT_SYMBOL(snd_gf1_i_look8); +EXPORT_SYMBOL(snd_gf1_i_write16); +EXPORT_SYMBOL(snd_gf1_i_look16); +EXPORT_SYMBOL(snd_gf1_dram_addr); +EXPORT_SYMBOL(snd_gf1_write_addr); +EXPORT_SYMBOL(snd_gf1_poke); +EXPORT_SYMBOL(snd_gf1_peek); + /* gus_reset.c */ +EXPORT_SYMBOL(snd_gf1_alloc_voice); +EXPORT_SYMBOL(snd_gf1_free_voice); +EXPORT_SYMBOL(snd_gf1_ctrl_stop); +EXPORT_SYMBOL(snd_gf1_stop_voice); +EXPORT_SYMBOL(snd_gf1_start); +EXPORT_SYMBOL(snd_gf1_stop); + /* gus_mixer.c */ +EXPORT_SYMBOL(snd_gf1_new_mixer); + /* gus_pcm.c */ +EXPORT_SYMBOL(snd_gf1_pcm_new); + /* gus.c */ +EXPORT_SYMBOL(snd_gus_use_inc); +EXPORT_SYMBOL(snd_gus_use_dec); +EXPORT_SYMBOL(snd_gus_create); +EXPORT_SYMBOL(snd_gus_initialize); + /* gus_irq.c */ +EXPORT_SYMBOL(snd_gus_interrupt); + /* gus_uart.c */ +EXPORT_SYMBOL(snd_gf1_rawmidi_new); + /* gus_dram.c */ +EXPORT_SYMBOL(snd_gus_dram_write); +EXPORT_SYMBOL(snd_gus_dram_read); + /* gus_volume.c */ +EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw); +EXPORT_SYMBOL(snd_gf1_translate_freq); + /* gus_mem.c */ +EXPORT_SYMBOL(snd_gf1_mem_alloc); +EXPORT_SYMBOL(snd_gf1_mem_xfree); +EXPORT_SYMBOL(snd_gf1_mem_free); +EXPORT_SYMBOL(snd_gf1_mem_lock); + +/* + * INIT part + */ + +static int __init alsa_gus_init(void) +{ + return 0; +} + +static void __exit alsa_gus_exit(void) +{ +} + +module_init(alsa_gus_init) +module_exit(alsa_gus_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_mem.c linux/sound/isa/gus/gus_mem.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_mem.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_mem.c 2003-01-31 08:19:51.000000000 -0700 @@ -0,0 +1,354 @@ +/* + * Copyright (c) by Jaroslav Kysela + * GUS's memory allocation routines / bottom layer + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG +static void snd_gf1_mem_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer); +#endif + +void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup) +{ + if (!xup) { + down(&alloc->memory_mutex); + } else { + up(&alloc->memory_mutex); + } +} + +snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc, + snd_gf1_mem_block_t * block) +{ + snd_gf1_mem_block_t *pblock, *nblock; + + nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL); + if (nblock == NULL) + return NULL; + *nblock = *block; + pblock = alloc->first; + while (pblock) { + if (pblock->ptr > nblock->ptr) { + nblock->prev = pblock->prev; + nblock->next = pblock; + pblock->prev = nblock; + if (pblock == alloc->first) + alloc->first = nblock; + else + nblock->prev->next = nblock; + up(&alloc->memory_mutex); + return 0; + } + pblock = pblock->next; + } + nblock->next = NULL; + if (alloc->last == NULL) { + nblock->prev = NULL; + alloc->first = alloc->last = nblock; + } else { + nblock->prev = alloc->last; + alloc->last->next = nblock; + alloc->last = nblock; + } + return nblock; +} + +int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block) +{ + if (block->share) { /* ok.. shared block */ + block->share--; + up(&alloc->memory_mutex); + return 0; + } + if (alloc->first == block) { + alloc->first = block->next; + if (block->next) + block->next->prev = NULL; + } else { + block->prev->next = block->next; + if (block->next) + block->next->prev = block->prev; + } + if (alloc->last == block) { + alloc->last = block->prev; + if (block->prev) + block->prev->next = NULL; + } else { + block->next->prev = block->prev; + if (block->prev) + block->prev->next = block->next; + } + if (block->name) + kfree(block->name); + kfree(block); + return 0; +} + +snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, + unsigned int address) +{ + snd_gf1_mem_block_t *block; + + for (block = alloc->first; block; block = block->next) { + if (block->ptr == address) { + return block; + } + } + return NULL; +} + +snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, + unsigned int *share_id) +{ + snd_gf1_mem_block_t *block; + + if (!share_id[0] && !share_id[1] && + !share_id[2] && !share_id[3]) + return NULL; + for (block = alloc->first; block; block = block->next) + if (!memcmp(share_id, block->share_id, sizeof(share_id))) + return block; + return NULL; +} + +static int snd_gf1_mem_find(snd_gf1_mem_t * alloc, + snd_gf1_mem_block_t * block, + unsigned int size, int w_16, int align) +{ + snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8; + unsigned int idx, boundary; + int size1; + snd_gf1_mem_block_t *pblock; + unsigned int ptr1, ptr2; + + align--; + if (w_16 && align < 1) + align = 1; + block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0; + block->owner = SNDRV_GF1_MEM_OWNER_DRIVER; + block->share = 0; + block->share_id[0] = block->share_id[1] = + block->share_id[2] = block->share_id[3] = 0; + block->name = NULL; + block->prev = block->next = NULL; + for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) { + while (pblock->ptr >= (boundary = info[idx].address + info[idx].size)) + idx++; + while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size)) + idx++; + ptr2 = boundary; + if (pblock->next) { + if (pblock->ptr + pblock->size == pblock->next->ptr) + continue; + if (pblock->next->ptr < boundary) + ptr2 = pblock->next->ptr; + } + ptr1 = (pblock->ptr + pblock->size + align) & ~align; + if (ptr1 >= ptr2) + continue; + size1 = ptr2 - ptr1; + if ((int)size <= size1) { + block->ptr = ptr1; + block->size = size; + return 0; + } + } + while (++idx < 4) { + if (size <= info[idx].size) { + /* I assume that bank address is already aligned.. */ + block->ptr = info[idx].address; + block->size = size; + return 0; + } + } + return -ENOMEM; +} + +snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, + char *name, int size, int w_16, int align, + unsigned int *share_id) +{ + snd_gf1_mem_block_t block, *nblock; + + snd_gf1_mem_lock(alloc, 0); + if (share_id != NULL) { + nblock = snd_gf1_mem_share(alloc, share_id); + if (nblock != NULL) { + if (size != (int)nblock->size) { + /* TODO: remove in the future */ + snd_printk("snd_gf1_mem_alloc - share: sizes differ\n"); + goto __std; + } + nblock->share++; + snd_gf1_mem_lock(alloc, 1); + return NULL; + } + } + __std: + if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) { + snd_gf1_mem_lock(alloc, 1); + return NULL; + } + if (share_id != NULL) + memcpy(&block.share_id, share_id, sizeof(block.share_id)); + block.owner = owner; + block.name = snd_kmalloc_strdup(name, GFP_KERNEL); + nblock = snd_gf1_mem_xalloc(alloc, &block); + snd_gf1_mem_lock(alloc, 1); + return nblock; +} + +int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address) +{ + int result; + snd_gf1_mem_block_t *block; + + snd_gf1_mem_lock(alloc, 0); + if ((block = snd_gf1_mem_look(alloc, address)) != NULL) { + result = snd_gf1_mem_xfree(alloc, block); + snd_gf1_mem_lock(alloc, 1); + return result; + } + snd_gf1_mem_lock(alloc, 1); + return -EINVAL; +} + +int snd_gf1_mem_init(snd_gus_card_t * gus) +{ + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t block; +#ifdef CONFIG_SND_DEBUG + snd_info_entry_t *entry; +#endif + + alloc = &gus->gf1.mem_alloc; + init_MUTEX(&alloc->memory_mutex); + alloc->first = alloc->last = NULL; + if (!gus->gf1.memory) + return 0; + + memset(&block, 0, sizeof(block)); + block.owner = SNDRV_GF1_MEM_OWNER_DRIVER; + if (gus->gf1.enh_mode) { + block.ptr = 0; + block.size = 1024; + block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL); + if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + return -ENOMEM; + } + block.ptr = gus->gf1.default_voice_address; + block.size = 4; + block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL); + if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + return -ENOMEM; +#ifdef CONFIG_SND_DEBUG + if (! snd_card_proc_new(gus->card, "gusmem", &entry)) { + snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read); + entry->c.text.read_size = 256 * 1024; + } +#endif + return 0; +} + +int snd_gf1_mem_done(snd_gus_card_t * gus) +{ + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t *block, *nblock; + + alloc = &gus->gf1.mem_alloc; + block = alloc->first; + while (block) { + nblock = block->next; + snd_gf1_mem_xfree(alloc, block); + block = nblock; + } + return 0; +} + +#ifdef CONFIG_SND_DEBUG +static void snd_gf1_mem_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_gus_card_t *gus; + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t *block; + unsigned int total, used; + int i; + + gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + alloc = &gus->gf1.mem_alloc; + down(&alloc->memory_mutex); + snd_iprintf(buffer, "8-bit banks : \n "); + for (i = 0; i < 4; i++) + snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : ""); + snd_iprintf(buffer, "\n" + "16-bit banks : \n "); + for (i = total = 0; i < 4; i++) { + snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : ""); + total += alloc->banks_16[i].size; + } + snd_iprintf(buffer, "\n"); + used = 0; + for (block = alloc->first, i = 0; block; block = block->next, i++) { + used += block->size; + snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size); + if (block->share || + block->share_id[0] || block->share_id[1] || + block->share_id[2] || block->share_id[3]) + snd_iprintf(buffer, " Share : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n", + block->share, + block->share_id[0], block->share_id[1], + block->share_id[2], block->share_id[3]); + snd_iprintf(buffer, " Flags :%s\n", + block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : ""); + snd_iprintf(buffer, " Owner : "); + switch (block->owner) { + case SNDRV_GF1_MEM_OWNER_DRIVER: + snd_iprintf(buffer, "driver - %s\n", block->name); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE: + snd_iprintf(buffer, "SIMPLE wave\n"); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_GF1: + snd_iprintf(buffer, "GF1 wave\n"); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF: + snd_iprintf(buffer, "IWFFFF wave\n"); + break; + default: + snd_iprintf(buffer, "unknown\n"); + } + } + snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n", + total, used, total - used); + up(&alloc->memory_mutex); +#if 0 + ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n", + ultra_memory_free_size(card, &card->gf1.mem_alloc), + ultra_memory_free_block(card, &card->gf1.mem_alloc, 0), + ultra_memory_free_block(card, &card->gf1.mem_alloc, 1)); +#endif +} +#endif diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_mem_proc.c linux/sound/isa/gus/gus_mem_proc.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_mem_proc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_mem_proc.c 2003-01-07 03:36:29.000000000 -0700 @@ -0,0 +1,135 @@ +/* + * Copyright (c) by Jaroslav Kysela + * GUS's memory access via proc filesystem + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +typedef struct gus_proc_private { + int rom; /* data are in ROM */ + unsigned int address; + unsigned int size; + snd_gus_card_t * gus; +} gus_proc_private_t; + +static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + snd_gus_card_t *gus = priv->gus; + int err; + + size = count; + if (file->f_pos + size > priv->size) + size = (long)priv->size - file->f_pos; + if (size > 0) { + if ((err = snd_gus_dram_read(gus, buf, file->f_pos, size, priv->rom)) < 0) + return err; + file->f_pos += size; + return size; + } + return 0; +} + +static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END, offset is negative */ + file->f_pos = priv->size + offset; + break; + default: + return -EINVAL; + } + if (file->f_pos > priv->size) + file->f_pos = priv->size; + return file->f_pos; +} + +static void snd_gf1_mem_proc_free(snd_info_entry_t *entry) +{ + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return); + snd_magic_kfree(priv); +} + +static struct snd_info_entry_ops snd_gf1_mem_proc_ops = { + .read = snd_gf1_mem_proc_dump, + .llseek = snd_gf1_mem_proc_llseek, +}; + +int snd_gf1_mem_proc_init(snd_gus_card_t * gus) +{ + int idx; + char name[16]; + gus_proc_private_t *priv; + snd_info_entry_t *entry; + + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { + priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + priv->gus = gus; + sprintf(name, "gus-ram-%i", idx); + if (! snd_card_proc_new(gus->card, name, &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = priv; + entry->private_free = snd_gf1_mem_proc_free; + entry->c.ops = &snd_gf1_mem_proc_ops; + priv->address = gus->gf1.mem_alloc.banks_8[idx].address; + priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; + } + } + } + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.rom_present & (1 << idx)) { + priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + priv->rom = 1; + priv->gus = gus; + sprintf(name, "gus-rom-%i", idx); + if (! snd_card_proc_new(gus->card, name, &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = priv; + entry->private_free = snd_gf1_mem_proc_free; + entry->c.ops = &snd_gf1_mem_proc_ops; + priv->address = idx * 4096 * 1024; + priv->size = entry->size = gus->gf1.rom_memory; + } + } + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_mixer.c linux/sound/isa/gus/gus_mixer.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_mixer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_mixer.c 2003-01-31 08:19:52.000000000 -0700 @@ -0,0 +1,205 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of ICS 2101 chip and "mixer" in GF1 chip + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#define chip_t snd_gus_card_t + +/* + * + */ + +#define GF1_SINGLE(xname, xindex, shift, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_gf1_info_single, \ + .get = snd_gf1_get_single, .put = snd_gf1_put_single, \ + .private_value = shift | (invert << 8) } + +static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + + ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; + if (invert) + ucontrol->value.integer.value[0] ^= 1; + return 0; +} + +static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int change; + unsigned char oval, nval; + + nval = ucontrol->value.integer.value[0] & 1; + if (invert) + nval ^= 1; + nval <<= shift; + spin_lock_irqsave(&gus->reg_lock, flags); + oval = gus->mix_cntrl_reg; + nval = (oval & ~(1 << shift)) | nval; + change = nval != oval; + outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); + outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +#define ICS_DOUBLE(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ics_info_double, \ + .get = snd_ics_get_double, .put = snd_ics_put_double, \ + .private_value = addr } + +static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value & 0xff; + unsigned char left, right; + + spin_lock_irqsave(&gus->reg_lock, flags); + left = gus->gf1.ics_regs[addr][0]; + right = gus->gf1.ics_regs[addr][1]; + spin_unlock_irqrestore(&gus->reg_lock, flags); + ucontrol->value.integer.value[0] = left & 127; + ucontrol->value.integer.value[1] = right & 127; + return 0; +} + +static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value & 0xff; + int change; + unsigned char val1, val2, oval1, oval2, tmp; + + val1 = ucontrol->value.integer.value[0] & 127; + val2 = ucontrol->value.integer.value[1] & 127; + spin_lock_irqsave(&gus->reg_lock, flags); + oval1 = gus->gf1.ics_regs[addr][0]; + oval2 = gus->gf1.ics_regs[addr][1]; + change = val1 != oval1 || val2 != oval2; + gus->gf1.ics_regs[addr][0] = val1; + gus->gf1.ics_regs[addr][1] = val2; + if (gus->ics_flag && gus->ics_flipped && + (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) { + tmp = val1; + val1 = val2; + val2 = tmp; + } + addr <<= 3; + outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); + outb(1, GUSP(gus, MIXDATAPORT)); + outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); + outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); + outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); + outb(2, GUSP(gus, MIXDATAPORT)); + outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); + outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +#define GF1_CONTROLS (sizeof(snd_gf1_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_gf1_controls[] = { +GF1_SINGLE("Master Playback Switch", 0, 1, 1), +GF1_SINGLE("Line Switch", 0, 0, 1), +GF1_SINGLE("Mic Switch", 0, 2, 0) +}; + +#define ICS_CONTROLS (sizeof(snd_ics_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ics_controls[] = { +GF1_SINGLE("Master Playback Switch", 0, 1, 1), +ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), +ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), +GF1_SINGLE("Line Switch", 0, 0, 1), +ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), +GF1_SINGLE("Mic Switch", 0, 2, 0), +ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), +ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) +}; + +int snd_gf1_new_mixer(snd_gus_card_t * gus) +{ + snd_card_t *card; + unsigned int idx, max; + int err; + + snd_assert(gus != NULL, return -EINVAL); + card = gus->card; + snd_assert(card != NULL, return -EINVAL); + + if (gus->ics_flag) + snd_component_add(card, "ICS2101"); + if (card->mixername[0] == '\0') { + strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); + } else { + if (gus->ics_flag) + strcat(card->mixername, ",ICS2101"); + strcat(card->mixername, ",GF1"); + } + + if (!gus->ics_flag) { + max = gus->ess_flag ? 1 : GF1_CONTROLS; + for (idx = 0; idx < max; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0) + return err; + } + } else { + for (idx = 0; idx < ICS_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0) + return err; + } + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_pcm.c linux/sound/isa/gus/gus_pcm.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_pcm.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_pcm.c 2003-03-01 12:04:29.000000000 -0700 @@ -0,0 +1,891 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of GF1 chip (PCM things) + * + * InterWave chips supports interleaved DMA, but this feature isn't used in + * this code. + * + * This code emulates autoinit DMA transfer for playback, recording by GF1 + * chip doesn't support autoinit DMA. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "gus_tables.h" + +#define chip_t snd_gus_card_t + +/* maximum rate */ + +#define SNDRV_GF1_PCM_RATE 48000 + +#define SNDRV_GF1_PCM_PFLG_NONE 0 +#define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) +#define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) + +typedef struct { + snd_gus_card_t * gus; + snd_pcm_substream_t * substream; + spinlock_t lock; + unsigned int voices; + snd_gus_voice_t *pvoices[2]; + unsigned int memory; + unsigned short flags; + unsigned char voice_ctrl, ramp_ctrl; + unsigned int bpos; + unsigned int blocks; + unsigned int block_size; + unsigned int dma_size; + wait_queue_head_t sleep; + atomic_t dma_count; + int final_volume; +} gus_pcm_private_t; + +static int snd_gf1_pcm_use_dma = 1; + +static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data) +{ + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, private_data, return); + + if (pcmp) { + atomic_dec(&pcmp->dma_count); + wake_up(&pcmp->sleep); + } +} + +static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream, + unsigned int offset, + unsigned int addr, + unsigned int count) +{ + snd_gf1_dma_block_t block; + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + count += offset & 31; + offset &= ~31; + // snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count); + memset(&block, 0, sizeof(block)); + block.cmd = SNDRV_GF1_DMA_IRQ; + if (snd_pcm_format_unsigned(runtime->format)) + block.cmd |= SNDRV_GF1_DMA_UNSIGNED; + if (snd_pcm_format_width(runtime->format) == 16) + block.cmd |= SNDRV_GF1_DMA_16BIT; + block.addr = addr & ~31; + block.buffer = runtime->dma_area + offset; + block.buf_addr = runtime->dma_addr + offset; + block.count = count; + block.private_data = pcmp; + block.ack = snd_gf1_pcm_block_change_ack; + if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) + atomic_inc(&pcmp->dma_count); + return 0; +} + +static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + snd_gus_card_t * gus = pcmp->gus; + unsigned long flags; + unsigned char voice_ctrl, ramp_ctrl; + unsigned short rate; + unsigned int curr, begin, end; + unsigned short vol; + unsigned char pan; + unsigned int voice; + + if (substream == NULL) + return; + spin_lock_irqsave(&pcmp->lock, flags); + if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { + spin_unlock_irqrestore(&pcmp->lock, flags); + return; + } + pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; + pcmp->final_volume = 0; + spin_unlock_irqrestore(&pcmp->lock, flags); + rate = snd_gf1_translate_freq(gus, runtime->rate << 4); + /* enable WAVE IRQ */ + voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; + /* enable RAMP IRQ + rollover */ + ramp_ctrl = 0x24; + if (pcmp->blocks == 1) { + voice_ctrl |= 0x08; /* loop enable */ + ramp_ctrl &= ~0x04; /* disable rollover */ + } + for (voice = 0; voice < pcmp->voices; voice++) { + begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); + curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; + end = curr + (pcmp->block_size / runtime->channels); + end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; + // snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate); + pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; + vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + spin_lock_irqsave(&gus->reg_lock, flags); + for (voice = 0; voice < pcmp->voices; voice++) { + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + voice_ctrl &= ~0x20; + } + voice_ctrl |= 0x20; + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + for (voice = 0; voice < pcmp->voices; voice++) { + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + voice_ctrl &= ~0x20; /* disable IRQ for next voice */ + } + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice) +{ + gus_pcm_private_t * pcmp; + snd_pcm_runtime_t * runtime; + unsigned char voice_ctrl, ramp_ctrl; + unsigned int idx; + unsigned int end, step; + + if (!pvoice->private_data) { + snd_printd("snd_gf1_pcm: unknown wave irq?\n"); + snd_gf1_smart_stop_voice(gus, pvoice->number); + return; + } + pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + if (pcmp == NULL) { + snd_printd("snd_gf1_pcm: unknown wave irq?\n"); + snd_gf1_smart_stop_voice(gus, pvoice->number); + return; + } + gus = pcmp->gus; + runtime = pcmp->substream->runtime; + + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; + ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; +#if 0 + snd_gf1_select_voice(gus, pvoice->number); + printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); + snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); + printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); + snd_gf1_select_voice(gus, pvoice->number); +#endif + pcmp->bpos++; + pcmp->bpos %= pcmp->blocks; + if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ + voice_ctrl |= 0x08; /* enable loop */ + } else { + ramp_ctrl |= 0x04; /* enable rollover */ + } + end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); + end -= voice_ctrl & 4 ? 2 : 1; + step = pcmp->dma_size / runtime->channels; + voice_ctrl |= 0x20; + if (!pcmp->final_volume) { + ramp_ctrl |= 0x20; + ramp_ctrl &= ~0x03; + } + for (idx = 0; idx < pcmp->voices; idx++, end += step) { + snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + voice_ctrl &= ~0x20; + } + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + voice_ctrl |= 0x20; + for (idx = 0; idx < pcmp->voices; idx++) { + snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + voice_ctrl &= ~0x20; + } + } + spin_unlock(&gus->reg_lock); + + snd_pcm_period_elapsed(pcmp->substream); +#if 0 + if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && + *runtime->state == SNDRV_PCM_STATE_RUNNING) { + end = pcmp->bpos * pcmp->block_size; + if (runtime->channels > 1) { + snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); + snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); + } else { + snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); + } + } +#endif +} + +static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice) +{ + unsigned short vol; + int cvoice; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + + /* stop ramp, but leave rollover bit untouched */ + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + spin_unlock(&gus->reg_lock); + if (pcmp == NULL) + return; + /* are we active? */ + if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) + return; + /* load real volume - better precision */ + cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; + if (pcmp->substream == NULL) + return; + vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); + pcmp->final_volume = 1; + spin_unlock(&gus->reg_lock); +} + +static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus) +{ +} + +static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf, + unsigned int pos, unsigned int count, + int w16, int invert) +{ + unsigned int len; + unsigned long flags; + + // printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port); + while (count > 0) { + len = count; + if (len > 512) /* limit, to allow IRQ */ + len = 512; + count -= len; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); + snd_gf1_dram_addr(gus, pos); + if (w16) { + outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); + outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); + } else { + outsb(GUSP(gus, DRAM), buf, len); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + buf += 512; + pos += 512; + } else { + invert = invert ? 0x80 : 0x00; + if (w16) { + len >>= 1; + while (len--) { + snd_gf1_poke(gus, pos++, *buf++); + snd_gf1_poke(gus, pos++, *buf++ ^ invert); + } + } else { + while (len--) + snd_gf1_poke(gus, pos++, *buf++ ^ invert); + } + } + schedule_timeout(1); + if (signal_pending(current)) + return -EAGAIN; + } + return 0; +} + +static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream, + int voice, + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int bpos, len; + + bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); + len = samples_to_bytes(runtime, count); + snd_assert(bpos <= pcmp->dma_size, return -EIO); + snd_assert(bpos + len <= pcmp->dma_size, return -EIO); + if (copy_from_user(runtime->dma_area + bpos, src, len)) + return -EFAULT; + if (snd_gf1_pcm_use_dma && len > 32) { + return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); + } else { + snd_gus_card_t *gus = pcmp->gus; + int err, w16, invert; + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) + return err; + } + return 0; +} + +static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream, + int voice, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int bpos, len; + + bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); + len = samples_to_bytes(runtime, count); + snd_assert(bpos <= pcmp->dma_size, return -EIO); + snd_assert(bpos + len <= pcmp->dma_size, return -EIO); + snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count); + if (snd_gf1_pcm_use_dma && len > 32) { + return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); + } else { + snd_gus_card_t *gus = pcmp->gus; + int err, w16, invert; + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) + return err; + } + return 0; +} + +static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + snd_gf1_mem_block_t *block; + if (pcmp->memory > 0) { + snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); + pcmp->memory = 0; + } + if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_DRIVER, + "GF1 PCM", + runtime->dma_bytes, 1, 32, + NULL)) == NULL) + return -ENOMEM; + pcmp->memory = block->ptr; + } + pcmp->voices = params_channels(hw_params); + if (pcmp->pvoices[0] == NULL) { + if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) + return -ENOMEM; + pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; + pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; + pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; + pcmp->pvoices[0]->private_data = pcmp; + } + if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { + if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) + return -ENOMEM; + pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; + pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; + pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; + pcmp->pvoices[1]->private_data = pcmp; + } else if (pcmp->voices == 1) { + if (pcmp->pvoices[1]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); + pcmp->pvoices[1] = NULL; + } + } + return 0; +} + +static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + snd_pcm_lib_free_pages(substream); + if (pcmp->pvoices[0]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); + pcmp->pvoices[0] = NULL; + } + if (pcmp->pvoices[1]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); + pcmp->pvoices[1] = NULL; + } + if (pcmp->memory > 0) { + snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); + pcmp->memory = 0; + } + return 0; +} + +static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + pcmp->bpos = 0; + pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); + pcmp->block_size = snd_pcm_lib_period_bytes(substream); + pcmp->blocks = pcmp->dma_size / pcmp->block_size; + return 0; +} + +static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + int voice; + + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_gf1_pcm_trigger_up(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + spin_lock(&pcmp->lock); + pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; + spin_unlock(&pcmp->lock); + voice = pcmp->pvoices[0]->number; + snd_gf1_stop_voices(gus, voice, voice); + if (pcmp->pvoices[1]) { + voice = pcmp->pvoices[1]->number; + snd_gf1_stop_voices(gus, voice, voice); + } + } else { + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int pos; + unsigned char voice_ctrl; + + pos = 0; + spin_lock(&gus->reg_lock); + if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { + snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); + voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; + if (substream->runtime->channels > 1) + pos <<= 1; + pos = bytes_to_frames(runtime, pos); + } + spin_unlock(&gus->reg_lock); + return pos; +} + +static ratnum_t clock = { + .num = 9878400/16, + .den_min = 2, + .den_max = 257, + .den_step = 1, +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + .nrats = 1, + .rats = &clock, +}; + +static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->c_dma_size = params_buffer_bytes(hw_params); + gus->c_period_size = params_period_bytes(hw_params); + gus->c_pos = 0; + gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ + if (params_channels(hw_params) > 1) + gus->gf1.pcm_rcntrl_reg |= 2; + if (gus->gf1.dma2 > 3) + gus->gf1.pcm_rcntrl_reg |= 4; + if (snd_pcm_format_unsigned(params_format(hw_params))) + gus->gf1.pcm_rcntrl_reg |= 0x80; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ + snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); + return 0; +} + +static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + int val; + + if (cmd == SNDRV_PCM_TRIGGER_START) { + val = gus->gf1.pcm_rcntrl_reg; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + val = 0; + } else { + return -EINVAL; + } + + spin_lock(&gus->reg_lock); + snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); + snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); + spin_unlock(&gus->reg_lock); + return 0; +} + +static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size); + pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); + return pos; +} + +static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ + if (gus->pcm_cap_substream != NULL) { + snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); + snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); + gus->c_pos += gus->c_period_size; + snd_pcm_period_elapsed(gus->pcm_cap_substream); + } +} + +static snd_pcm_hardware_t snd_gf1_pcm_playback = +{ + .info = SNDRV_PCM_INFO_NONINTERLEAVED, + .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_gf1_pcm_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + .rate_min = 5510, + .rate_max = 44100, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime) +{ + gus_pcm_private_t * pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + snd_magic_kfree(pcmp); +} + +static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream) +{ + gus_pcm_private_t *pcmp; + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + pcmp = snd_magic_kcalloc(gus_pcm_private_t, 0, GFP_KERNEL); + if (pcmp == NULL) + return -ENOMEM; + pcmp->gus = gus; + spin_lock_init(&pcmp->lock); + init_waitqueue_head(&pcmp->sleep); + atomic_set(&pcmp->dma_count, 0); + + runtime->private_data = pcmp; + runtime->private_free = snd_gf1_pcm_playback_free; + +#if 0 + printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); +#endif + if ((err = snd_gf1_dma_init(gus)) < 0) + return err; + pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; + pcmp->substream = substream; + runtime->hw = snd_gf1_pcm_playback; + snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); + return 0; +} + +static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned long jiffies_old; + + jiffies_old = jiffies; + while (atomic_read(&pcmp->dma_count) > 0) { + interruptible_sleep_on_timeout(&pcmp->sleep, 1); + if ((signed long)(jiffies - jiffies_old) > 2*HZ) { + snd_printk("gf1 pcm - serious DMA problem\n"); + break; + } + } + snd_gf1_dma_done(gus); + return 0; +} + +static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; + gus->pcm_cap_substream = substream; + substream->runtime->hw = snd_gf1_pcm_capture; + snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->pcm_cap_substream = NULL; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); + return 0; +} + +static void snd_gf1_pcm_free(snd_pcm_t *pcm) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, pcm->private_data, return); + gus->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); + ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; + ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; + spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); + return 0; +} + +static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int idx; + unsigned short val1, val2, vol; + gus_pcm_private_t *pcmp; + snd_gus_voice_t *pvoice; + + val1 = ucontrol->value.integer.value[0] & 127; + val2 = ucontrol->value.integer.value[1] & 127; + spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); + change = val1 != gus->gf1.pcm_volume_level_left1 || + val2 != gus->gf1.pcm_volume_level_right1; + gus->gf1.pcm_volume_level_left1 = val1; + gus->gf1.pcm_volume_level_right1 = val2; + gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; + gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; + spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); + /* are we active? */ + spin_lock_irqsave(&gus->voice_alloc, flags); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (!pvoice->pcm) + continue; + pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return -ENXIO); + if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) + continue; + /* load real volume - better precision */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); + pcmp->final_volume = 1; + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return change; +} + +static snd_kcontrol_new_t snd_gf1_pcm_volume_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = snd_gf1_pcm_volume_info, + .get = snd_gf1_pcm_volume_get, + .put = snd_gf1_pcm_volume_put +}; + +static snd_pcm_ops_t snd_gf1_pcm_playback_ops = { + .open = snd_gf1_pcm_playback_open, + .close = snd_gf1_pcm_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_gf1_pcm_playback_hw_params, + .hw_free = snd_gf1_pcm_playback_hw_free, + .prepare = snd_gf1_pcm_playback_prepare, + .trigger = snd_gf1_pcm_playback_trigger, + .pointer = snd_gf1_pcm_playback_pointer, + .copy = snd_gf1_pcm_playback_copy, + .silence = snd_gf1_pcm_playback_silence, +}; + +static snd_pcm_ops_t snd_gf1_pcm_capture_ops = { + .open = snd_gf1_pcm_capture_open, + .close = snd_gf1_pcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_gf1_pcm_capture_hw_params, + .hw_free = snd_gf1_pcm_capture_hw_free, + .prepare = snd_gf1_pcm_capture_prepare, + .trigger = snd_gf1_pcm_capture_trigger, + .pointer = snd_gf1_pcm_capture_pointer, +}; + +int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm) +{ + snd_card_t *card; + snd_kcontrol_t *kctl; + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + int capture, err; + + if (rpcm) + *rpcm = NULL; + card = gus->card; + capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; + err = snd_pcm_new(card, + gus->interwave ? "AMD InterWave" : "GF1", + pcm_dev, + gus->gf1.pcm_channels / 2, + capture, + &pcm); + if (err < 0) + return err; + pcm->private_data = gus; + pcm->private_free = snd_gf1_pcm_free; + /* playback setup */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); + + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_isa_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + if (capture) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); + if (gus->gf1.dma2 == gus->gf1.dma1) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); + } + strcpy(pcm->name, pcm->id); + if (gus->interwave) { + sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); + } + strcat(pcm->name, " (synth)"); + gus->pcm = pcm; + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus))) < 0) + return err; + kctl->id.index = control_index; + + if (rpcm) + *rpcm = pcm; + return 0; +} + diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_reset.c linux/sound/isa/gus/gus_reset.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_reset.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_reset.c 2003-01-07 03:36:29.000000000 -0700 @@ -0,0 +1,418 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +extern void snd_gf1_timers_init(snd_gus_card_t * gus); +extern void snd_gf1_timers_done(snd_gus_card_t * gus); +extern int snd_gf1_synth_init(snd_gus_card_t * gus); +extern void snd_gf1_synth_done(snd_gus_card_t * gus); + +/* + * ok.. default interrupt handlers... + */ + +static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus) +{ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20); +} + +static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus) +{ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80); +} + +static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4); +} + +static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8); +} + +static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice) +{ + snd_gf1_i_ctrl_stop(gus, 0x00); + snd_gf1_i_ctrl_stop(gus, 0x0d); +} + +static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, 0x41, 0x00); +} + +static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, 0x49, 0x00); +} + +void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what) +{ + if (what & SNDRV_GF1_HANDLER_MIDI_OUT) + gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out; + if (what & SNDRV_GF1_HANDLER_MIDI_IN) + gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in; + if (what & SNDRV_GF1_HANDLER_TIMER1) + gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1; + if (what & SNDRV_GF1_HANDLER_TIMER2) + gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2; + if (what & SNDRV_GF1_HANDLER_VOICE) { + snd_gus_voice_t *voice; + + voice = &gus->gf1.voices[what & 0xffff]; + voice->handler_wave = + voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume; + voice->handler_effect = NULL; + voice->volume_change = NULL; + } + if (what & SNDRV_GF1_HANDLER_DMA_WRITE) + gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write; + if (what & SNDRV_GF1_HANDLER_DMA_READ) + gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read; +} + +/* + + */ + +static void snd_gf1_clear_regs(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + inb(GUSP(gus, IRQSTAT)); + snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */ + snd_gf1_write8(gus, 0x45, 0); /* Timer Control */ + snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */ + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void snd_gf1_look_regs(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */ + snd_gf1_look8(gus, 0x49); /* Sampling Control Register */ + inb(GUSP(gus, IRQSTAT)); + snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */ + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +/* + * put selected GF1 voices to initial stage... + */ + +void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice); +#if 0 + printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); +#endif + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice); +#if 0 + printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); +#endif + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO); + snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO); +#endif +} + +void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) +{ + unsigned long flags; + unsigned int daddr; + unsigned short i, w_16; + + daddr = gus->gf1.default_voice_address << 4; + for (i = v_min; i <= v_max; i++) { +#if 0 + if (gus->gf1.syn_voices) + gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC; +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, i); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */ + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */ + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82); /* Deactivate voice */ + w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04; + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7); + if (gus->gf1.enh_mode) { + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO); + snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO); +#endif + } +} + +void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) +{ + unsigned long flags; + short i, ramp_ok; + unsigned short ramp_end; + long time; + + if (!in_interrupt()) { /* this can't be done in interrupt */ + for (i = v_min, ramp_ok = 0; i <= v_max; i++) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, i); + ramp_end = snd_gf1_read16(gus, 9) >> 8; + if (ramp_end > SNDRV_GF1_MIN_OFFSET) { + ramp_ok++; + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20); /* ramp rate */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); /* ramp start */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end); /* ramp end */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); /* ramp down */ + if (gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); + } + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + time = HZ / 20; + while (time > 0 && !signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + } + } + snd_gf1_clear_voices(gus, v_min, v_max); +} + +static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, + snd_gus_voice_t * pvoice, + int type, int client, int port) +{ + pvoice->use = 1; + switch (type) { + case SNDRV_GF1_VOICE_TYPE_PCM: + gus->gf1.pcm_alloc_voices++; + pvoice->pcm = 1; + break; + case SNDRV_GF1_VOICE_TYPE_SYNTH: + pvoice->synth = 1; + pvoice->client = client; + pvoice->port = port; + break; + case SNDRV_GF1_VOICE_TYPE_MIDI: + pvoice->midi = 1; + pvoice->client = client; + pvoice->port = port; + break; + } +} + +snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port) +{ + snd_gus_voice_t *pvoice; + unsigned long flags; + int idx; + + spin_lock_irqsave(&gus->voice_alloc, flags); + if (type == SNDRV_GF1_VOICE_TYPE_PCM) { + if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) { + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return NULL; + } + } + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (!pvoice->use) { + snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return pvoice; + } + } + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (pvoice->midi && !pvoice->client) { + snd_gf1_clear_voices(gus, pvoice->number, pvoice->number); + snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return pvoice; + } + } + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return NULL; +} + +void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice) +{ + unsigned long flags; + void (*private_free)(snd_gus_voice_t *voice); + void *private_data; + + if (voice == NULL || !voice->use) + return; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number); + snd_gf1_clear_voices(gus, voice->number, voice->number); + spin_lock_irqsave(&gus->voice_alloc, flags); + private_free = voice->private_free; + private_data = voice->private_data; + voice->private_free = NULL; + voice->private_data = NULL; + if (voice->pcm) + gus->gf1.pcm_alloc_voices--; + voice->use = voice->pcm = 0; + voice->sample_ops = NULL; + spin_unlock_irqrestore(&gus->voice_alloc, flags); + if (private_free) + private_free(voice); +} + +/* + * call this function only by start of driver + */ + +int snd_gf1_start(snd_gus_card_t * gus) +{ + unsigned long flags; + unsigned int i; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); + + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); + for (i = 0; i < 32; i++) { + gus->gf1.voices[i].number = i; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + } + + snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ + + if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + } + snd_gf1_clear_regs(gus); + snd_gf1_select_active_voices(gus); + snd_gf1_delay(gus); + gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; + /* initialize LFOs & clear LFOs memory */ + if (gus->gf1.enh_mode && gus->gf1.memory) { + gus->gf1.hw_lfo = 1; + gus->gf1.default_voice_address += 1024; + } else { + gus->gf1.sw_lfo = 1; + } +#if 0 + snd_gf1_lfo_init(gus); +#endif + if (gus->gf1.memory > 0) + for (i = 0; i < 4; i++) + snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0); + snd_gf1_clear_regs(gus); + snd_gf1_clear_voices(gus, 0, 31); + snd_gf1_look_regs(gus); + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ + if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + } + while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + snd_gf1_timers_init(gus); + snd_gf1_look_regs(gus); + snd_gf1_mem_init(gus); + snd_gf1_mem_proc_init(gus); +#ifdef CONFIG_SND_DEBUG + snd_gus_irq_profile_init(gus); +#endif + +#if 0 + if (gus->pnp_flag) { + if (gus->chip.playback_fifo_size > 0) + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8); + if (gus->chip.record_fifo_size > 0) + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8); + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg); + } +#endif + + return 0; +} + +/* + * call this function only by shutdown of driver + */ + +int snd_gf1_stop(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */ + snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ + snd_gf1_timers_done(gus); + snd_gf1_mem_done(gus); +#if 0 + snd_gf1_lfo_done(gus); +#endif + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_sample.c linux/sound/isa/gus/gus_sample.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_sample.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_sample.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,155 @@ +/* + * Routines for Gravis UltraSound soundcards - Sample support + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +/* + * + */ + +static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v) +{ + snd_seq_kinstr_t *instr; + +#if 0 + printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n", + v->instr.cluster, + v->instr.std, + v->instr.bank, + v->instr.prg); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1); + if (instr != NULL) { + if (instr->ops) { + if (instr->ops->instr_type == snd_seq_simple_id) + snd_gf1_simple_init(v); + } + snd_seq_instr_free_use(gus->gf1.ilist, instr); + } +} + +/* + * + */ + +static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.std = ev->data.sample.param.sample.std; + if (v->instr.std & 0xff000000) { /* private instrument */ + v->instr.std &= 0x00ffffff; + v->instr.std |= (unsigned int)ev->source.client << 24; + } + v->instr.bank = ev->data.sample.param.sample.bank; + v->instr.prg = ev->data.sample.param.sample.prg; + select_instrument(p->gus, v); +} + +static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.cluster = ev->data.sample.param.cluster.cluster; + select_instrument(p->gus, v); +} + +static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_start) + v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position); +} + +static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode); +} + +static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_freq) + v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency); +} + +static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_volume) + v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume); +} + +static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_loop) + v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop); +} + +static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_pos) + v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position); +} + +static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_private1) + v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8); +} + +typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v); + +static gus_sample_event_handler_t *gus_sample_event_handlers[9] = { + event_sample, + event_cluster, + event_start, + event_stop, + event_freq, + event_volume, + event_loop, + event_position, + event_private1 +}; + +void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p) +{ + int idx, voice; + snd_gus_card_t *gus = p->gus; + snd_gus_voice_t *v; + unsigned long flags; + + idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; + if (idx < 0 || idx > 8) + return; + for (voice = 0; voice < 32; voice++) { + v = &gus->gf1.voices[voice]; + if (v->use && v->client == ev->source.client && + v->port == ev->source.port && + v->index == ev->data.sample.channel) { + spin_lock_irqsave(&gus->event_lock, flags); + gus_sample_event_handlers[idx](ev, p, v); + spin_unlock_irqrestore(&gus->event_lock, flags); + return; + } + } +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_simple.c linux/sound/isa/gus/gus_simple.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_simple.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_simple.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,634 @@ +/* + * Routines for Gravis UltraSound soundcards - Simple instrument handlers + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "gus_tables.h" + +/* + * + */ + +static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice); + +static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); +static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); +static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); +static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); +static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); +static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); +static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); + +static snd_gus_sample_ops_t sample_ops = { + sample_start, + sample_stop, + sample_freq, + sample_volume, + sample_loop, + sample_pos, + sample_private1 +}; + +#if 0 + +static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait); +static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice); + +static struct snd_gus_note_handlers note_commands = { + note_stop, + note_wait, + note_off, + note_volume, + note_pitchbend, + note_vibrato, + note_tremolo +}; + +static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ); +static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ); +static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ); + +static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = { + chn_trigger_down, + chn_trigger_up, + chn_control +}; + +#endif + +static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); + +/* + * + */ + +static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + snd_gf1_stop_voice(gus, voice->number); + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); + spin_unlock(&gus->reg_lock); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + spin_unlock(&gus->event_lock); +} + +static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + if (voice->flags & SNDRV_GF1_VFLG_RUNNING) + do_volume_envelope(gus, voice); + else + snd_gf1_stop_voice(gus, voice->number); + spin_unlock(&gus->event_lock); +} + +static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) == + (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) + do_pan_envelope(gus, voice); + spin_unlock(&gus->event_lock); +} + +/* + * + */ + +static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + unsigned short next, rate, old_volume; + int program_next_ramp; + unsigned long flags; + + if (!gus->gf1.volume_ramp) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume); + printk("gf1_volume = 0x%x\n", voice->gf1_volume); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } + program_next_ramp = 0; + rate = next = 0; + while (1) { + program_next_ramp = 0; + rate = next = 0; + switch (voice->venv_state) { + case VENV_BEFORE: + voice->venv_state = VENV_ATTACK; + voice->venv_value_next = 0; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); + spin_unlock_irqrestore(&gus->reg_lock, flags); + break; + case VENV_ATTACK: + voice->venv_state = VENV_SUSTAIN; + program_next_ramp++; + next = 255; + rate = gus->gf1.volume_ramp; + break; + case VENV_SUSTAIN: + voice->venv_state = VENV_RELEASE; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + case VENV_RELEASE: + voice->venv_state = VENV_DONE; + program_next_ramp++; + next = 0; + rate = gus->gf1.volume_ramp; + break; + case VENV_DONE: + snd_gf1_stop_voice(gus, voice->number); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + return; + case VENV_VOLUME: + program_next_ramp++; + next = voice->venv_value_next; + rate = gus->gf1.volume_ramp; + voice->venv_state = voice->venv_state_prev; + break; + } + voice->venv_value_next = next; + if (!program_next_ramp) + continue; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8; + if (!rate) { + spin_unlock_irqrestore(&gus->reg_lock, flags); + continue; + } + next = (((int)voice->gf1_volume * (int)next) / 255) >> 8; + if (old_volume < SNDRV_GF1_MIN_OFFSET) + old_volume = SNDRV_GF1_MIN_OFFSET; + if (next < SNDRV_GF1_MIN_OFFSET) + next = SNDRV_GF1_MIN_OFFSET; + if (next > SNDRV_GF1_MAX_OFFSET) + next = SNDRV_GF1_MAX_OFFSET; + if (old_volume == next) { + spin_unlock_irqrestore(&gus->reg_lock, flags); + continue; + } + voice->volume_control &= ~0xc3; + voice->volume_control |= 0x20; + if (old_volume > next) { + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume); + voice->volume_control |= 0x40; + } else { + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next); + } + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } +} + +static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + unsigned long flags; + unsigned char old_pan; + +#if 0 + snd_gf1_select_voice(gus, voice->number); + printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", + voice->number, + voice->flags, + voice->gf1_pan, + snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f); +#endif + if (gus->gf1.enh_mode) { + voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); + return; + } + if (!gus->gf1.smooth_pan) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } + if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */ + voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f; + if (old_pan > voice->gf1_pan ) + old_pan--; + if (old_pan < voice->gf1_pan) + old_pan++; + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan); + spin_unlock_irqrestore(&gus->reg_lock, flags); + if (old_pan == voice->gf1_pan) /* the goal was reached */ + voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); +#if 0 + snd_gf1_select_voice(gus, voice->number); + printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", + voice->number, + voice->flags, + voice->gf1_pan, + snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f); +#endif +} + +static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan) +{ + unsigned long flags; + unsigned short vlo, vro; + + vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan); + vro = SNDRV_GF1_ATTEN(pan); + if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) { + vlo >>= 1; + vro >>= 1; + } + vlo <<= 4; + vro <<= 4; +#if 0 + printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n", + vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT), + vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT)); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro); + spin_unlock_irqrestore(&gus->reg_lock, flags); + voice->vlo = vlo; + voice->vro = vro; +} + +/* + * + */ + +static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) +{ + unsigned long flags; + unsigned int begin, addr, addr_end, addr_start; + int w_16; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory << 4; + w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0; + addr_start = simple->loop_start; + if (simple->format & SIMPLE_WAVE_LOOP) { + addr_end = simple->loop_end; + } else { + addr_end = (simple->size << 4) - (w_16 ? 40 : 24); + } + if (simple->format & SIMPLE_WAVE_BACKWARD) { + addr = simple->loop_end; + if (position < simple->loop_end) + addr -= position; + } else { + addr = position; + } + voice->control = 0x00; + voice->mode = 0x20; /* enable offset registers */ + if (simple->format & SIMPLE_WAVE_16BIT) + voice->control |= 0x04; + if (simple->format & SIMPLE_WAVE_BACKWARD) + voice->control |= 0x40; + if (simple->format & SIMPLE_WAVE_LOOP) { + voice->control |= 0x08; + } else { + voice->control |= 0x20; + } + if (simple->format & SIMPLE_WAVE_BIDIR) + voice->control |= 0x10; + if (simple->format & SIMPLE_WAVE_ULAW) + voice->mode |= 0x40; + if (w_16) { + addr = ((addr << 1) & ~0x1f) | (addr & 0x0f); + addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f); + addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f); + } + addr += begin; + addr_start += begin; + addr_end += begin; + snd_gf1_stop_voice(gus, voice->number); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); + voice->venv_state = VENV_BEFORE; + voice->volume_control = 0x03; + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); + if (!gus->gf1.enh_mode) { + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); + } else { + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro); + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + do_volume_envelope(gus, voice); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control ); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_print_voice_registers(gus); +#endif + voice->flags |= SNDRV_GF1_VFLG_RUNNING; + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode) +{ + unsigned char control; + unsigned long flags; + + if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING)) + return; + switch (mode) { + default: + if (gus->gf1.volume_ramp > 0) { + if (voice->venv_state < VENV_RELEASE) { + voice->venv_state = VENV_RELEASE; + do_volume_envelope(gus, voice); + } + } + if (mode != SAMPLE_STOP_VENVELOPE) { + snd_gf1_stop_voice(gus, voice->number); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); + spin_unlock_irqrestore(&gus->reg_lock, flags); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + } + break; + case SAMPLE_STOP_LOOP: /* disable loop only */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + control &= ~(0x83 | 0x04); + control |= 0x20; + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control); + spin_unlock_irqrestore(&gus->reg_lock, flags); + break; + } +} + +static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + voice->fc_register = snd_gf1_translate_freq(gus, freq); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume) +{ + if (volume->volume >= 0) { + volume->volume &= 0x3fff; + voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4; + voice->venv_state_prev = VENV_SUSTAIN; + voice->venv_state = VENV_VOLUME; + do_volume_envelope(gus, voice); + } + if (volume->lr >= 0) { + volume->lr &= 0x3fff; + if (!gus->gf1.enh_mode) { + voice->gf1_pan = (volume->lr >> 10) & 15; + if (!gus->gf1.full_range_pan) { + if (voice->gf1_pan == 0) + voice->gf1_pan++; + if (voice->gf1_pan == 15) + voice->gf1_pan--; + } + voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */ + do_pan_envelope(gus, voice); + } else { + set_enhanced_pan(gus, voice, volume->lr >> 7); + } + } +} + +static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop) +{ + unsigned long flags; + int w_16 = voice->control & 0x04; + unsigned int begin, addr_start, addr_end; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + +#if 0 + printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory; + addr_start = loop->start; + addr_end = loop->end; + addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin; + addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) +{ + unsigned long flags; + int w_16 = voice->control & 0x04; + unsigned int begin, addr; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + +#if 0 + printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory; + addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +#if 0 + +static unsigned char get_effects_mask( ultra_card_t *card, int value ) +{ + if ( value > 7 ) return 0; + if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE ) + return card -> gf1.effects -> chip.interwave.voice_output[ value ]; + return 0; +} + +#endif + +static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data) +{ +#if 0 + unsigned long flags; + unsigned char uc; + + switch ( *data ) { + case ULTRA_PRIV1_IW_EFFECT: + uc = get_effects_mask( card, ultra_get_byte( data, 4 ) ); + uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 ); + uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) ); + uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 ); + voice -> data.simple.effect_accumulator = uc; + voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4; + if ( !card -> gf1.enh_mode ) return; + if ( voice -> flags & VFLG_WAIT_FOR_START ) return; + if ( voice -> flags & VFLG_RUNNING ) + { + CLI( &flags ); + gf1_select_voice( card, voice -> number ); + ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator ); + ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume ); + STI( &flags ); + } + break; + case ULTRA_PRIV1_IW_LFO: + ultra_lfo_command( card, voice -> number, data ); + } +#endif +} + +#if 0 + +/* + * + */ + +static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait ) +{ +} + +static void note_wait( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_off( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_volume( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +/* + * + */ + +static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ) +{ +} + +static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ) +{ +} + +static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ) +{ +} + +/* + * + */ + +#endif + +void snd_gf1_simple_init(snd_gus_voice_t *voice) +{ + voice->handler_wave = interrupt_wave; + voice->handler_volume = interrupt_volume; + voice->handler_effect = interrupt_effect; + voice->volume_change = NULL; + voice->sample_ops = &sample_ops; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_synth.c linux/sound/isa/gus/gus_synth.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_synth.c 2003-02-28 08:08:24.000000000 -0700 @@ -0,0 +1,325 @@ +/* + * Routines for Gravis UltraSound soundcards - Synthesizer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer"); +MODULE_LICENSE("GPL"); + +/* + * + */ + +static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port) +{ + int idx; + snd_gus_voice_t * voice; + + for (idx = 0; idx < 32; idx++) { + voice = &gus->gf1.voices[idx]; + if (voice->use && voice->client == client && voice->port == port) + snd_gf1_free_voice(gus, voice); + } +} + +static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_gus_port_t * port = (snd_gus_port_t *)private_data; + snd_gus_card_t * gus = port->gus; + snd_gus_voice_t * voice; + unsigned int idx; + + if (info->voices > 32) + return -EINVAL; + down(&gus->register_mutex); + if (!snd_gus_use_inc(gus)) { + up(&gus->register_mutex); + return -EFAULT; + } + for (idx = 0; idx < info->voices; idx++) { + voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); + snd_gus_use_dec(gus); + up(&gus->register_mutex); + return -EBUSY; + } + voice->index = idx; + } + up(&gus->register_mutex); + return 0; +} + +static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_gus_port_t * port = (snd_gus_port_t *)private_data; + snd_gus_card_t * gus = port->gus; + + down(&gus->register_mutex); + snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); + snd_gus_use_dec(gus); + up(&gus->register_mutex); + return 0; +} + +/* + * + */ + +static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client) +{ + snd_seq_instr_header_t ifree; + + memset(&ifree, 0, sizeof(ifree)); + ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; + snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0); +} + +int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) +{ + snd_gus_port_t * p = (snd_gus_port_t *) private_data; + + snd_assert(p != NULL, return -EINVAL); + if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && + ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { + snd_gus_sample_event(ev, p); + return 0; + } + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && + ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { + if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { + snd_gus_synth_free_private_instruments(p, ev->data.addr.client); + return 0; + } + } + if (direct) { + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { + snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops, + p->gus->gf1.ilist, + ev, + p->gus->gf1.seq_client, + atomic, hop); + return 0; + } + } + return 0; +} + +static void snd_gus_synth_instr_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + unsigned int idx; + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return); + snd_gus_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&gus->event_lock, flags); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { + if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { + pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY); + } else { + snd_gf1_stop_voice(gus, pvoice->number); + pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + } + } + } + spin_unlock_irqrestore(&gus->event_lock, flags); +} + +/* + * + */ + +static void snd_gus_synth_free_port(void *private_data) +{ + snd_gus_port_t * p = (snd_gus_port_t *)private_data; + + if (p) + snd_midi_channel_free_set(p->chset); +} + +static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx) +{ + snd_gus_port_t * p; + snd_seq_port_callback_t callbacks; + char name[32]; + int result; + + p = &gus->gf1.seq_ports[idx]; + p->chset = snd_midi_channel_alloc_set(16); + if (p->chset == NULL) + return -ENOMEM; + p->chset->private_data = p; + p->gus = gus; + p->client = gus->gf1.seq_client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_gus_synth_use; + callbacks.unuse = snd_gus_synth_unuse; + callbacks.event_input = snd_gus_synth_event_input; + callbacks.private_free = snd_gus_synth_free_port; + callbacks.private_data = p; + + sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx); + p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client, + &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_MIDI_GS | + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_SYNTH, + 16, 0, + name); + if (p->chset->port < 0) { + result = p->chset->port; + snd_gus_synth_free_port(p); + return result; + } + p->port = p->chset->port; + return 0; +} + +/* + * + */ + +static int snd_gus_synth_new_device(snd_seq_device_t *dev) +{ + snd_gus_card_t *gus; + int client, i; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + snd_seq_port_subscribe_t sub; + snd_iwffff_ops_t *iwops; + snd_gf1_ops_t *gf1ops; + snd_simple_ops_t *simpleops; + + gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (gus == NULL) + return -EINVAL; + + init_MUTEX(&gus->register_mutex); + gus->gf1.seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = gus; + callbacks.allow_output = callbacks.allow_input = 1; + client = gus->gf1.seq_client = + snd_seq_create_kernel_client(gus->card, 1, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + sprintf(cinfo.name, gus->interwave ? "AMD InterWave" : "GF1"); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + for (i = 0; i < 4; i++) + snd_gus_synth_create_port(gus, i); + + gus->gf1.ilist = snd_seq_instr_list_new(); + if (gus->gf1.ilist == NULL) { + snd_seq_delete_kernel_client(client); + gus->gf1.seq_client = -1; + return -ENOMEM; + } + gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + + simpleops = &gus->gf1.simple_ops; + snd_seq_simple_init(simpleops, gus, NULL); + simpleops->put_sample = snd_gus_simple_put_sample; + simpleops->get_sample = snd_gus_simple_get_sample; + simpleops->remove_sample = snd_gus_simple_remove_sample; + simpleops->notify = snd_gus_synth_instr_notify; + + gf1ops = &gus->gf1.gf1_ops; + snd_seq_gf1_init(gf1ops, gus, &simpleops->kops); + gf1ops->put_sample = snd_gus_gf1_put_sample; + gf1ops->get_sample = snd_gus_gf1_get_sample; + gf1ops->remove_sample = snd_gus_gf1_remove_sample; + gf1ops->notify = snd_gus_synth_instr_notify; + + iwops = &gus->gf1.iwffff_ops; + snd_seq_iwffff_init(iwops, gus, &gf1ops->kops); + iwops->put_sample = snd_gus_iwffff_put_sample; + iwops->get_sample = snd_gus_iwffff_get_sample; + iwops->remove_sample = snd_gus_iwffff_remove_sample; + iwops->notify = snd_gus_synth_instr_notify; + + memset(&sub, 0, sizeof(sub)); + sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + sub.dest.client = client; + sub.dest.port = 0; + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); + + return 0; +} + +static int snd_gus_synth_delete_device(snd_seq_device_t *dev) +{ + snd_gus_card_t *gus; + + gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (gus == NULL) + return -EINVAL; + + if (gus->gf1.seq_client >= 0) { + snd_seq_delete_kernel_client(gus->gf1.seq_client); + gus->gf1.seq_client = -1; + } + if (gus->gf1.ilist) + snd_seq_instr_list_free(&gus->gf1.ilist); + return 0; +} + +static int __init alsa_gus_synth_init(void) +{ + static snd_seq_dev_ops_t ops = { + snd_gus_synth_new_device, + snd_gus_synth_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops, + sizeof(snd_gus_card_t*)); +} + +static void __exit alsa_gus_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS); +} + +module_init(alsa_gus_synth_init) +module_exit(alsa_gus_synth_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_tables.h linux/sound/isa/gus/gus_tables.h --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_tables.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_tables.h 2001-12-30 02:26:46.000000000 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_GF1_SCALE_TABLE_SIZE 128 +#define SNDRV_GF1_ATTEN_TABLE_SIZE 128 + +#ifdef __GUS_TABLES_ALLOC__ + +unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] = +{ + 8372, 8870, 9397, 9956, 10548, 11175, + 11840, 12544, 13290, 14080, 14917, 15804, + 16744, 17740, 18795, 19912, 21096, 22351, + 23680, 25088, 26580, 28160, 29834, 31609, + 33488, 35479, 37589, 39824, 42192, 44701, + 47359, 50175, 53159, 56320, 59669, 63217, + 66976, 70959, 75178, 79649, 84385, 89402, + 94719, 100351, 106318, 112640, 119338, 126434, + 133952, 141918, 150356, 159297, 168769, 178805, + 189437, 200702, 212636, 225280, 238676, 252868, + 267905, 283835, 300713, 318594, 337539, 357610, + 378874, 401403, 425272, 450560, 477352, 505737, + 535809, 567670, 601425, 637188, 675077, 715219, + 757749, 802807, 850544, 901120, 954703, 1011473, + 1071618, 1135340, 1202851, 1274376, 1350154, 1430439, + 1515497, 1605613, 1701088, 1802240, 1909407, 2022946, + 2143237, 2270680, 2405702, 2548752, 2700309, 2860878, + 3030994, 3211227, 3402176, 3604480, 3818814, 4045892, + 4286473, 4541360, 4811404, 5097505, 5400618, 5721755, + 6061989, 6422453, 6804352, 7208960, 7637627, 8091784, + 8572947, 9082720, 9622807, 10195009, 10801236, 11443511, + 12123977, 12844906 +}; + +unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = { + 4095 /* 0 */,1789 /* 1 */,1533 /* 2 */,1383 /* 3 */,1277 /* 4 */, + 1195 /* 5 */,1127 /* 6 */,1070 /* 7 */,1021 /* 8 */,978 /* 9 */, + 939 /* 10 */,903 /* 11 */,871 /* 12 */,842 /* 13 */,814 /* 14 */, + 789 /* 15 */,765 /* 16 */,743 /* 17 */,722 /* 18 */,702 /* 19 */, + 683 /* 20 */,665 /* 21 */,647 /* 22 */,631 /* 23 */,615 /* 24 */, + 600 /* 25 */,586 /* 26 */,572 /* 27 */,558 /* 28 */,545 /* 29 */, + 533 /* 30 */,521 /* 31 */,509 /* 32 */,498 /* 33 */,487 /* 34 */, + 476 /* 35 */,466 /* 36 */,455 /* 37 */,446 /* 38 */,436 /* 39 */, + 427 /* 40 */,418 /* 41 */,409 /* 42 */,400 /* 43 */,391 /* 44 */, + 383 /* 45 */,375 /* 46 */,367 /* 47 */,359 /* 48 */,352 /* 49 */, + 344 /* 50 */,337 /* 51 */,330 /* 52 */,323 /* 53 */,316 /* 54 */, + 309 /* 55 */,302 /* 56 */,296 /* 57 */,289 /* 58 */,283 /* 59 */, + 277 /* 60 */,271 /* 61 */,265 /* 62 */,259 /* 63 */,253 /* 64 */, + 247 /* 65 */,242 /* 66 */,236 /* 67 */,231 /* 68 */,225 /* 69 */, + 220 /* 70 */,215 /* 71 */,210 /* 72 */,205 /* 73 */,199 /* 74 */, + 195 /* 75 */,190 /* 76 */,185 /* 77 */,180 /* 78 */,175 /* 79 */, + 171 /* 80 */,166 /* 81 */,162 /* 82 */,157 /* 83 */,153 /* 84 */, + 148 /* 85 */,144 /* 86 */,140 /* 87 */,135 /* 88 */,131 /* 89 */, + 127 /* 90 */,123 /* 91 */,119 /* 92 */,115 /* 93 */,111 /* 94 */, + 107 /* 95 */,103 /* 96 */,100 /* 97 */,96 /* 98 */,92 /* 99 */, + 88 /* 100 */,85 /* 101 */,81 /* 102 */,77 /* 103 */,74 /* 104 */, + 70 /* 105 */,67 /* 106 */,63 /* 107 */,60 /* 108 */,56 /* 109 */, + 53 /* 110 */,50 /* 111 */,46 /* 112 */,43 /* 113 */,40 /* 114 */, + 37 /* 115 */,33 /* 116 */,30 /* 117 */,27 /* 118 */,24 /* 119 */, + 21 /* 120 */,18 /* 121 */,15 /* 122 */,12 /* 123 */,9 /* 124 */, + 6 /* 125 */,3 /* 126 */,0 /* 127 */, +}; + +#else + +extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE]; +extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE]; + +#endif diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_timer.c linux/sound/isa/gus/gus_timer.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_timer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_timer.c 2002-08-13 10:13:38.000000000 -0600 @@ -0,0 +1,206 @@ +/* + * Routines for Gravis UltraSound soundcards - Timers + * Copyright (c) by Jaroslav Kysela + * + * GUS have similar timers as AdLib (OPL2/OPL3 chips). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#define chip_t snd_gus_card_t + +/* + * Timer 1 - 80us + */ + +static int snd_gf1_timer1_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + ticks = timer->sticks; + tmp = (gus->gf1.timer_enabled |= 4); + snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */ + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */ + snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +static int snd_gf1_timer1_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + tmp = (gus->gf1.timer_enabled &= ~4); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +/* + * Timer 2 - 320us + */ + +static int snd_gf1_timer2_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + ticks = timer->sticks; + tmp = (gus->gf1.timer_enabled |= 8); + snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */ + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */ + snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +static int snd_gf1_timer2_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + tmp = (gus->gf1.timer_enabled &= ~8); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +/* + + */ + +static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus) +{ + snd_timer_t *timer = gus->gf1.timer1; + + if (timer == NULL) + return; + snd_timer_interrupt(timer, timer->sticks); +} + +static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus) +{ + snd_timer_t *timer = gus->gf1.timer2; + + if (timer == NULL) + return; + snd_timer_interrupt(timer, timer->sticks); +} + +/* + + */ + +static struct _snd_timer_hardware snd_gf1_timer1 = +{ + .flags = SNDRV_TIMER_HW_STOP, + .resolution = 80000, + .ticks = 256, + .start = snd_gf1_timer1_start, + .stop = snd_gf1_timer1_stop, +}; + +static struct _snd_timer_hardware snd_gf1_timer2 = +{ + .flags = SNDRV_TIMER_HW_STOP, + .resolution = 320000, + .ticks = 256, + .start = snd_gf1_timer2_start, + .stop = snd_gf1_timer2_stop, +}; + +static void snd_gf1_timer1_free(snd_timer_t *timer) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + gus->gf1.timer1 = NULL; +} + +static void snd_gf1_timer2_free(snd_timer_t *timer) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + gus->gf1.timer2 = NULL; +} + +void snd_gf1_timers_init(snd_gus_card_t * gus) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + + if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL) + return; + + gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; + gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = gus->card->number; + tid.device = gus->timer_dev; + tid.subdevice = 0; + + if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { + strcpy(timer->name, "GF1 timer #1"); + timer->private_data = gus; + timer->private_free = snd_gf1_timer1_free; + timer->hw = snd_gf1_timer1; + } + gus->gf1.timer1 = timer; + + tid.device++; + + if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { + strcpy(timer->name, "GF1 timer #2"); + timer->private_data = gus; + timer->private_free = snd_gf1_timer2_free; + timer->hw = snd_gf1_timer2; + } + gus->gf1.timer2 = timer; +} + +void snd_gf1_timers_done(snd_gus_card_t * gus) +{ + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2); + if (gus->gf1.timer1) { + snd_device_free(gus->card, gus->gf1.timer1); + gus->gf1.timer1 = NULL; + } + if (gus->gf1.timer2) { + snd_device_free(gus->card, gus->gf1.timer2); + gus->gf1.timer2 = NULL; + } +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_uart.c linux/sound/isa/gus/gus_uart.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_uart.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_uart.c 2002-08-26 02:47:58.000000000 -0600 @@ -0,0 +1,257 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for the GF1 MIDI interface - like UART 6850 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) +{ + int count; + unsigned char stat, data, byte; + unsigned long flags; + + count = 10; + while (count) { + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + stat = snd_gf1_uart_stat(gus); + if (!(stat & 0x01)) { /* data in Rx FIFO? */ + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + count--; + continue; + } + count = 100; /* arm counter to new value */ + data = snd_gf1_uart_get(gus); + if (!(gus->gf1.uart_cmd & 0x80)) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + continue; + } + if (stat & 0x10) { /* framing error */ + gus->gf1.uart_framing++; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + continue; + } + byte = snd_gf1_uart_get(gus); + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); + if (stat & 0x20) { + gus->gf1.uart_overrun++; + } + } +} + +static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus) +{ + char byte; + unsigned long flags; + + /* try unlock output */ + if (snd_gf1_uart_stat(gus) & 0x01) + snd_gf1_interrupt_midi_in(gus); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ + if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ + } else { + snd_gf1_uart_put(gus, byte); + } + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close) +{ + snd_gf1_uart_cmd(gus, 0x03); /* reset */ + if (!close && gus->uart_enable) { + udelay(160); + snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ + } +} + +static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ + snd_gf1_uart_reset(gus, 0); + } + gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; + gus->midi_substream_output = substream; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +#if 0 + snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); +#endif + return 0; +} + +static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + int i; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { + snd_gf1_uart_reset(gus, 0); + } + gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; + gus->midi_substream_input = substream; + if (gus->uart_enable) { + for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) + snd_gf1_uart_get(gus); /* clean Rx */ + if (i >= 1000) + snd_printk("gus midi uart init read - cleanup error\n"); + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +#if 0 + snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); + snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); +#endif + return 0; +} + +static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) + snd_gf1_uart_reset(gus, 1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); + gus->midi_substream_output = NULL; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return 0; +} + +static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) + snd_gf1_uart_reset(gus, 1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); + gus->midi_substream_input = NULL; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return 0; +} + +static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_gus_card_t *gus; + unsigned long flags; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (up) { + if ((gus->gf1.uart_cmd & 0x80) == 0) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ + } else { + if (gus->gf1.uart_cmd & 0x80) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_gus_card_t *gus; + char byte; + int timeout; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (up) { + if ((gus->gf1.uart_cmd & 0x20) == 0) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + /* wait for empty Rx - Tx is probably unlocked */ + timeout = 10000; + while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); + /* Tx FIFO free? */ + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.uart_cmd & 0x20) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return; + } + if (snd_gf1_uart_stat(gus) & 0x02) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return; + } + snd_gf1_uart_put(gus, byte); + } + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ + } + } else { + if (gus->gf1.uart_cmd & 0x20) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static snd_rawmidi_ops_t snd_gf1_uart_output = +{ + .open = snd_gf1_uart_output_open, + .close = snd_gf1_uart_output_close, + .trigger = snd_gf1_uart_output_trigger, +}; + +static snd_rawmidi_ops_t snd_gf1_uart_input = +{ + .open = snd_gf1_uart_input_open, + .close = snd_gf1_uart_input_close, + .trigger = snd_gf1_uart_input_trigger, +}; + +int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = gus; + gus->midi_uart = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return err; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gus_volume.c linux/sound/isa/gus/gus_volume.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gus_volume.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gus_volume.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,210 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#define __GUS_TABLES_ALLOC__ +#include "gus_tables.h" + +EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */ + +unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol) +{ + unsigned short e, m, tmp; + + if (vol > 65535) + vol = 65535; + tmp = vol; + e = 7; + if (tmp < 128) { + while (e > 0 && tmp < (1 << e)) + e--; + } else { + while (tmp > 255) { + tmp >>= 1; + e++; + } + } + m = vol - (1 << e); + if (m > 0) { + if (e > 8) + m >>= e - 8; + else if (e < 8) + m <<= 8 - e; + m &= 255; + } + return (e << 8) | m; +} + +unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol) +{ + unsigned int rvol; + unsigned short e, m; + + if (!gf1_vol) + return 0; + e = gf1_vol >> 8; + m = (unsigned char) gf1_vol; + rvol = 1 << e; + if (e > 8) + return rvol | (m << (e - 8)); + return rvol | (m >> (8 - e)); +} + +unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, + unsigned short start, + unsigned short end, + unsigned int us) +{ + static unsigned char vol_rates[19] = + { + 23, 24, 26, 28, 29, 31, 32, 34, + 36, 37, 39, 40, 42, 44, 45, 47, + 49, 50, 52 + }; + unsigned short range, increment, value, i; + + start >>= 4; + end >>= 4; + if (start < end) + us /= end - start; + else + us /= start - end; + range = 4; + value = gus->gf1.enh_mode ? + vol_rates[0] : + vol_rates[gus->gf1.active_voices - 14]; + for (i = 0; i < 3; i++) { + if (us < value) { + range = i; + break; + } else + value <<= 3; + } + if (range == 4) { + range = 3; + increment = 1; + } else + increment = (value + (value >> 1)) / us; + return (range << 6) | (increment & 0x3f); +} + +unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16) +{ + freq16 >>= 3; + if (freq16 < 50) + freq16 = 50; + if (freq16 & 0xf8000000) { + freq16 = ~0xf8000000; + snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16); + } + return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq; +} + +short snd_gf1_compute_vibrato(short cents, unsigned short fc_register) +{ + static short vibrato_table[] = + { + 0, 0, 32, 592, 61, 1175, 93, 1808, + 124, 2433, 152, 3007, 182, 3632, 213, 4290, + 241, 4834, 255, 5200 + }; + + long depth; + short *vi1, *vi2, pcents, v1; + + pcents = cents < 0 ? -cents : cents; + for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2); + v1 = *(vi1 + 1); + /* The FC table above is a list of pairs. The first number in the pair */ + /* is the cents index from 0-255 cents, and the second number in the */ + /* pair is the FC adjustment needed to change the pitch by the indexed */ + /* number of cents. The table was created for an FC of 32768. */ + /* The following expression does a linear interpolation against the */ + /* approximated log curve in the table above, and then scales the number */ + /* by the FC before the LFO. This calculation also adjusts the output */ + /* value to produce the appropriate depth for the hardware. The depth */ + /* is 2 * desired FC + 1. */ + depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14; + if (depth) + depth++; + if (depth > 255) + depth = 255; + return cents < 0 ? -(short) depth : (short) depth; +} + +unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens) +{ + static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933}; + int wheel, sensitivity; + unsigned int mantissa, f1, f2; + unsigned short semitones, f1_index, f2_index, f1_power, f2_power; + char bend_down = 0; + int bend; + + if (!sens) + return 1024; + wheel = (int) pitchbend - 8192; + sensitivity = ((int) sens * wheel) / 128; + if (sensitivity < 0) { + bend_down = 1; + sensitivity = -sensitivity; + } + semitones = (unsigned int) (sensitivity >> 13); + mantissa = sensitivity % 8192; + f1_index = semitones % 12; + f2_index = (semitones + 1) % 12; + f1_power = semitones / 12; + f2_power = (semitones + 1) / 12; + f1 = log_table[f1_index] << f1_power; + f2 = log_table[f2_index] << f2_power; + bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1); + if (bend_down) + bend = 1048576L / bend; + return bend; +} + +unsigned short snd_gf1_compute_freq(unsigned int freq, + unsigned int rate, + unsigned short mix_rate) +{ + unsigned int fc; + int scale = 0; + + while (freq >= 4194304L) { + scale++; + freq >>= 1; + } + fc = (freq << 10) / rate; + if (fc > 97391L) { + fc = 97391; + snd_printk("patch: (1) fc frequency overflow - %u\n", fc); + } + fc = (fc * 44100UL) / mix_rate; + while (scale--) + fc <<= 1; + if (fc > 65535L) { + fc = 65535; + snd_printk("patch: (2) fc frequency overflow - %u\n", fc); + } + return (unsigned short) fc; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gusclassic.c linux/sound/isa/gus/gusclassic.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gusclassic.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gusclassic.c 2002-10-21 12:28:22.000000000 -0600 @@ -0,0 +1,300 @@ +/* + * Driver for Gravis UltraSound Classic soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound Classic"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound Classic}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for GUS Classic soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for GUS Classic soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable GUS Classic soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for GUS Classic driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x10}},dialog:list"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for GUS Classic driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for GUS Classic driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for GUS Classic driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver."); +MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(channels, "GF1 channels for GUS Classic driver."); +MODULE_PARM_SYNTAX(channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Classic driver."); +MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusclassic_detect(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + + return 0; +} + +static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus) +{ + gus->equal_irq = 0; + gus->codec_flag = 0; + gus->max_flag = 0; + gus->joystick_dac = joystick_dac[dev]; +} + +static int __init snd_gusclassic_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1}; + static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; + int xirq, xdma1, xdma2; + snd_card_t *card; + struct snd_gusclassic *guscard; + snd_gus_card_t *gus = NULL; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + guscard = (struct snd_gusclassic *)card->private_data; + if (pcm_channels[dev] < 2) + pcm_channels[dev] = 2; + + xirq = irq[dev]; + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + xdma1 = dma1[dev]; + if (xdma1 == SNDRV_AUTO_DMA) { + if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + xdma2 = dma2[dev]; + if (xdma2 == SNDRV_AUTO_DMA) { + if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + + if ((err = snd_gus_create(card, + port[dev], + xirq, xdma1, xdma2, + 0, channels[dev], pcm_channels[dev], + 0, &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusclassic_detect(gus)) < 0) { + snd_card_free(card); + return err; + } + snd_gusclassic_init(dev, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (gus->max_flag || gus->ess_flag) { + snd_card_free(card); + snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + if ((err = snd_gf1_new_mixer(gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->ace_flag) { + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, xirq, xdma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", xdma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_gusclassic_cards[dev] = card; + return 0; +} + +static int __init snd_gusclassic_legacy_auto_probe(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; + port[dev] = xport; + res = snd_gusclassic_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusclassic_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusclassic_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "GUS Classic soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusclassic_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_gusclassic_cards[idx]); +} + +module_init(alsa_card_gusclassic_init) +module_exit(alsa_card_gusclassic_exit) + +#ifndef MODULE + +/* format is: snd-gusclassic=enable,index,id, + port,irq, + dma1,dma2, + joystick_dac, + channels,pcm_channels */ + +static int __init alsa_card_gusclassic_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2 && + get_option(&str,&joystick_dac[nr_dev]) == 2 && + get_option(&str,&channels[nr_dev]) == 2 && + get_option(&str,&pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusclassic=", alsa_card_gusclassic_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gusextreme.c linux/sound/isa/gus/gusextreme.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gusextreme.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gusextreme.c 2002-10-21 12:28:22.000000000 -0600 @@ -0,0 +1,434 @@ +/* + * Driver for Gravis UltraSound Extreme soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound Extreme"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound Extreme}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */ +static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x20}},dialog:list"); +MODULE_PARM(gf1_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(gf1_port, "GF1 port # for GUS Extreme driver (optional)."); +MODULE_PARM_SYNTAX(gf1_port, SNDRV_ENABLED ",allows:{{0x210,0x270,0x10}},dialog:list"); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x320,0x10}},dialog:list"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); +MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); +MODULE_PARM(gf1_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(gf1_irq, SNDRV_ENABLED ",allows:{{2},{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "GF1 DMA # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver."); +MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(channels, "GF1 channels for GUS Extreme driver."); +MODULE_PARM_SYNTAX(channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Extreme driver."); +MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusextreme_detect(int dev, + snd_card_t * card, + snd_gus_card_t * gus, + es1688_t *es1688) +{ + unsigned long flags; + + /* + * This is main stuff - enable access to GF1 chip... + * I'm not sure, if this will work for card which have + * ES1688 chip in another place than 0x220. + * + * I used reverse-engineering in DOSEMU. [--jk] + * + * ULTRINIT.EXE: + * 0x230 = 0,2,3 + * 0x240 = 2,0,1 + * 0x250 = 2,0,3 + * 0x260 = 2,2,1 + */ + + spin_lock_irqsave(&es1688->mixer_lock, flags); + snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */ + spin_unlock_irqrestore(&es1688->mixer_lock, flags); + spin_lock_irqsave(&es1688->reg_lock, flags); + outb(gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1)); + outb(0, 0x201); + outb(gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1)); + outb(0, 0x201); + outb(gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); + spin_unlock_irqrestore(&es1688->reg_lock, flags); + + udelay(100); + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -EIO; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -EIO; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -EIO; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -EIO; +#endif + + return 0; +} + +static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus) +{ + gus->joystick_dac = joystick_dac[dev]; +} + +static int __init snd_gusextreme_mixer(es1688_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign Master Playback Switch to Synth Playback Switch */ + strcpy(id1.name, "Master Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + return 0; +} + +static int __init snd_gusextreme_probe(int dev) +{ + static int possible_ess_irqs[] = {5, 9, 10, 7, -1}; + static int possible_ess_dmas[] = {1, 3, 0, -1}; + static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1}; + int xgf1_irq, xgf1_dma, xess_irq, xmpu_irq, xess_dma; + snd_card_t *card; + struct snd_gusextreme *acard; + snd_gus_card_t *gus; + es1688_t *es1688; + opl3_t *opl3; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_gusextreme *)card->private_data; + + xgf1_irq = gf1_irq[dev]; + if (xgf1_irq == SNDRV_AUTO_IRQ) { + if ((xgf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ for GF1\n"); + return -EBUSY; + } + } + xess_irq = irq[dev]; + if (xess_irq == SNDRV_AUTO_IRQ) { + if ((xess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ for ES1688\n"); + return -EBUSY; + } + } + if (mpu_port[dev] == SNDRV_AUTO_PORT) + mpu_port[dev] = 0; + xmpu_irq = mpu_irq[dev]; + if (xmpu_irq > 15) + xmpu_irq = -1; + xgf1_dma = dma1[dev]; + if (xgf1_dma == SNDRV_AUTO_DMA) { + if ((xgf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA for GF1\n"); + return -EBUSY; + } + } + xess_dma = dma8[dev]; + if (xess_dma == SNDRV_AUTO_DMA) { + if ((xess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA for ES1688\n"); + return -EBUSY; + } + } + + if ((err = snd_es1688_create(card, port[dev], mpu_port[dev], + xess_irq, xmpu_irq, xess_dma, + ES1688_HW_1688, &es1688)) < 0) { + snd_card_free(card); + return err; + } + if (gf1_port[dev] < 0) + gf1_port[dev] = port[dev] + 0x20; + if ((err = snd_gus_create(card, + gf1_port[dev], + xgf1_irq, + xgf1_dma, + -1, + 0, channels[dev], + pcm_channels[dev], 0, + &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) { + snd_card_free(card); + return err; + } + snd_gusextreme_init(dev, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->ess_flag) { + snd_card_free(card); + snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_mixer(es1688)) < 0) { + snd_card_free(card); + return err; + } + snd_component_add(card, "ES1688"); + if (pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_gf1_new_mixer(gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusextreme_mixer(es1688)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, es1688->port, es1688->port + 2, + OPL3_HW_OPL3, 0, &opl3) < 0) { + printk(KERN_ERR "gusextreme: opl3 not detected at 0x%lx\n", es1688->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (es1688->mpu_port >= 0x300) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, + es1688->mpu_port, 0, + xmpu_irq, + SA_INTERRUPT, + NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i", + es1688->port, xgf1_irq, xess_irq, xgf1_dma, xess_dma); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_gusextreme_cards[dev] = card; + return 0; +} + +static int __init snd_gusextreme_legacy_auto_probe(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; + port[dev] = xport; + res = snd_gusextreme_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusextreme_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev] > 0; dev++) { + if (port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusextreme_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "GUS Extreme soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusextreme_exit(void) +{ + int idx; + snd_card_t *card; + struct snd_gusextreme *acard; + + for (idx = 0; idx < SNDRV_CARDS; idx++) { + card = snd_gusextreme_cards[idx]; + if (card == NULL) + continue; + acard = (struct snd_gusextreme *)card->private_data; + snd_card_free(snd_gusextreme_cards[idx]); + } +} + +module_init(alsa_card_gusextreme_init) +module_exit(alsa_card_gusextreme_exit) + +#ifndef MODULE + +/* format is: snd-gusextreme=enable,index,id, + port,gf1_port,mpu_port, + irq,gf1_irq,mpu_irq, + dma8,dma1, + joystick_dac, + channels,pcm_channels */ + +static int __init alsa_card_gusextreme_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&gf1_port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&gf1_irq[nr_dev]) == 2 && + get_option(&str,&mpu_irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusextreme=", alsa_card_gusextreme_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/gusmax.c linux/sound/isa/gus/gusmax.c --- linux-2.4.21-rc1.orig/sound/isa/gus/gusmax.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/gusmax.c 2002-10-21 12:28:22.000000000 -0600 @@ -0,0 +1,436 @@ +/* + * Driver for Gravis UltraSound MAX soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound MAX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound MAX}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for GUS MAX soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for GUS MAX driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220},{0x230},{0x240},{0x250},{0x260}},dialog:list"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); +MODULE_PARM(joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); +MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(channels, "Used GF1 channels for GUS MAX driver."); +MODULE_PARM_SYNTAX(channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS MAX driver."); +MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +struct snd_gusmax { + int irq; + snd_card_t *card; + snd_gus_card_t *gus; + cs4231_t *cs4231; + unsigned short gus_status_reg; + unsigned short pcm_status_reg; +}; + +static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusmax_detect(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + return 0; +} + +static void snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id; + int loop, max = 5; + + do { + loop = 0; + if (inb(maxcard->gus_status_reg)) { + snd_gus_interrupt(irq, maxcard->gus, regs); + loop++; + } + if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ + snd_cs4231_interrupt(irq, maxcard->cs4231, regs); + loop++; + } + } while (loop && --max > 0); +} + +static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus) +{ + gus->equal_irq = 1; + gus->codec_flag = 1; + gus->joystick_dac = joystick_dac[dev]; + /* init control register */ + gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f; + if (gus->gf1.dma1 > 3) + gus->max_cntrl_val |= 0x10; + if (gus->gf1.dma2 > 3) + gus->max_cntrl_val |= 0x20; + gus->max_cntrl_val |= 0x40; + outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT)); +} + +#define CS4231_PRIVATE( left, right, shift, mute ) \ + ((left << 24)|(right << 16)|(shift<<8)|mute) + +static int __init snd_gusmax_mixer(cs4231_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUXA to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUXB to CD */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; +#if 0 + /* reassign Mono Input to MIC */ + if (snd_mixer_group_rename(mixer, + SNDRV_MIXER_IN_MONO, 0, + SNDRV_MIXER_IN_MIC, 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT, + SNDRV_MIXER_IN_MIC, 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + "Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1, + "Mic Capture Volume", 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + "Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1, + "Mic Capture Switch", 0) < 0) + goto __error; +#endif + return 0; +} + +static void snd_gusmax_free(snd_card_t *card) +{ + struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; + + if (maxcard == NULL) + return; + if (maxcard->irq >= 0) + free_irq(maxcard->irq, (void *)maxcard); +} + +static int __init snd_gusmax_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; + int xirq, xdma1, xdma2, err; + snd_card_t *card; + snd_gus_card_t *gus = NULL; + cs4231_t *cs4231; + struct snd_gusmax *maxcard; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_gusmax)); + if (card == NULL) + return -ENOMEM; + card->private_free = snd_gusmax_free; + maxcard = (struct snd_gusmax *)card->private_data; + maxcard->card = card; + maxcard->irq = -1; + + xirq = irq[dev]; + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + xdma1 = dma1[dev]; + if (xdma1 == SNDRV_AUTO_DMA) { + if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + xdma2 = dma2[dev]; + if (xdma2 == SNDRV_AUTO_DMA) { + if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_gus_create(card, + port[dev], + -xirq, xdma1, xdma2, + 0, channels[dev], + pcm_channels[dev], + 0, &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusmax_detect(gus)) < 0) { + snd_card_free(card); + return err; + } + maxcard->gus_status_reg = gus->gf1.reg_irqstat; + maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; + snd_gusmax_init(dev, card, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->max_flag) { + snd_card_free(card); + printk(KERN_ERR "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + + if (request_irq(xirq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { + snd_card_free(card); + printk(KERN_ERR "gusmax: unable to grab IRQ %d\n", xirq); + return -EBUSY; + } + maxcard->irq = xirq; + + if ((err = snd_cs4231_create(card, + gus->gf1.port + 0x10c, -1, xirq, + xdma2 < 0 ? xdma1 : xdma2, xdma1, + CS4231_HW_DETECT, + CS4231_HWSHARE_IRQ | + CS4231_HWSHARE_DMA1 | + CS4231_HWSHARE_DMA2, + &cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_gusmax_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1); + if (xdma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%i", xdma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + maxcard->gus = gus; + maxcard->cs4231 = cs4231; + snd_gusmax_cards[dev] = card; + return 0; +} + +static int __init snd_gusmax_legacy_auto_probe(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; + port[dev] = xport; + res = snd_gusmax_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusmax_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev] > 0; dev++) { + if (port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusmax_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "GUS MAX soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusmax_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_gusmax_cards[idx]); +} + +module_init(alsa_card_gusmax_init) +module_exit(alsa_card_gusmax_exit) + +#ifndef MODULE + +/* format is: snd-gusmax=enable,index,id, + port,irq, + dma1,dma2, + joystick_dac, + channels,pcm_channels */ + +static int __init alsa_card_gusmax_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2 && + get_option(&str,&joystick_dac[nr_dev]) == 2 && + get_option(&str,&channels[nr_dev]) == 2 && + get_option(&str,&pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusmax=", alsa_card_gusmax_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/interwave-stb.c linux/sound/isa/gus/interwave-stb.c --- linux-2.4.21-rc1.orig/sound/isa/gus/interwave-stb.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/interwave-stb.c 2001-12-18 06:46:10.000000000 -0700 @@ -0,0 +1,2 @@ +#define SNDRV_STB +#include "interwave.c" diff -urN linux-2.4.21-rc1.orig/sound/isa/gus/interwave.c linux/sound/isa/gus/interwave.c --- linux-2.4.21-rc1.orig/sound/isa/gus/interwave.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/gus/interwave.c 2003-03-01 12:04:29.000000000 -0700 @@ -0,0 +1,1024 @@ +/* + * Driver for AMD InterWave soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 1999/07/22 Erik Inge Bolso + * * mixer group handlers + * + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#ifdef SNDRV_STB +#include +#endif +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +#ifndef SNDRV_STB +MODULE_DESCRIPTION("AMD InterWave"); +MODULE_DEVICES("{{Gravis,UltraSound Plug & Play}," + "{STB,SoundRage32}," + "{MED,MED3210}," + "{Dynasonix,Dynasonix Pro}," + "{Panasonic,PCA761AW}}"); +#else +MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); +MODULE_DEVICES("{{AMD,InterWave STB with TEA6330T}}"); +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */ +#ifdef SNDRV_STB +static long port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */ +#endif +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; +static int effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for InterWave soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for InterWave soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable InterWave soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for InterWave driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x210,0x260,0x10}},dialog:list"); +#ifdef SNDRV_STB +MODULE_PARM(port_tc, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); +MODULE_PARM_SYNTAX(port_tc, SNDRV_ENABLED ",allows:{{0x350,0x380,0x10}},dialog:list"); +#endif +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for InterWave driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); +MODULE_PARM(joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); +MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver."); +MODULE_PARM_SYNTAX(midi, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver."); +MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); +MODULE_PARM(effect, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(effect, "Effects enable for InterWave driver."); +MODULE_PARM_SYNTAX(effect, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); + +struct snd_interwave { + int irq; + snd_card_t *card; + snd_gus_card_t *gus; + cs4231_t *cs4231; +#ifdef SNDRV_STB + struct resource *i2c_res; +#endif + unsigned short gus_status_reg; + unsigned short pcm_status_reg; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#ifdef SNDRV_STB + struct isapnp_dev *devtc; +#endif +#endif +}; + +static snd_card_t *snd_interwave_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_interwave_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_interwave_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#define ISAPNP_INTERWAVE(_va, _vb, _vc, _device, _audio) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \ + } +#define ISAPNP_INTERWAVE_STB(_va, _vb, _vc, _device, _audio, _tone) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _tone), } \ + } + +static struct isapnp_card_id snd_interwave_pnpids[] __devinitdata = { +#ifndef SNDRV_STB + /* Gravis UltraSound Plug & Play */ + ISAPNP_INTERWAVE('G','R','V',0x0001,0x0000), + /* STB SoundRage32 */ + ISAPNP_INTERWAVE('S','T','B',0x011a,0x0010), + /* MED3210 */ + ISAPNP_INTERWAVE('D','X','P',0x3201,0x0010), + /* Dynasonic Pro */ + /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ + ISAPNP_INTERWAVE('C','D','C',0x1111,0x1112), + /* Panasonic PCA761AW Audio Card */ + ISAPNP_INTERWAVE('A','D','V',0x55ff,0x0010), +#else + /* InterWave STB with TEA6330T */ + ISAPNP_INTERWAVE_STB('A','D','V',0x550a,0x0010,0x0015), +#endif + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_interwave_pnpids); + +#endif /* __ISAPNP__ */ + + +#ifdef SNDRV_STB +static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data) +{ + unsigned long port = bus->private_value; + +#if 0 + printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data); +#endif + outb((data << 1) | ctrl, port); + udelay(10); +} + +static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus) +{ + unsigned long port = bus->private_value; + unsigned char res; + + res = inb(port) & 1; +#if 0 + printk("i2c_getclockline - 0x%lx -> %i\n", port, res); +#endif + return res; +} + +static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack) +{ + unsigned long port = bus->private_value; + unsigned char res; + + if (ack) + udelay(10); + res = (inb(port) & 2) >> 1; +#if 0 + printk("i2c_getdataline - 0x%lx -> %i\n", port, res); +#endif + return res; +} + +static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = { + .setlines = snd_interwave_i2c_setlines, + .getclock = snd_interwave_i2c_getclockline, + .getdata = snd_interwave_i2c_getdataline, +}; + +static int __init snd_interwave_detect_stb(struct snd_interwave *iwcard, + snd_gus_card_t * gus, int dev, + snd_i2c_bus_t **rbus) +{ + unsigned long port; + snd_i2c_bus_t *bus; + snd_card_t *card = iwcard->card; + char name[32]; + int err; + + *rbus = NULL; + port = port_tc[dev]; + if (port == SNDRV_AUTO_PORT) { + port = 0x350; + if (gus->gf1.port == 0x250) { + port = 0x360; + } + while (port <= 0x380) { + if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) + break; + port += 0x10; + } + if (port > 0x380) + return -ENODEV; + } else { + if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) + return -ENODEV; + } + sprintf(name, "InterWave-%i", card->number); + if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0) + return err; + bus->private_value = port; + bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; + if ((err = snd_tea6330t_detect(bus, 0)) < 0) + return err; + *rbus = bus; + return 0; +} +#endif + +static int __init snd_interwave_detect(struct snd_interwave *iwcard, + snd_gus_card_t * gus, + int dev +#ifdef SNDRV_STB + , snd_i2c_bus_t **rbus +#endif + ) +{ + unsigned long flags; + unsigned char rev1, rev2; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + int d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + int d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + + spin_lock_irqsave(&gus->reg_lock, flags); + rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); + rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); + if ((rev1 & 0xf0) == (rev2 & 0xf0) && + (rev1 & 0x0f) != (rev2 & 0x0f)) { + snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); + gus->interwave = 1; + strcpy(gus->card->shortname, "AMD InterWave"); + gus->revision = rev1 >> 4; +#ifndef SNDRV_STB + return 0; /* ok.. We have an InterWave board */ +#else + return snd_interwave_detect_stb(iwcard, gus, dev, rbus); +#endif + } + snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); + return -ENODEV; +} + +static void snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct snd_interwave *iwcard = (struct snd_interwave *) dev_id; + int loop, max = 5; + + do { + loop = 0; + if (inb(iwcard->gus_status_reg)) { + snd_gus_interrupt(irq, iwcard->gus, regs); + loop++; + } + if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ + snd_cs4231_interrupt(irq, iwcard->cs4231, regs); + loop++; + } + } while (loop && --max > 0); +} + +static void __init snd_interwave_reset(snd_gus_card_t * gus) +{ + snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); + udelay(160); + snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); + udelay(160); +} + +static void __init snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes) +{ + unsigned int idx; + unsigned int local; + unsigned char d; + + for (idx = 0; idx < 4; idx++) { + sizes[idx] = 0; + d = 0x55; + for (local = idx << 22; + local < (idx << 22) + 0x400000; + local += 0x40000, d++) { + snd_gf1_poke(gus, local, d); + snd_gf1_poke(gus, local + 1, d + 1); +#if 0 + printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n", + d, + snd_gf1_peek(gus, local), + snd_gf1_peek(gus, local + 1), + snd_gf1_peek(gus, idx << 22)); +#endif + if (snd_gf1_peek(gus, local) != d || + snd_gf1_peek(gus, local + 1) != d + 1 || + snd_gf1_peek(gus, idx << 22) != 0x55) + break; + sizes[idx]++; + } + } +#if 0 + printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]); +#endif +} + +struct rom_hdr { + /* 000 */ unsigned char iwave[8]; + /* 008 */ unsigned char rom_hdr_revision; + /* 009 */ unsigned char series_number; + /* 010 */ unsigned char series_name[16]; + /* 026 */ unsigned char date[10]; + /* 036 */ unsigned short vendor_revision_major; + /* 038 */ unsigned short vendor_revision_minor; + /* 040 */ unsigned int rom_size; + /* 044 */ unsigned char copyright[128]; + /* 172 */ unsigned char vendor_name[64]; + /* 236 */ unsigned char rom_description[128]; + /* 364 */ unsigned char pad[147]; + /* 511 */ unsigned char csum; +}; + +static void __init snd_interwave_detect_memory(snd_gus_card_t * gus) +{ + static unsigned int lmc[13] = + { + 0x00000001, 0x00000101, 0x01010101, 0x00000401, + 0x04040401, 0x00040101, 0x04040101, 0x00000004, + 0x00000404, 0x04040404, 0x00000010, 0x00001010, + 0x10101010 + }; + + int bank_pos, pages; + unsigned int i, lmct; + int psizes[4]; + unsigned char csum; + struct rom_hdr romh; + + snd_interwave_reset(gus); + snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); + /* ok.. simple test of memory size */ + pages = 0; + snd_gf1_poke(gus, 0, 0x55); + snd_gf1_poke(gus, 1, 0xaa); +#if 1 + if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa) +#else + if (0) /* ok.. for testing of 0k RAM */ +#endif + { + snd_interwave_bank_sizes(gus, psizes); + lmct = (psizes[3] << 24) | (psizes[2] << 16) | + (psizes[1] << 8) | psizes[0]; +#if 0 + printk("lmct = 0x%08x\n", lmct); +#endif + for (i = 0; i < sizeof(lmc) / sizeof(unsigned int); i++) + if (lmct == lmc[i]) { +#if 0 + printk("found !!! %i\n", i); +#endif + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); + snd_interwave_bank_sizes(gus, psizes); + break; + } + if (i >= sizeof(lmc) / sizeof(unsigned int) && !gus->gf1.enh_mode) + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); + for (i = 0; i < 4; i++) { + gus->gf1.mem_alloc.banks_8[i].address = + gus->gf1.mem_alloc.banks_16[i].address = i << 22; + gus->gf1.mem_alloc.banks_8[i].size = + gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; + pages += psizes[i]; + } + } + pages <<= 18; + gus->gf1.memory = pages; + + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); + gus->gf1.rom_banks = 0; + gus->gf1.rom_memory = 0; + for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { + for (i = 0; i < sizeof(struct rom_hdr); i++) + *(((unsigned char *) &romh) + i) = snd_gf1_peek(gus, i + bank_pos); +#ifdef CONFIG_SND_DEBUG_ROM + printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, + romh.iwave[0], romh.iwave[1], romh.iwave[2], romh.iwave[3], + romh.iwave[4], romh.iwave[5], romh.iwave[6], romh.iwave[7]); +#endif + if (strncmp(romh.iwave, "INTRWAVE", 8)) + continue; /* first check */ + csum = 0; + for (i = 0; i < sizeof(struct rom_hdr) - 1; i++) + csum += *(((unsigned char *) &romh) + i); +#ifdef CONFIG_SND_DEBUG_ROM + printk("ROM checksum = 0x%x == 0x%x (computed)\n", romh.csum, (unsigned char) (256 - csum)); +#endif + if (256 - csum != romh.csum) + continue; /* not valid rom */ + gus->gf1.rom_banks++; + gus->gf1.rom_present |= 1 << (bank_pos >> 22); +#ifdef SNDRV_LITTLE_ENDIAN + gus->gf1.rom_memory = romh.rom_size; +#else + gus->gf1.rom_memory = ((romh.rom_size >> 24) & 0x000000ff) | + ((romh.rom_size >> 8) & 0x0000ff00) | + ((romh.rom_size << 8) & 0x00ff0000) | + ((romh.rom_size << 24) & 0xff000000); +#endif + } +#if 0 + if (gus->gf1.rom_memory > 0) { + if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) + gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; + } +#endif + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ + + if (!gus->gf1.enh_mode) + snd_interwave_reset(gus); +} + +static void __init snd_interwave_init(int dev, snd_gus_card_t * gus) +{ + unsigned long flags; + + /* ok.. some InterWave specific initialization */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); + snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); + snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); + spin_unlock_irqrestore(&gus->reg_lock, flags); + gus->equal_irq = 1; + gus->codec_flag = 1; + gus->interwave = 1; + gus->max_flag = 1; + gus->joystick_dac = joystick_dac[dev]; + +} + +#define INTERWAVE_CONTROLS (sizeof(snd_interwave_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_interwave_controls[] = { +CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) +}; + +static int __init snd_interwave_mixer(cs4231_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + unsigned int idx; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; +#if 0 + /* remove mono microphone controls */ + strcpy(id1.name, "Mic Playback Switch"); + if ((err = snd_ctl_remove_id(card, &id1)) < 0) + return err; + strcpy(id1.name, "Mic Playback Volume"); + if ((err = snd_ctl_remove_id(card, &id1)) < 0) + return err; +#endif + /* add new master and mic controls */ + for (idx = 0; idx < INTERWAVE_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0) + return err; + snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); + snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); + snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); + snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); + /* reassign AUXA to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUXB to CD */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + return 0; +} + +#ifdef __ISAPNP__ + +static int __init snd_interwave_isapnp(int dev, struct snd_interwave *iwcard) +{ + const struct isapnp_card_id *id = snd_interwave_isapnp_id[dev]; + struct isapnp_card *card = snd_interwave_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + iwcard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (iwcard->dev->active) { + iwcard->dev = NULL; + return -EBUSY; + } +#ifdef SNDRV_STB + iwcard->devtc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (iwcard->devtc->active) { + iwcard->dev = iwcard->devtc = NULL; + return -EBUSY; + } +#endif + /* Synth & Codec initialization */ + pdev = iwcard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + if (port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + isapnp_resource_change(&pdev->resource[1], port[dev] + 0x100, 12); + isapnp_resource_change(&pdev->resource[2], port[dev] + 0x10c, 4); + } + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev], 1); + if (dma2[dev] < 0) + isapnp_resource_change(&pdev->dma_resource[1], 4, 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + if (pdev->resource[0].start + 0x100 != pdev->resource[1].start || + pdev->resource[0].start + 0x10c != pdev->resource[2].start) { + snd_printk("isapnp configure failure (wrong ports)\n"); + pdev->deactivate(pdev); + return -ENOENT; + } + port[dev] = pdev->resource[0].start; + dma1[dev] = pdev->dma_resource[0].start; + if (dma2[dev] >= 0) + dma2[dev] = pdev->dma_resource[1].start; + irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n", + pdev->resource[0].start, + pdev->resource[1].start, + pdev->resource[2].start); + snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); +#ifdef SNDRV_STB + /* Tone Control initialization */ + pdev = iwcard->devtc; + if (pdev->prepare(pdev)<0) { + iwcard->dev->deactivate(iwcard->dev); + return -EAGAIN; + } + if (port_tc[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port_tc[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("Tone Control isapnp configure failure (out of resources?)\n"); + iwcard->dev->deactivate(iwcard->dev); + return -EBUSY; + } + port_tc[dev] = pdev->resource[0].start; + snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]); +#endif + return 0; +} + +static void snd_interwave_deactivate(struct snd_interwave *iwcard) +{ + if (iwcard->dev) { + iwcard->dev->deactivate(iwcard->dev); + iwcard->dev = NULL; + } +#ifdef SNDRV_STB + if (iwcard->devtc) { + iwcard->devtc->deactivate(iwcard->devtc); + iwcard->devtc = NULL; + } +#endif +} + +#endif /* __ISAPNP__ */ + +static void snd_interwave_free(snd_card_t *card) +{ + struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data; + + if (iwcard == NULL) + return; +#ifdef __ISAPNP__ + snd_interwave_deactivate(iwcard); +#endif +#ifdef SNDRV_STB + if (iwcard->i2c_res) { + release_resource(iwcard->i2c_res); + kfree_nocheck(iwcard->i2c_res); + } +#endif + if (iwcard->irq >= 0) + free_irq(iwcard->irq, (void *)iwcard); +} + +static int __init snd_interwave_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1}; + int xirq, xdma1, xdma2; + snd_card_t *card; + struct snd_interwave *iwcard; + cs4231_t *cs4231; + snd_gus_card_t *gus; +#ifdef SNDRV_STB + snd_i2c_bus_t *i2c_bus; +#endif + snd_pcm_t *pcm; + char *str; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_interwave)); + if (card == NULL) + return -ENOMEM; + iwcard = (struct snd_interwave *)card->private_data; + iwcard->card = card; + iwcard->irq = -1; + card->private_free = snd_interwave_free; +#ifdef __ISAPNP__ + if (isapnp[dev] && snd_interwave_isapnp(dev, iwcard)) { + snd_card_free(card); + return -ENODEV; + } +#endif + xirq = irq[dev]; + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + xdma1 = dma1[dev]; + if (xdma1 == SNDRV_AUTO_DMA) { + if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + xdma2 = dma2[dev]; + if (xdma2 == SNDRV_AUTO_DMA) { + if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_gus_create(card, + port[dev], + -xirq, xdma1, xdma2, + 0, 32, + pcm_channels[dev], effect[dev], &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_interwave_detect(iwcard, gus, dev +#ifdef SNDRV_STB + , &i2c_bus +#endif + )) < 0) { + snd_card_free(card); + return err; + } + iwcard->gus_status_reg = gus->gf1.reg_irqstat; + iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; + + snd_interwave_init(dev, gus); + snd_interwave_detect_memory(gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + + if (request_irq(xirq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) { + snd_card_free(card); + snd_printk("unable to grab IRQ %d\n", irq); + return -EBUSY; + } + iwcard->irq = xirq; + + if ((err = snd_cs4231_create(card, + gus->gf1.port + 0x10c, -1, xirq, + xdma2 < 0 ? xdma1 : xdma2, xdma1, + CS4231_HW_INTERWAVE, + CS4231_HWSHARE_IRQ | + CS4231_HWSHARE_DMA1 | + CS4231_HWSHARE_DMA2, + &cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); + strcat(pcm->name, " (codec)"); + if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + if (pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_interwave_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } +#ifdef SNDRV_STB + { + snd_ctl_elem_id_t id1, id2; + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id1.name, "Master Playback Switch"); + strcpy(id2.name, id1.name); + id2.index = 1; + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { + snd_card_free(card); + return err; + } + strcpy(id1.name, "Master Playback Volume"); + strcpy(id2.name, id1.name); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) { + snd_card_free(card); + return err; + } + } +#endif + + gus->uart_enable = midi[dev]; + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + +#ifndef SNDRV_STB + str = "AMD InterWave"; + if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) + str = "Dynasonic 3-D"; +#else + str = "InterWave STB"; +#endif + strcpy(card->driver, str); + strcpy(card->shortname, str); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", + str, + gus->gf1.port, + xirq, + xdma1); + if (xdma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", xdma2); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + iwcard->cs4231 = cs4231; + iwcard->gus = gus; + snd_interwave_cards[dev++] = card; + return 0; +} + +static int __init snd_interwave_probe_legacy_port(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + port[dev] = xport; + res = snd_interwave_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +#ifdef __ISAPNP__ + +static int __init snd_interwave_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || !isapnp[dev]) + continue; + snd_interwave_isapnp_cards[dev] = card; + snd_interwave_isapnp_id[dev] = id; + res = snd_interwave_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_interwave_init(void) +{ + int cards = 0; + static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (!snd_interwave_probe(dev)) { + cards++; + continue; + } +#ifdef MODULE + printk(KERN_ERR "InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, port[dev]); +#endif + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards */ + cards += isapnp_probe_cards(snd_interwave_pnpids, snd_interwave_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "InterWave soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_interwave_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_interwave_cards[dev]); +} + +module_init(alsa_card_interwave_init) +module_exit(alsa_card_interwave_exit) + +#ifndef MODULE + +/* format is: snd-interwave=enable,index,id,isapnp, + port[,port_tc],irq, + dma1,dma2, + joystick_dac,midi, + pcm_channels,effect */ + +static int __init alsa_card_interwave_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && +#ifdef SNDRV_STB + get_option(&str,(int *)&port_tc[nr_dev]) == 2 && +#endif + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2 && + get_option(&str,&joystick_dac[nr_dev]) == 2 && + get_option(&str,&midi[nr_dev]) == 2 && + get_option(&str,&pcm_channels[nr_dev]) == 2 && + get_option(&str,&effect[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +#ifndef SNDRV_STB +__setup("snd-interwave=", alsa_card_interwave_setup); +#else +__setup("snd-interwave-stb=", alsa_card_interwave_setup); +#endif + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/opl3sa2.c linux/sound/isa/opl3sa2.c --- linux-2.4.21-rc1.orig/sound/isa/opl3sa2.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/opl3sa2.c 2003-01-31 08:19:44.000000000 -0700 @@ -0,0 +1,948 @@ +/* + * Driver for Yamaha OPL3-SA[2,3] soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Yamaha OPL3SA2+"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Yamaha,YMF719E-S}," + "{Genius,Sound Maker 3DX}," + "{Yamaha,OPL3SA3}," + "{Intel,AL440LX sound}," + "{NeoMagic,MagicWave 3DX}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0xf86,0x370,0x100 */ +static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x388 */ +static long midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 0,1,3,5,9,11,12,15 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* 0,1,2,3 */ /*SL Added*/ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0xf86},{0x370},{0x100}},dialog:list"); +MODULE_PARM(sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(sb_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260}},dialog:list"); +MODULE_PARM(wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(wss_port, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0x388}},dialog:list"); +MODULE_PARM(midi_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(midi_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{0},{1},{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(opl3sa3_ymode, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); /* SL Added */ +MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi."); +MODULE_PARM_SYNTAX(opl3sa3_ymode, SNDRV_ENABLED ",allows:{{0,3}},dialog:list"); /* SL Added */ + +/* control ports */ +#define OPL3SA2_PM_CTRL 0x01 +#define OPL3SA2_SYS_CTRL 0x02 +#define OPL3SA2_IRQ_CONFIG 0x03 +#define OPL3SA2_IRQ_STATUS 0x04 +#define OPL3SA2_DMA_CONFIG 0x06 +#define OPL3SA2_MASTER_LEFT 0x07 +#define OPL3SA2_MASTER_RIGHT 0x08 +#define OPL3SA2_MIC 0x09 +#define OPL3SA2_MISC 0x0A + +/* opl3sa3 only */ +#define OPL3SA3_DGTL_DOWN 0x12 +#define OPL3SA3_ANLG_DOWN 0x13 +#define OPL3SA3_WIDE 0x14 +#define OPL3SA3_BASS 0x15 +#define OPL3SA3_TREBLE 0x16 + +/* power management bits */ +#define OPL3SA2_PM_ADOWN 0x20 +#define OPL3SA2_PM_PSV 0x04 +#define OPL3SA2_PM_PDN 0x02 +#define OPL3SA2_PM_PDX 0x01 + +#define OPL3SA2_PM_D0 0x00 +#define OPL3SA2_PM_D3 (OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX) + +typedef struct snd_opl3sa2 opl3sa2_t; +#define chip_t opl3sa2_t + +struct snd_opl3sa2 { + snd_card_t *card; + int version; /* 2 or 3 */ + unsigned long port; /* control port */ + struct resource *res_port; /* control port resource */ + int irq; + int single_dma; + spinlock_t reg_lock; + snd_hwdep_t *synth; + snd_rawmidi_t *rmidi; + cs4231_t *cs4231; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#endif + unsigned char ctlregs[0x20]; + int ymode; /* SL added */ + snd_kcontrol_t *master_switch; + snd_kcontrol_t *master_volume; +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + void (*cs4231_suspend)(cs4231_t *); + void (*cs4231_resume)(cs4231_t *); +#endif +}; + +static snd_card_t *snd_opl3sa2_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_opl3sa2_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_opl3sa2_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_OPL3SA2(_va, _vb, _vc, _device, _function) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _function), } \ + } + +static struct isapnp_card_id snd_opl3sa2_pnpids[] __devinitdata = { + /* Yamaha YMF719E-S (Genius Sound Maker 3DX) */ + ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021), + /* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */ + ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021), + /* Yamaha OPL3-SA2 */ + ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021), + /* NeoMagic MagicWave 3DX */ + ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_opl3sa2_pnpids); + +#endif /* __ISAPNP__ */ + + +/* read control port (w/o spinlock) */ +static unsigned char __snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) +{ + unsigned char result; +#if 0 + outb(0x1d, port); /* password */ + printk("read [0x%lx] = 0x%x\n", port, inb(port)); +#endif + outb(reg, chip->port); /* register */ + result = inb(chip->port + 1); +#if 0 + printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port)); +#endif + return result; +} + +/* read control port (with spinlock) */ +static unsigned char snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) +{ + unsigned long flags; + unsigned char result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = __snd_opl3sa2_read(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* write control port (w/o spinlock) */ +static void __snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) +{ +#if 0 + outb(0x1d, port); /* password */ +#endif + outb(reg, chip->port); /* register */ + outb(value, chip->port + 1); + chip->ctlregs[reg] = value; +} + +/* write control port (with spinlock) */ +static void snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __snd_opl3sa2_write(chip, reg, value); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int __init snd_opl3sa2_detect(opl3sa2_t *chip) +{ + snd_card_t *card; + unsigned long port; + unsigned char tmp, tmp1; + char str[2]; + + card = chip->card; + port = chip->port; + if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) + return -EBUSY; + // snd_printk("REG 0A = 0x%x\n", snd_opl3sa2_read(chip, 0x0a)); + chip->version = 0; + tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC); + if (tmp == 0xff) { + snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp); + return -ENODEV; + } + switch (tmp & 0x07) { + case 0x01: + chip->version = 2; /* YMF711 */ + break; + default: + chip->version = 3; + /* 0x02 - standard */ + /* 0x03 - YM715B */ + /* 0x04 - YM719 - OPL-SA4? */ + /* 0x05 - OPL3-SA3 - Libretto 100 */ + break; + } + str[0] = chip->version + '0'; + str[1] = 0; + strcat(card->shortname, str); + snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7); + if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) { + snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1); + return -ENODEV; + } + /* try if the MIC register is accesible */ + tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC); + snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a); + if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) { + snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1); + return -ENODEV; + } + snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f); + /* initialization */ + /* Power Management - full on */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); + if (chip->version > 2) { + /* ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */ + snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, (chip->ymode << 4)); + } else { + /* default for opl3sa2 versions */ + snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, 0x00); + } + snd_opl3sa2_write(chip, OPL3SA2_IRQ_CONFIG, 0x0d); /* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */ + if (chip->single_dma) { + snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x03); /* DMA Configuration - DMA A = WSS-R + WSS-P */ + } else { + snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x21); /* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */ + } + snd_opl3sa2_write(chip, OPL3SA2_MISC, 0x80 | (tmp & 7)); /* Miscellaneous - default */ + if (chip->version > 2) { + snd_opl3sa2_write(chip, OPL3SA3_DGTL_DOWN, 0x00); /* Digital Block Partial Power Down - default */ + snd_opl3sa2_write(chip, OPL3SA3_ANLG_DOWN, 0x00); /* Analog Block Partial Power Down - default */ + } + return 0; +} + +static void snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned short status; + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev_id, return); + + if (chip == NULL || chip->card == NULL) + return; + + status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS); + + if (status & 0x20) + snd_opl3_interrupt(chip->synth); + + if ((status & 0x10) && chip->rmidi != NULL) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + + if (status & 0x07) /* TI,CI,PI */ + snd_cs4231_interrupt(irq, chip->cs4231, regs); + + if (status & 0x40) { /* hardware volume change */ + /* reading from Master Lch register at 0x07 clears this bit */ + snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT); + snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT); + if (chip->master_switch && chip->master_volume) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + } +} + +#define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_opl3sa2_info_single, \ + .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_opl3sa2_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_opl3sa2_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_opl3sa2_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val, oval; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = chip->ctlregs[reg]; + val = (oval & ~(mask << shift)) | val; + change = val != oval; + __snd_opl3sa2_write(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_opl3sa2_info_double, \ + .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_opl3sa2_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_opl3sa2_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_opl3sa2_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + oval1 = chip->ctlregs[left_reg]; + oval2 = chip->ctlregs[right_reg]; + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + __snd_opl3sa2_write(chip, left_reg, val1); + __snd_opl3sa2_write(chip, right_reg, val2); + } else { + oval1 = chip->ctlregs[left_reg]; + val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval1; + __snd_opl3sa2_write(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define OPL3SA2_CONTROLS (sizeof(snd_opl3sa2_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opl3sa2_controls[] = { +OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), +OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), +OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1), +OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) +}; + +#define OPL3SA2_TONE_CONTROLS (sizeof(snd_opl3sa2_tone_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opl3sa2_tone_controls[] = { +OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0), +OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0), +OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0) +}; + +static void snd_opl3sa2_master_free(snd_kcontrol_t *kcontrol) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_switch = NULL; + chip->master_volume = NULL; +} + +static int __init snd_opl3sa2_mixer(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 to CD */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUX1 to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "FM Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* add OPL3SA2 controls */ + for (idx = 0; idx < OPL3SA2_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0) + return err; + switch (idx) { + case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break; + case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break; + } + } + if (chip->version > 2) { + for (idx = 0; idx < OPL3SA2_TONE_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +/* Power Management support functions */ +#ifdef CONFIG_PM +static void snd_opl3sa2_suspend(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + /* FIXME: is this order ok? */ + chip->cs4231_suspend(chip->cs4231); + snd_pcm_suspend_all(chip->cs4231->pcm); + + /* power down */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void snd_opl3sa2_resume(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + /* power up */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); + + /* restore registers */ + for (i = 2; i <= 0x0a; i++) { + if (i != OPL3SA2_IRQ_STATUS) + snd_opl3sa2_write(chip, i, chip->ctlregs[i]); + } + if (chip->version > 2) { + for (i = 0x12; i <= 0x16; i++) + snd_opl3sa2_write(chip, i, chip->ctlregs[i]); + } + /* restore cs4231 */ + chip->cs4231_resume(chip->cs4231); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +/* callback for control API */ +static int snd_opl3sa2_set_power_state(snd_card_t *card, unsigned int power_state) +{ + opl3sa2_t *chip = (opl3sa2_t *) card->power_state_private_data; + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_opl3sa2_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_opl3sa2_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_opl3sa2_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + snd_opl3sa2_suspend(chip); + break; + case PM_RESUME: + snd_opl3sa2_resume(chip); + break; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#ifdef __ISAPNP__ +static int __init snd_opl3sa2_isapnp(int dev, opl3sa2_t *chip) +{ + const struct isapnp_card_id *id = snd_opl3sa2_isapnp_id[dev]; + struct isapnp_card *card = snd_opl3sa2_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + chip->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (chip->dev->active) { + chip->dev = NULL; + return -EBUSY; + } + /* PnP initialization */ + pdev = chip->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + if (sb_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], sb_port[dev], 16); + if (wss_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], wss_port[dev], 8); + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], fm_port[dev], 4); + if (midi_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[3], midi_port[dev], 2); + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[4], port[dev], 2); + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev], 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + sb_port[dev] = pdev->resource[0].start; + wss_port[dev] = pdev->resource[1].start; + fm_port[dev] = pdev->resource[2].start; + midi_port[dev] = pdev->resource[3].start; + port[dev] = pdev->resource[4].start; + dma1[dev] = pdev->dma_resource[0].start; + dma2[dev] = pdev->dma_resource[1].start; + irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", + sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]); + snd_printdd("isapnp OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", + port[dev], dma1[dev], dma2[dev], irq[dev]); + return 0; +} + +static void snd_opl3sa2_deactivate(opl3sa2_t *chip) +{ + if (chip->dev) { + chip->dev->deactivate(chip->dev); + chip->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static int snd_opl3sa2_free(opl3sa2_t *chip) +{ +#ifdef __ISAPNP__ + snd_opl3sa2_deactivate(chip); +#endif +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_opl3sa2_dev_free(snd_device_t *device) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, device->device_data, return -ENXIO); + return snd_opl3sa2_free(chip); +} + +static int __init snd_opl3sa2_probe(int dev) +{ + int xirq, xdma1, xdma2; + snd_card_t *card; + struct snd_opl3sa2 *chip; + cs4231_t *cs4231; + opl3_t *opl3; + static snd_device_ops_t ops = { + .dev_free = snd_opl3sa2_dev_free, + }; + int err; + +#ifdef __ISAPNP__ + if (!isapnp[dev]) { +#endif + if (port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify port\n"); + return -EINVAL; + } + if (wss_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify wss_port\n"); + return -EINVAL; + } + if (fm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify fm_port\n"); + return -EINVAL; + } + if (midi_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify midi_port\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + strcpy(card->driver, "OPL3SA2"); + strcpy(card->shortname, "Yamaha OPL3-SA2"); + chip = snd_magic_kcalloc(opl3sa2_t, 0, GFP_KERNEL); + if (chip == NULL) { + err = -ENOMEM; + goto __error; + } + chip->irq = -1; + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; +#ifdef __ISAPNP__ + if (isapnp[dev] && (err = snd_opl3sa2_isapnp(dev, chip)) < 0) + goto __error; +#endif + chip->ymode = opl3sa3_ymode[dev] & 0x03 ; /* initialise this card from supplied (or default) parameter*/ + chip->card = card; + chip->port = port[dev]; + xirq = irq[dev]; + xdma1 = dma1[dev]; + xdma2 = dma2[dev]; + if (xdma2 < 0) + chip->single_dma = 1; + if ((err = snd_opl3sa2_detect(chip)) < 0) + goto __error; + if (request_irq(xirq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2/3", (void *)chip)) { + err = -ENODEV; + goto __error; + } + chip->irq = xirq; + if ((err = snd_cs4231_create(card, + wss_port[dev] + 4, -1, + xirq, xdma1, xdma2, + CS4231_HW_OPL3SA2, + CS4231_HWSHARE_IRQ, + &cs4231)) < 0) { + snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4); + goto __error; + } + chip->cs4231 = cs4231; + if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) + goto __error; + if ((err = snd_cs4231_mixer(cs4231)) < 0) + goto __error; + if ((err = snd_opl3sa2_mixer(chip)) < 0) + goto __error; + if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0) + goto __error; + if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) { + if ((err = snd_opl3_create(card, fm_port[dev], + fm_port[dev] + 2, + OPL3_HW_OPL3, 0, &opl3)) < 0) + goto __error; + if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0) + goto __error; + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0) + goto __error; + } + if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x340) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2, + midi_port[dev], 0, + xirq, 0, &chip->rmidi)) < 0) + goto __error; + } +#ifdef CONFIG_PM + /* Power Management */ + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_opl3sa2_pm_callback); + if (chip->pm_dev) { + chip->pm_dev->data = chip; + /* remember callbacks for cs4231 - they are called inside + * opl3sa2 pm callback + */ + chip->cs4231_suspend = chip->cs4231->suspend; + chip->cs4231_resume = chip->cs4231->resume; + /* now clear callbacks for cs4231 */ + chip->cs4231->suspend = NULL; + chip->cs4231->resume = NULL; + /* set control api callback */ + card->set_power_state = snd_opl3sa2_set_power_state; + card->power_state_private_data = chip; + } +#endif + + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + card->shortname, chip->port, xirq, xdma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", xdma2); + + if ((err = snd_card_register(card)) < 0) + goto __error; + + snd_opl3sa2_cards[dev] = card; + return 0; + + __error: + snd_card_free(card); + return err; +} + +#ifdef __ISAPNP__ +static int __init snd_opl3sa2_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_opl3sa2_isapnp_cards[dev] = card; + snd_opl3sa2_isapnp_id[dev] = id; + res = snd_opl3sa2_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_opl3sa2_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (snd_opl3sa2_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_opl3sa2_pnpids, snd_opl3sa2_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_opl3sa2_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_opl3sa2_cards[idx]); +} + +module_init(alsa_card_opl3sa2_init) +module_exit(alsa_card_opl3sa2_exit) + +#ifndef MODULE + +/* format is: snd-opl3sa2=enable,index,id,isapnp, + port,sb_port,wss_port,fm_port, + midi_port,irq,dma1,dma2, + opl3sa3_ymode */ + +static int __init alsa_card_opl3sa2_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&sb_port[nr_dev]) == 2 && + get_option(&str,(int *)&wss_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&midi_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2 && + get_option(&str,&opl3sa3_ymode[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-opl3sa2=", alsa_card_opl3sa2_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/opti9xx/Makefile linux/sound/isa/opti9xx/Makefile --- linux-2.4.21-rc1.orig/sound/isa/opti9xx/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/opti9xx/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,28 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _opti9xx.o + +list-multi := snd-opti92x-ad1848.o snd-opti92x-cs4231.o snd-opti93x.o + +snd-opti92x-ad1848-objs := opti92x-ad1848.o +snd-opti92x-cs4231-objs := opti92x-cs4231.o +snd-opti93x-objs := opti93x.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o +obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o + +include $(TOPDIR)/Rules.make + +snd-opti92x-ad1848.o: $(snd-opti92x-ad1848-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-ad1848-objs) + +snd-opti92x-cs4231.o: $(snd-opti92x-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-cs4231-objs) + +snd-opti93x.o: $(snd-opti93x-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti93x-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/opti9xx/opti92x-ad1848.c linux/sound/isa/opti9xx/opti92x-ad1848.c --- linux-2.4.21-rc1.orig/sound/isa/opti9xx/opti92x-ad1848.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/opti9xx/opti92x-ad1848.c 2003-03-13 02:24:03.000000000 -0700 @@ -0,0 +1,2241 @@ +/* + card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. + Copyright (C) 1998-2000 by Massimo Piccioni + + Part of this code was developed at the Italian Ministry of Air Defence, + Sixth Division (oh, che pace ...), Rome. + + Thanks to Maria Grazia Pollarini, Salvatore Vassallo. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#ifdef CS4231 +#include +#else +#ifndef OPTi93X +#include +#else +#include +#include +#endif /* OPTi93X */ +#endif /* CS4231 */ +#include +#include +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +#ifdef OPTi93X +MODULE_DESCRIPTION("OPTi93X"); +MODULE_DEVICES("{{OPTi,82C931/3}}"); +#else /* OPTi93X */ +#ifdef CS4231 +MODULE_DESCRIPTION("OPTi92X - CS4231"); +MODULE_DEVICES("{{OPTi,82C924 (CS4231)}," + "{OPTi,82C925 (CS4231)}}"); +#else /* CS4231 */ +MODULE_DESCRIPTION("OPTi92X - AD1848"); +MODULE_DEVICES("{{OPTi,82C924 (AD1848)}," + "{OPTi,82C925 (AD1848)}," + "{OAK,Mozart}}"); +#endif /* CS4231 */ +#endif /* OPTi93X */ + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ +static int isapnp = 1; /* Enable ISA PnP detection */ +static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ +static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ +static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ +static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ +static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ +static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ +#if defined(CS4231) || defined(OPTi93X) +static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ +#endif /* CS4231 || OPTi93X */ + +MODULE_PARM(index, "i"); +MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "s"); +MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +//MODULE_PARM(enable, "i"); +//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); +//MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(port, "l"); +MODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT_DESC); +MODULE_PARM(mpu_port, "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(fm_port, "l"); +MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT_DESC); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver."); +MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma1, "i"); +MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +#if defined(CS4231) || defined(OPTi93X) +MODULE_PARM(dma2, "i"); +MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); +#endif /* CS4231 || OPTi93X */ + +#define OPTi9XX_HW_DETECT 0 +#define OPTi9XX_HW_82C928 1 +#define OPTi9XX_HW_82C929 2 +#define OPTi9XX_HW_82C924 3 +#define OPTi9XX_HW_82C925 4 +#define OPTi9XX_HW_82C930 5 +#define OPTi9XX_HW_82C931 6 +#define OPTi9XX_HW_82C933 7 +#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 + +#define OPTi9XX_MC_REG(n) n + +typedef struct _snd_opti9xx opti9xx_t; + +#ifdef OPTi93X + +#define OPTi93X_INDEX 0x00 +#define OPTi93X_DATA 0x01 +#define OPTi93X_STATUS 0x02 +#define OPTi93X_DDATA 0x03 +#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) + +#define OPTi93X_MIXOUT_LEFT 0x00 +#define OPTi93X_MIXOUT_RIGHT 0x01 +#define OPTi93X_CD_LEFT_INPUT 0x02 +#define OPTi93X_CD_RIGHT_INPUT 0x03 +#define OPTi930_AUX_LEFT_INPUT 0x04 +#define OPTi930_AUX_RIGHT_INPUT 0x05 +#define OPTi931_FM_LEFT_INPUT 0x04 +#define OPTi931_FM_RIGHT_INPUT 0x05 +#define OPTi93X_DAC_LEFT 0x06 +#define OPTi93X_DAC_RIGHT 0x07 +#define OPTi93X_PLAY_FORMAT 0x08 +#define OPTi93X_IFACE_CONF 0x09 +#define OPTi93X_PIN_CTRL 0x0a +#define OPTi93X_ERR_INIT 0x0b +#define OPTi93X_ID 0x0c +#define OPTi93X_PLAY_UPR_CNT 0x0e +#define OPTi93X_PLAY_LWR_CNT 0x0f +#define OPTi931_AUX_LEFT_INPUT 0x10 +#define OPTi931_AUX_RIGHT_INPUT 0x11 +#define OPTi93X_LINE_LEFT_INPUT 0x12 +#define OPTi93X_LINE_RIGHT_INPUT 0x13 +#define OPTi93X_MIC_LEFT_INPUT 0x14 +#define OPTi93X_MIC_RIGHT_INPUT 0x15 +#define OPTi93X_OUT_LEFT 0x16 +#define OPTi93X_OUT_RIGHT 0x17 +#define OPTi93X_CAPT_FORMAT 0x1c +#define OPTi93X_CAPT_UPR_CNT 0x1e +#define OPTi93X_CAPT_LWR_CNT 0x1f + +#define OPTi93X_TRD 0x20 +#define OPTi93X_MCE 0x40 +#define OPTi93X_INIT 0x80 + +#define OPTi93X_MIXOUT_MIC_GAIN 0x20 +#define OPTi93X_MIXOUT_LINE 0x00 +#define OPTi93X_MIXOUT_CD 0x40 +#define OPTi93X_MIXOUT_MIC 0x80 +#define OPTi93X_MIXOUT_MIXER 0xc0 + +#define OPTi93X_STEREO 0x10 +#define OPTi93X_LINEAR_8 0x00 +#define OPTi93X_ULAW_8 0x20 +#define OPTi93X_LINEAR_16_LIT 0x40 +#define OPTi93X_ALAW_8 0x60 +#define OPTi93X_ADPCM_16 0xa0 +#define OPTi93X_LINEAR_16_BIG 0xc0 + +#define OPTi93X_CAPTURE_PIO 0x80 +#define OPTi93X_PLAYBACK_PIO 0x40 +#define OPTi93X_AUTOCALIB 0x08 +#define OPTi93X_SINGLE_DMA 0x04 +#define OPTi93X_CAPTURE_ENABLE 0x02 +#define OPTi93X_PLAYBACK_ENABLE 0x01 + +#define OPTi93X_IRQ_ENABLE 0x02 + +#define OPTi93X_DMA_REQUEST 0x10 +#define OPTi93X_CALIB_IN_PROGRESS 0x20 + +#define OPTi93X_IRQ_PLAYBACK 0x04 +#define OPTi93X_IRQ_CAPTURE 0x08 + + +typedef struct _snd_opti93x opti93x_t; + +struct _snd_opti93x { + unsigned long port; + struct resource *res_port; + int irq; + int dma1; + int dma2; + + opti9xx_t *chip; + unsigned short hardware; + unsigned char image[32]; + + unsigned char mce_bit; + unsigned short mode; + int mute; + + spinlock_t lock; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; +}; + +#define OPTi93X_MODE_NONE 0x00 +#define OPTi93X_MODE_PLAY 0x01 +#define OPTi93X_MODE_CAPTURE 0x02 +#define OPTi93X_MODE_OPEN (OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE) + +#endif /* OPTi93X */ + +struct _snd_opti9xx { + unsigned short hardware; + unsigned char password; + char name[7]; + + unsigned long mc_base; + struct resource *res_mc_base; + unsigned long mc_base_size; +#ifdef OPTi93X + unsigned long mc_indir_index; +#endif /* OPTi93X */ + unsigned long pwd_reg; + + spinlock_t lock; + + long wss_base; + int irq; + int dma1; +#if defined(CS4231) || defined(OPTi93X) + int dma2; +#endif /* CS4231 || OPTi93X */ + + long fm_port; + + long mpu_port; + int mpu_irq; + +#if defined(OPTi93X) + opti93x_t *opti93x; +#elif defined(CS4231) + cs4231_t *cs4231; +#else + ad1848_t *ad1848; +#endif /* AD1848 */ + snd_rawmidi_t *rmidi; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_opti9xx_card = SNDRV_DEFAULT_PTR1; + +#ifdef __ISAPNP__ + +#define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \ + ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \ + } + +static struct isapnp_card_id snd_card_opti9xx_pnpids[] = { +#ifndef OPTi93X + /* OPTi 82C924 */ + ISAPNP_OPTI9XX('O','P','T',0x0924,'O','P','T',0x0000,0x0002), + /* OPTi 82C925 */ + ISAPNP_OPTI9XX('O','P','T',0x0925,'O','P','T',0x9250,0x0002), +#else + /* OPTi 82C931/3 */ + ISAPNP_OPTI9XX('O','P','T',0x0931,'O','P','T',0x9310,0x0002), +#endif /* OPTi93X */ + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_card_opti9xx_pnpids); + +#endif /* __ISAPNP__ */ + +#ifdef OPTi93X +#define DRIVER_NAME "snd-card-opti93x" +#else +#define DRIVER_NAME "snd-card-opti92x" +#endif /* OPTi93X */ + +static char * snd_opti9xx_names[] = { + "unkown", + "82C928", "82C929", + "82C924", "82C925", + "82C930", "82C931", "82C933" +}; + + +static long snd_legacy_find_free_ioport(long *port_table, long size) +{ + while (*port_table != -1) { + if (!check_region(*port_table, size)) + return *port_table; + port_table++; + } + return -1; +} + +static int __init snd_opti9xx_init(opti9xx_t *chip, unsigned short hardware) +{ + static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; + + chip->hardware = hardware; + strcpy(chip->name, snd_opti9xx_names[hardware]); + + chip->mc_base_size = opti9xx_mc_size[hardware]; + + spin_lock_init(&chip->lock); + + chip->wss_base = -1; + chip->irq = -1; + chip->dma1 = -1; +#if defined(CS4231) || defined (OPTi93X) + chip->dma2 = -1; +#endif /* CS4231 || OPTi93X */ + chip->fm_port = -1; + chip->mpu_port = -1; + chip->mpu_irq = -1; + + switch (hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + chip->mc_base = 0xf8c; + chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; + chip->pwd_reg = 3; + break; + + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + chip->mc_base = 0xf8c; + chip->password = 0xe5; + chip->pwd_reg = 3; + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; + chip->mc_indir_index = 0xe0e; + chip->password = 0xe4; + chip->pwd_reg = 0; + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", hardware); + return -ENODEV; + } + return 0; +} + +static unsigned char snd_opti9xx_read(opti9xx_t *chip, + unsigned char reg) +{ + unsigned long flags; + unsigned char retval = 0xff; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + retval = inb(chip->mc_base + reg); + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + outb(reg, chip->mc_indir_index); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_indir_index + 1); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); + return retval; +} + +static void snd_opti9xx_write(opti9xx_t *chip, unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + outb(value, chip->mc_base + reg); + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + outb(reg, chip->mc_indir_index); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_indir_index + 1); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +#define snd_opti9xx_write_mask(chip, reg, value, mask) \ + snd_opti9xx_write(chip, reg, \ + (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) + + +static int __init snd_opti9xx_configure(opti9xx_t *chip) +{ + unsigned char wss_base_bits; + unsigned char irq_bits; + unsigned char dma_bits; + unsigned char mpu_port_bits = 0; + unsigned char mpu_irq_bits; + unsigned long flags; + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); + + case OPTi9XX_HW_82C925: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); +#ifdef CS4231 + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); +#else + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); +#endif /* CS4231 */ + break; + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + /* + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); + */ + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); +#ifdef CS4231 + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); +#else + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); +#endif /* CS4231 */ + break; + +#else /* OPTi93X */ + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | + (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), + 0x34); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + return -EINVAL; + } + + switch (chip->wss_base) { + case 0x530: + wss_base_bits = 0x00; + break; + case 0x604: + wss_base_bits = 0x03; + break; + case 0xe80: + wss_base_bits = 0x01; + break; + case 0xf40: + wss_base_bits = 0x02; + break; + default: + snd_printk("WSS port 0x%lx not valid\n", chip->wss_base); + goto __skip_base; + } + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); + +__skip_base: + switch (chip->irq) { +#ifdef OPTi93X + case 5: + irq_bits = 0x05; + break; +#endif /* OPTi93X */ + case 7: + irq_bits = 0x01; + break; + case 9: + irq_bits = 0x02; + break; + case 10: + irq_bits = 0x03; + break; + case 11: + irq_bits = 0x04; + break; + default: + snd_printk("WSS irq # %d not valid\n", chip->irq); + goto __skip_resources; + } + + switch (chip->dma1) { + case 0: + dma_bits = 0x01; + break; + case 1: + dma_bits = 0x02; + break; + case 3: + dma_bits = 0x03; + break; + default: + snd_printk("WSS dma1 # %d not valid\n", chip->dma1); + goto __skip_resources; + } + +#if defined(CS4231) || defined(OPTi93X) + if (chip->dma1 == chip->dma2) { + snd_printk("don't want to share dmas\n"); + return -EBUSY; + } + + switch (chip->dma2) { + case 0: + case 1: + break; + default: + snd_printk("WSS dma2 # %d not valid\n", chip->dma2); + goto __skip_resources; + } + dma_bits |= 0x04; +#endif /* CS4231 || OPTi93X */ + + spin_lock_irqsave(&chip->lock, flags); + snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); + spin_unlock_irqrestore(&chip->lock, flags); + +__skip_resources: + if (chip->hardware > OPTi9XX_HW_82C928) { + switch (chip->mpu_port) { + case -1: + break; + case 0x300: + mpu_port_bits = 0x03; + break; + case 0x310: + mpu_port_bits = 0x02; + break; + case 0x320: + mpu_port_bits = 0x01; + break; + case 0x330: + mpu_port_bits = 0x00; + break; + default: + snd_printk("MPU-401 port 0x%lx not valid\n", + chip->mpu_port); + goto __skip_mpu; + } + + switch (chip->mpu_irq) { + case 5: + mpu_irq_bits = 0x02; + break; + case 7: + mpu_irq_bits = 0x03; + break; + case 9: + mpu_irq_bits = 0x00; + break; + case 10: + mpu_irq_bits = 0x01; + break; + default: + snd_printk("MPU-401 irq # %d not valid\n", + chip->mpu_irq); + goto __skip_mpu; + } + + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), + (chip->mpu_port == -1) ? 0x00 : + 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, + 0xf8); + } +__skip_mpu: + + return 0; +} + +#ifdef OPTi93X + +#define chip_t opti93x_t + +static unsigned char snd_opti93x_default_image[32] = +{ + 0x00, /* 00/00 - l_mixout_outctrl */ + 0x00, /* 01/01 - r_mixout_outctrl */ + 0x88, /* 02/02 - l_cd_inctrl */ + 0x88, /* 03/03 - r_cd_inctrl */ + 0x88, /* 04/04 - l_a1/fm_inctrl */ + 0x88, /* 05/05 - r_a1/fm_inctrl */ + 0x80, /* 06/06 - l_dac_inctrl */ + 0x80, /* 07/07 - r_dac_inctrl */ + 0x00, /* 08/08 - ply_dataform_reg */ + 0x00, /* 09/09 - if_conf */ + 0x00, /* 0a/10 - pin_ctrl */ + 0x00, /* 0b/11 - err_init_reg */ + 0x0a, /* 0c/12 - id_reg */ + 0x00, /* 0d/13 - reserved */ + 0x00, /* 0e/14 - ply_upcount_reg */ + 0x00, /* 0f/15 - ply_lowcount_reg */ + 0x88, /* 10/16 - reserved/l_a1_inctrl */ + 0x88, /* 11/17 - reserved/r_a1_inctrl */ + 0x88, /* 12/18 - l_line_inctrl */ + 0x88, /* 13/19 - r_line_inctrl */ + 0x88, /* 14/20 - l_mic_inctrl */ + 0x88, /* 15/21 - r_mic_inctrl */ + 0x80, /* 16/22 - l_out_outctrl */ + 0x80, /* 17/23 - r_out_outctrl */ + 0x00, /* 18/24 - reserved */ + 0x00, /* 19/25 - reserved */ + 0x00, /* 1a/26 - reserved */ + 0x00, /* 1b/27 - reserved */ + 0x00, /* 1c/28 - cap_dataform_reg */ + 0x00, /* 1d/29 - reserved */ + 0x00, /* 1e/30 - cap_upcount_reg */ + 0x00 /* 1f/31 - cap_lowcount_reg */ +}; + + +static int snd_opti93x_busy_wait(opti93x_t *chip) +{ + int timeout; + + for (timeout = 250; timeout-- > 0; udelay(10)) + if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT)) + return 0; + + snd_printk("chip still busy.\n"); + return -EBUSY; +} + +static unsigned char snd_opti93x_in(opti93x_t *chip, unsigned char reg) +{ + snd_opti93x_busy_wait(chip); + outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); + return inb(OPTi93X_PORT(chip, DATA)); +} + +static void snd_opti93x_out(opti93x_t *chip, unsigned char reg, + unsigned char value) +{ + snd_opti93x_busy_wait(chip); + outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); + outb(value, OPTi93X_PORT(chip, DATA)); +} + +static void snd_opti93x_out_image(opti93x_t *chip, unsigned char reg, + unsigned char value) +{ + snd_opti93x_out(chip, reg, chip->image[reg] = value); +} + +static void snd_opti93x_out_mask(opti93x_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + snd_opti93x_out_image(chip, reg, + (chip->image[reg] & ~mask) | (value & mask)); +} + + +static void snd_opti93x_mce_up(opti93x_t *chip) +{ + snd_opti93x_busy_wait(chip); + + chip->mce_bit = OPTi93X_MCE; + if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)) + outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); +} + +static void snd_opti93x_mce_down(opti93x_t *chip) +{ + snd_opti93x_busy_wait(chip); + + chip->mce_bit = 0; + if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE) + outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); +} + +#define snd_opti93x_mute_reg(chip, reg, mute) \ + snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]); + +static void snd_opti93x_mute(opti93x_t *chip, int mute) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + mute = mute ? 1 : 0; + if (chip->mute == mute) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + chip->mute = mute; + + snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute); + switch (chip->hardware) { + case OPTi9XX_HW_82C930: + snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute); + break; + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute); + } + snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute); + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static unsigned int snd_opti93x_get_count(unsigned char format, + unsigned int size) +{ + switch (format & 0xe0) { + case OPTi93X_LINEAR_16_LIT: + case OPTi93X_LINEAR_16_BIG: + size >>= 1; + break; + case OPTi93X_ADPCM_16: + return size >> 2; + } + return (format & OPTi93X_STEREO) ? (size >> 1) : size; +} + +static unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, + 18900, 22050, 27428, 32000, 33075, 37800, + 44100, 48000 }; +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = RATES, + .list = rates, + .mask = 0, +}; + +static unsigned char bits[] = { 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, + 0x05, 0x07, 0x04, 0x06, 0x0d, 0x09, + 0x0b, 0x0c}; + +static unsigned char snd_opti93x_get_freq(unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < RATES; i++) { + if (rate == rates[i]) + return bits[i]; + } + snd_BUG(); + return bits[RATES-1]; +} + +static unsigned char snd_opti93x_get_format(opti93x_t *chip, + unsigned int format, int channels) +{ + unsigned char retval = OPTi93X_LINEAR_8; + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + retval = OPTi93X_ULAW_8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + retval = OPTi93X_ALAW_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + retval = OPTi93X_LINEAR_16_LIT; + break; + case SNDRV_PCM_FORMAT_S16_BE: + retval = OPTi93X_LINEAR_16_BIG; + break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + retval = OPTi93X_ADPCM_16; + } + return (channels > 1) ? (retval | OPTi93X_STEREO) : retval; +} + + +static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt) +{ + unsigned long flags; + unsigned char mask; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mute(chip, 1); + + snd_opti93x_mce_up(chip); + mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff; + snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt); + snd_opti93x_mce_down(chip); + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mute(chip, 1); + + snd_opti93x_mce_up(chip); + if (!(chip->mode & OPTi93X_MODE_PLAY)) + snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt); + else + fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0; + snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt); + snd_opti93x_mce_down(chip); + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static int snd_opti93x_open(opti93x_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (chip->mode & mode) { + spin_unlock_irqrestore(&chip->lock, flags); + return -EAGAIN; + } + + if (!(chip->mode & OPTi93X_MODE_OPEN)) { + outb(0x00, OPTi93X_PORT(chip, STATUS)); + snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, + OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE); + chip->mode = mode; + } + else + chip->mode |= mode; + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static void snd_opti93x_close(opti93x_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + chip->mode &= ~mode; + if (chip->mode & OPTi93X_MODE_OPEN) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + + snd_opti93x_mute(chip, 1); + + outb(0, OPTi93X_PORT(chip, STATUS)); + snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE, + ~OPTi93X_IRQ_ENABLE); + + snd_opti93x_mce_up(chip); + snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00); + snd_opti93x_mce_down(chip); + chip->mode = 0; + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_opti93x_trigger(snd_pcm_substream_t *substream, + unsigned char what, int cmd) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == chip->playback_substream) { + what |= OPTi93X_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= OPTi93X_CAPTURE_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&chip->lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what); + if (what & OPTi93X_CAPTURE_ENABLE) + udelay(50); + } else + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00); + spin_unlock(&chip->lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_opti93x_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + return snd_opti93x_trigger(substream, + OPTi93X_PLAYBACK_ENABLE, cmd); +} + +static int snd_opti93x_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + return snd_opti93x_trigger(substream, + OPTi93X_CAPTURE_ENABLE, cmd); +} + +static int snd_opti93x_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + + +static int snd_opti93x_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + + +static int snd_opti93x_playback_prepare(snd_pcm_substream_t * substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned char format; + unsigned int count = snd_pcm_lib_period_bytes(substream); + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&chip->lock, flags); + + chip->p_dma_size = size; + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, + OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO, + ~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO)); + + snd_dma_program(chip->dma1, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + format = snd_opti93x_get_freq(runtime->rate); + format |= snd_opti93x_get_format(chip, runtime->format, + runtime->channels); + snd_opti93x_playback_format(chip, format); + format = chip->image[OPTi93X_PLAY_FORMAT]; + + count = snd_opti93x_get_count(format, count) - 1; + snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count); + snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_opti93x_capture_prepare(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned char format; + unsigned int count = snd_pcm_lib_period_bytes(substream); + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&chip->lock, flags); + + chip->c_dma_size = size; + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, + OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, + (unsigned char)~(OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO)); + + snd_dma_program(chip->dma2, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + format = snd_opti93x_get_freq(runtime->rate); + format |= snd_opti93x_get_format(chip, runtime->format, + runtime->channels); + snd_opti93x_capture_format(chip, format); + format = chip->image[OPTi93X_CAPT_FORMAT]; + + count = snd_opti93x_get_count(format, count) - 1; + snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count); + snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_opti93x_playback_pointer(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE)) + return 0; + + ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_opti93x_capture_pointer(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE)) + return 0; + + ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + + +static void snd_opti93x_overrange(opti93x_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02)) + chip->capture_substream->runtime->overrange++; + + spin_unlock_irqrestore(&chip->lock, flags); +} + +void snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + opti93x_t *codec = snd_magic_cast(opti93x_t, dev_id, return); + unsigned char status; + + status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11)); + if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) + snd_pcm_period_elapsed(codec->playback_substream); + if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { + snd_opti93x_overrange(codec); + snd_pcm_period_elapsed(codec->capture_substream); + } + outb(0x00, OPTi93X_PORT(codec, STATUS)); +} + + +static snd_pcm_hardware_t snd_opti93x_playback = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5512, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_opti93x_capture = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 5512, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_opti93x_playback_open(snd_pcm_substream_t *substream) +{ + int error; + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0) + return error; + snd_pcm_set_sync(substream); + chip->playback_substream = substream; + runtime->hw = snd_opti93x_playback; + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return error; +} + +static int snd_opti93x_capture_open(snd_pcm_substream_t *substream) +{ + int error; + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0) + return error; + runtime->hw = snd_opti93x_capture; + snd_pcm_set_sync(substream); + chip->capture_substream = substream; + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return error; +} + +static int snd_opti93x_playback_close(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_opti93x_close(chip, OPTi93X_MODE_PLAY); + return 0; +} + +static int snd_opti93x_capture_close(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE); + return 0; +} + + +static void snd_opti93x_init(opti93x_t *chip) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mce_up(chip); + + for (i = 0; i < 32; i++) + snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]); + + snd_opti93x_mce_down(chip); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_opti93x_probe(opti93x_t *chip) +{ + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f; + spin_unlock_irqrestore(&chip->lock, flags); + + return (val == 0x0a) ? 0 : -ENODEV; +} + +static int snd_opti93x_free(opti93x_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->dma1 >= 0) { + disable_dma(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + disable_dma(chip->dma2); + free_dma(chip->dma2); + } + if (chip->irq >= 0) { + free_irq(chip->irq, chip); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_opti93x_dev_free(snd_device_t *device) +{ + opti93x_t *chip = snd_magic_cast(opti93x_t, device->device_data, return -ENXIO); + return snd_opti93x_free(chip); +} + +static const char *snd_opti93x_chip_id(opti93x_t *codec) +{ + switch (codec->hardware) { + case OPTi9XX_HW_82C930: return "82C930"; + case OPTi9XX_HW_82C931: return "82C931"; + case OPTi9XX_HW_82C933: return "82C933"; + default: return "???"; + } +} + +int snd_opti93x_create(snd_card_t *card, opti9xx_t *chip, + int dma1, int dma2, + opti93x_t **rcodec) +{ + static snd_device_ops_t ops = { + .dev_free = snd_opti93x_dev_free, + }; + int error; + opti93x_t *codec; + + *rcodec = NULL; + codec = snd_magic_kcalloc(opti93x_t, 0, GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + codec->irq = -1; + codec->dma1 = -1; + codec->dma2 = -1; + + if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) { + snd_opti93x_free(codec); + return -EBUSY; + } + if (request_dma(dma1, "OPTI93x - 1")) { + snd_opti93x_free(codec); + return -EBUSY; + } + codec->dma1 = chip->dma1; + if (request_dma(dma2, "OPTI93x - 2")) { + snd_opti93x_free(codec); + return -EBUSY; + } + codec->dma2 = chip->dma2; + + if (request_irq(chip->irq, snd_opti93x_interrupt, SA_INTERRUPT, DRIVER_NAME" - WSS", codec)) { + snd_opti93x_free(codec); + return -EBUSY; + } + + codec->card = card; + codec->port = chip->wss_base + 4; + codec->irq = chip->irq; + + spin_lock_init(&codec->lock); + codec->hardware = chip->hardware; + codec->chip = chip; + + if ((error = snd_opti93x_probe(codec))) { + snd_opti93x_free(codec); + return error; + } + + snd_opti93x_init(codec); + + /* Register device */ + if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) { + snd_opti93x_free(codec); + return error; + } + + *rcodec = codec; + return 0; +} + +static snd_pcm_ops_t snd_opti93x_playback_ops = { + .open = snd_opti93x_playback_open, + .close = snd_opti93x_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_opti93x_hw_params, + .hw_free = snd_opti93x_hw_free, + .prepare = snd_opti93x_playback_prepare, + .trigger = snd_opti93x_playback_trigger, + .pointer = snd_opti93x_playback_pointer, +}; + +static snd_pcm_ops_t snd_opti93x_capture_ops = { + .open = snd_opti93x_capture_open, + .close = snd_opti93x_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_opti93x_hw_params, + .hw_free = snd_opti93x_hw_free, + .prepare = snd_opti93x_capture_prepare, + .trigger = snd_opti93x_capture_trigger, + .pointer = snd_opti93x_capture_pointer, +}; + +static void snd_opti93x_pcm_free(snd_pcm_t *pcm) +{ + opti93x_t *codec = snd_magic_cast(opti93x_t, pcm->private_data, return); + codec->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_opti93x_pcm(opti93x_t *codec, int device, snd_pcm_t **rpcm) +{ + int error; + snd_pcm_t *pcm; + + if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm))) + return error; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops); + + pcm->private_data = codec; + pcm->private_free = snd_opti93x_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + strcpy(pcm->name, snd_opti93x_chip_id(codec)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); + + codec->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_opti93x_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line1", "Aux", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_opti93x_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6; + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_opti93x_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->lock, flags); + left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left; + right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right; + change = left != chip->image[OPTi93X_MIXOUT_LEFT] || + right != chip->image[OPTi93X_MIXOUT_RIGHT]; + snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left); + snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#if 0 + +#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_opti93x_info_single, \ + .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_opti93x_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_opti93x_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_opti93x_out(chip, reg, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#endif /* single */ + +#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_opti93x_info_double, \ + .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \ + do { xctl.private_value ^= 22; } while (0) +#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \ + do { xctl.private_value &= ~0x0000ffff; \ + xctl.private_value |= left_reg | (right_reg << 8); } while (0) + +static int snd_opti93x_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_opti93x_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_opti93x_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_opti93x_out_image(chip, left_reg, val1); + snd_opti93x_out_image(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define OPTi93X_CONTROLS (sizeof(snd_opti93x_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opti93x_controls[] = { +OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), +OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1), +OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1), +OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1), +OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1), +OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), +OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1), +OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1), +OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), +OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_opti93x_info_mux, + .get = snd_opti93x_get_mux, + .put = snd_opti93x_put_mux, +} +}; + +int snd_opti93x_mixer(opti93x_t *chip) +{ + snd_card_t *card; + snd_kcontrol_new_t knew; + int err; + unsigned int idx; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_opti93x_chip_id(chip)); + + for (idx = 0; idx < OPTi93X_CONTROLS; idx++) { + knew = snd_opti93x_controls[idx]; + if (chip->hardware == OPTi9XX_HW_82C930) { + if (strstr(knew.name, "FM")) /* skip FM controls */ + continue; + else if (strcmp(knew.name, "Mic Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + else if (strstr(knew.name, "Aux")) + OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT); + else if (strcmp(knew.name, "PCM Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + else if (strcmp(knew.name, "Master Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + } + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +#endif /* OPTi93X */ + +static int __init snd_card_opti9xx_detect(snd_card_t *card, opti9xx_t *chip) +{ + int i, err; + +#ifndef OPTi93X + for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { + unsigned char value; + + if ((err = snd_opti9xx_init(chip, i)) < 0) + return err; + + if (check_region(chip->mc_base, chip->mc_base_size)) + continue; + + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); + if ((value != 0xff) && (value != inb(chip->mc_base + 1))) + if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) + return 1; + } +#else /* OPTi93X */ + for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { + unsigned long flags; + unsigned char value; + + if ((err = snd_opti9xx_init(chip, i)) < 0) + return err; + + if (check_region(chip->mc_base, chip->mc_base_size)) + continue; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(((chip->mc_indir_index & (1 << 8)) >> 4) | + ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base); + spin_unlock_irqrestore(&chip->lock, flags); + + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); + snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); + if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) + return 1; + } +#endif /* OPTi93X */ + + return -ENODEV; +} + +#ifdef __ISAPNP__ +static int __init snd_card_opti9xx_isapnp(opti9xx_t *chip) +{ + struct isapnp_dev *pdev = NULL; + const struct isapnp_card_id *pid = snd_card_opti9xx_pnpids-1; + static struct isapnp_card *card = NULL; + + __again: + while (1) { + pid++; + if (pid->card_vendor == 0) + return -ENODEV; + if ((card = isapnp_find_card(pid->card_vendor, pid->card_device, card))) + break; + } + if (card == NULL) + return -ENODEV; + + chip->dev = isapnp_find_dev(card, pid->devs[0].vendor, pid->devs[0].function, NULL); + if (chip->dev == NULL) + goto __again; + + chip->devmpu = isapnp_find_dev(card, pid->devs[1].vendor, pid->devs[1].function, NULL); + + pdev = chip->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + +#ifdef OPTi93X + if (port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port + 4, 4); +#else + if ((pid->card_device != ISAPNP_DEVICE(0x0924)) && (port != SNDRV_AUTO_PORT)) + isapnp_resource_change(&pdev->resource[1], port, 4); +#endif /* OPTi93X */ + if (irq != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq, 1); + if (dma1 != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1, 1); +#if defined(CS4231) || defined(OPTi93X) + if (dma2 != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2, 1); +#endif /* CS4231 || OPTi93X */ + if (fm_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], fm_port, 4); + + if (pdev->activate(pdev) < 0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + +#ifdef OPTi93X + port = pdev->resource[0].start - 4; + fm_port = pdev->resource[1].start; +#else + if (pid->card_device != ISAPNP_DEVICE(0x0924)) + port = pdev->resource[1].start; + fm_port = pdev->resource[2].start; +#endif /* OPTi93X */ + irq = pdev->irq_resource[0].start; + dma1 = pdev->dma_resource[0].start; +#if defined(CS4231) || defined(OPTi93X) + dma2 = pdev->dma_resource[1].start; +#endif /* CS4231 || OPTi93X */ + + pdev = chip->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + mpu_port = -1; + chip->devmpu = NULL; + return pid->card_device; + } + + if (mpu_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], mpu_port, 2); + if (mpu_irq != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], mpu_irq, 1); + + if (pdev->activate(pdev) < 0) { + snd_printk("MPU-401 isapnp configure failure\n"); + mpu_port = -1; + chip->devmpu = NULL; + } else { + mpu_port = pdev->resource[0].start; + mpu_irq = pdev->irq_resource[0].start; + } + return pid->card_device; +} + +static void snd_card_opti9xx_deactivate(opti9xx_t *chip) +{ + if (chip->dev) + chip->dev->deactivate(chip->dev); + if (chip->devmpu) + chip->devmpu->deactivate(chip->devmpu); +} +#endif /* __ISAPNP__ */ + +#if 0 +static int __init snd_card_opti9xx_resources(struct snd_card_opti9xx *chip, + snd_card_t *card) +{ + int error, i, pnp = 0; + +#ifdef __ISAPNP__ + pnp = chip->dev != NULL; +#endif /* __ISAPNP__ */ + +#ifndef OPTi93X + if (chip->chip->hardware == OPTi9XX_HW_82C928) + mpu_port = -1; +#endif /* OPTi93X */ + error = 0; + if (!pnp && (mpu_port == SNDRV_DEFAULT_PORT1)) { + for (i = 0; possible_mpu_ports[i] != -1; i++) + if (!snd_register_ioport(card, possible_mpu_ports[i], 2, + DRIVER_NAME" - MPU-401", NULL)) { + mpu_port = possible_mpu_ports[i]; + break; + } + if (mpu_port == SNDRV_DEFAULT_PORT1) + error = -EBUSY; + } + else + error = (mpu_port == -1) ? -ENODEV : + snd_register_ioport(card, mpu_port, 2, + DRIVER_NAME" - MPU-401", NULL); + if (error) + chip->chip->mpu_port = -1; + else if (pnp && (irq == mpu_irq)) + chip->chip->mpu_irq = mpu_irq; + else if (!snd_register_interrupt(card, + DRIVER_NAME" - MPU-401", + mpu_irq, SNDRV_IRQ_TYPE_ISA, + snd_card_opti9xx_mpu_interrupt, chip, + pnp ? no_alternatives : possible_mpu_irqs, + &chip->mpuirqptr)) { + chip->chip->mpu_port = mpu_port; + chip->chip->mpu_irq = chip->mpuirqptr->irq; + } + else + chip->chip->mpu_port = -1; + + if (!pnp && (port == SNDRV_DEFAULT_PORT1)) { + for (i = 0; possible_ports[i] != -1; i++) + if (!snd_register_ioport(card, possible_ports[i], 8, + DRIVER_NAME" - WSS", NULL)) { + port = possible_ports[i]; + break; + } + if (port == SNDRV_DEFAULT_PORT1) + return -EBUSY; + } + else if ((error = snd_register_ioport(card, port, 8, + DRIVER_NAME" - WSS", NULL)) < 0) + return error; + chip->chip->wss_base = port; + if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS", + irq, SNDRV_IRQ_TYPE_ISA, + snd_card_opti9xx_interrupt, chip, + pnp ? no_alternatives : possible_irqs, + &chip->irqptr)) < 0) + return error; + chip->chip->irq = chip->irqptr->irq; + if ((error = snd_register_dma_channel(card, +#if defined(CS4231) || defined(OPTi93X) + DRIVER_NAME" - WSS playback", +#else + DRIVER_NAME" - WSS", +#endif /* CS4231 || OPTi93X */ + dma1, SNDRV_DMA_TYPE_ISA, dma1_size, + pnp ? no_alternatives : possible_dma1s, + &chip->dma1ptr)) < 0) + return error; + chip->chip->dma1 = chip->dma1ptr->dma; +#if defined(CS4231) || defined(OPTi93X) + if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture", + dma2, SNDRV_DMA_TYPE_ISA, dma2_size, + pnp ? no_alternatives : + possible_dma2s[chip->dma1ptr->dma], + &chip->dma2ptr)) < 0) + return error; + chip->chip->dma2 = chip->dma2ptr->dma; +#endif /* CS4231 || OPTi93X */ + + if (snd_register_ioport(card, + pnp ? fm_port : fm_port = 0x388, 4, + DRIVER_NAME" - OPL", NULL) < 0) + fm_port = -1; + chip->chip->fm_port = fm_port; + + return 0; +} +#endif + +static void snd_card_opti9xx_free(snd_card_t *card) +{ + opti9xx_t *chip = (opti9xx_t *)card->private_data; + + if (chip) { +#ifdef __ISAPNP__ + snd_card_opti9xx_deactivate(chip); +#endif /* __ISAPNP__ */ + if (chip->res_mc_base) { + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + } + } +} + +static int __init snd_card_opti9xx_probe(void) +{ + static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; + static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; +#ifdef OPTi93X + static int possible_irqs[] = {5, 9, 10, 11, 7, -1}; +#else + static int possible_irqs[] = {9, 10, 11, 7, -1}; +#endif /* OPTi93X */ + static int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dma1s[] = {3, 1, 0, -1}; +#if defined(CS4231) || defined(OPTi93X) + static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; +#endif /* CS4231 || OPTi93X */ + int error; + opti9xx_t *chip; +#if defined(OPTi93X) + opti93x_t *codec; +#elif defined(CS4231) + cs4231_t *codec; + snd_timer_t *timer; +#else + ad1848_t *codec; +#endif + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_hwdep_t *synth; +#ifdef __ISAPNP__ + int hw; +#endif /* __ISAPNP__ */ + + if (!(card = snd_card_new(index, id, THIS_MODULE, + sizeof(opti9xx_t)))) + return -ENOMEM; + card->private_free = snd_card_opti9xx_free; + chip = (opti9xx_t *)card->private_data; + +#ifdef __ISAPNP__ + if (isapnp && (hw = snd_card_opti9xx_isapnp(chip)) > 0) { + switch (hw) { + case ISAPNP_DEVICE(0x0924): + hw = OPTi9XX_HW_82C924; + break; + case ISAPNP_DEVICE(0x0925): + hw = OPTi9XX_HW_82C925; + break; + case ISAPNP_DEVICE(0x0931): + hw = OPTi9XX_HW_82C931; + break; + default: + snd_card_free(card); + return -ENODEV; + } + + if ((error = snd_opti9xx_init(chip, hw))) { + snd_card_free(card); + return error; + } + if (hw <= OPTi9XX_HW_82C930) + chip->mc_base -= 0x80; + } else { +#endif /* __ISAPNP__ */ + if ((error = snd_card_opti9xx_detect(card, chip)) < 0) { + snd_card_free(card); + return error; + } +#ifdef __ISAPNP__ + } +#endif /* __ISAPNP__ */ + + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { + snd_card_free(card); + return -ENOMEM; + } + + chip->wss_base = port; + chip->fm_port = fm_port; + chip->mpu_port = mpu_port; + chip->irq = irq; + chip->mpu_irq = mpu_irq; + chip->dma1 = dma1; +#if defined(CS4231) || defined(OPTi93X) + chip->dma2 = dma2; +#endif + +#ifdef __ISAPNP__ + if (!isapnp) { +#endif + if (chip->wss_base == SNDRV_AUTO_PORT) { + if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free WSS port\n"); + return -EBUSY; + } + } + if (chip->mpu_port == SNDRV_AUTO_PORT) { + if ((chip->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free MPU401 port\n"); + return -EBUSY; + } + } + if (chip->irq == SNDRV_AUTO_IRQ) { + if ((chip->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (chip->mpu_irq == SNDRV_AUTO_IRQ) { + if ((chip->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free MPU401 IRQ\n"); + return -EBUSY; + } + } + if (chip->dma1 == SNDRV_AUTO_DMA) { + if ((chip->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } +#if defined(CS4231) || defined(OPTi93X) + if (chip->dma2 == SNDRV_AUTO_DMA) { + if ((chip->dma2 = snd_legacy_find_free_dma(possible_dma2s[chip->dma1 % 4])) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } +#endif + +#ifdef __ISAPNP__ + } +#endif + + if ((error = snd_opti9xx_configure(chip))) { + snd_card_free(card); + return error; + } + +#if defined(OPTi93X) + if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) { + snd_card_free(card); + return error; + } + if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opti93x_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } +#elif defined(CS4231) + if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1, + chip->irq, chip->dma1, chip->dma2, + CS4231_HW_DETECT, + 0, + &codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) { + snd_card_free(card); + return error; + } +#else + if ((error = snd_ad1848_create(card, chip->wss_base + 4, + chip->irq, chip->dma1, + AD1848_HW_DETECT, &codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_ad1848_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } +#endif + + if (chip->mpu_port <= 0) + rmidi = NULL; + else + if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT, + &rmidi))) + snd_printk("no MPU-401 device at 0x%lx?\n", chip->mpu_port); + + if (chip->fm_port > 0) { + opl3_t *opl3; + if (snd_opl3_create(card, + chip->fm_port, + chip->fm_port + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + chip->fm_port, chip->fm_port + 4 - 1); + } else { + if ((error = snd_opl3_timer_new(opl3, +#ifdef CS4231 + 1, 2)) < 0) { +#else + 0, 1)) < 0) { +#endif /* CS4231 */ + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, chip->name); + sprintf(card->shortname, "OPTi %s", card->driver); +#if defined(CS4231) || defined(OPTi93X) + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, pcm->name, chip->wss_base + 4, + chip->irq, chip->dma1, chip->dma2); +#else + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, pcm->name, chip->wss_base + 4, + chip->irq, chip->dma1); +#endif /* CS4231 || OPTi93X */ + if ((error = snd_card_register(card))) { + snd_card_free(card); + return error; + } + snd_opti9xx_card = card; + return 0; +} + +static int __init alsa_card_opti9xx_init(void) +{ + int error; + + if ((error = snd_card_opti9xx_probe())) { +#ifdef MODULE +#ifdef OPTi93X + printk(KERN_ERR "no OPTi 82C93x soundcard found\n"); +#else + printk(KERN_ERR "no OPTi 82C92x soundcard found\n"); +#endif /* OPTi93X */ +#endif + return error; + } + return 0; +} + +static void __exit alsa_card_opti9xx_exit(void) +{ + if (snd_opti9xx_card) + snd_card_free(snd_opti9xx_card); +} + +module_init(alsa_card_opti9xx_init) +module_exit(alsa_card_opti9xx_exit) + +#ifndef MODULE + +/* format is: snd-opti9xx=enable,index,id,isapnp, + port,mpu_port,fm_port, + irq,mpu_irq, + dma1,[dma2] */ + +static int __init alsa_card_opti9xx_setup(char *str) +{ + int __attribute__ ((__unused__)) enable = 1; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&index) == 2 && + get_id(&str,&id) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port) == 2 && + get_option(&str,(int *)&mpu_port) == 2 && + get_option(&str,(int *)&fm_port) == 2 && + get_option(&str,&irq) == 2 && + get_option(&str,&mpu_irq) == 2 && + get_option(&str,&dma1) == 2 +#if defined(CS4231) || defined(OPTi93X) + && + get_option(&str,&dma2) == 2 +#endif + ); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp = pnp; +#endif + return 1; +} + +#if defined(OPTi93X) +__setup("snd-opti93x=", alsa_card_opti9xx_setup); +#elif defined(CS4231) +__setup("snd-opti92x-cs4231=", alsa_card_opti9xx_setup); +#else +__setup("snd-opti92x-ad1848=", alsa_card_opti9xx_setup); +#endif + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/opti9xx/opti92x-cs4231.c linux/sound/isa/opti9xx/opti92x-cs4231.c --- linux-2.4.21-rc1.orig/sound/isa/opti9xx/opti92x-cs4231.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/opti9xx/opti92x-cs4231.c 2001-12-18 06:57:41.000000000 -0700 @@ -0,0 +1,2 @@ +#define CS4231 +#include "opti92x-ad1848.c" diff -urN linux-2.4.21-rc1.orig/sound/isa/opti9xx/opti93x.c linux/sound/isa/opti9xx/opti93x.c --- linux-2.4.21-rc1.orig/sound/isa/opti9xx/opti93x.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/opti9xx/opti93x.c 2001-12-18 06:57:41.000000000 -0700 @@ -0,0 +1,3 @@ +#define OPTi93X +#include "opti92x-ad1848.c" + diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/Makefile linux/sound/isa/sb/Makefile --- linux-2.4.21-rc1.orig/sound/isa/sb/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,69 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _sb.o + +list-multi := snd-sb-common.o snd-sb8-dsp.o snd-sb16-dsp.o snd-sb16-csp.o \ + snd-sb8.o snd-sb16.o snd-sbawe.o snd-emu8000-synth.o snd-es968.o + +export-objs := emu8000.o sb_common.o sb8_main.o sb16_main.o sb16_csp.o + +snd-sb-common-objs := sb_common.o sb_mixer.o +snd-sb8-dsp-objs := sb8_main.o sb8_midi.o +snd-sb16-dsp-objs := sb16_main.o +snd-sb16-csp-objs := sb16_csp.o +snd-sb8-objs := sb8.o +snd-sb16-objs := sb16.o +snd-sbawe-objs := sbawe.o emu8000.o +snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o +snd-es968-objs := es968.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o + obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o +endif +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o +endif + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-sb-common.o: $(snd-sb-common-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb-common-objs) + +snd-sb8-dsp.o: $(snd-sb8-dsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-dsp-objs) + +snd-sb16-dsp.o: $(snd-sb16-dsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-dsp-objs) + +snd-sb16-csp.o: $(snd-sb16-csp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-csp-objs) + +snd-sb8.o: $(snd-sb8-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-objs) + +snd-sb16.o: $(snd-sb16-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-objs) + +snd-sbawe.o: $(snd-sbawe-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sbawe-objs) + +snd-emu8000-synth.o: $(snd-emu8000-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu8000-synth-objs) + +snd-es968.o: $(snd-es968-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es968-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/emu8000.c linux/sound/isa/sb/emu8000.c --- linux-2.4.21-rc1.orig/sound/isa/sb/emu8000.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/emu8000.c 2003-01-31 08:19:58.000000000 -0700 @@ -0,0 +1,1169 @@ +/* + * Copyright (c) by Jaroslav Kysela + * and (c) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * Routines for control of EMU8000 chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * emu8000 register controls + */ + +/* + * The following routines read and write registers on the emu8000. They + * should always be called via the EMU8000*READ/WRITE macros and never + * directly. The macros handle the port number and command word. + */ +/* Write a word */ +void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + outw((unsigned short)val, port); /* Send data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +/* Read a word */ +unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg) +{ + unsigned short res; + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + res = inw(port); /* Read data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); + return res; +} + +/* Write a double word */ +void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + outw((unsigned short)val, port); /* Send low word of data */ + outw((unsigned short)(val>>16), port+2); /* Send high word of data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +/* Read a double word */ +unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg) +{ + unsigned short low; + unsigned int res; + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + low = inw(port); /* Read low word of data */ + res = low + (inw(port+2) << 16); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return res; +} + +/* + * Set up / close a channel to be used for DMA. + */ +/*exported*/ void +snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode) +{ + unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0; + mode &= EMU8000_RAM_MODE_MASK; + if (mode == EMU8000_RAM_CLOSE) { + EMU8000_CCCA_WRITE(emu, ch, 0); + EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); + return; + } + EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); + EMU8000_VTFT_WRITE(emu, ch, 0); + EMU8000_CVCF_WRITE(emu, ch, 0); + EMU8000_PTRX_WRITE(emu, ch, 0x40000000); + EMU8000_CPF_WRITE(emu, ch, 0x40000000); + EMU8000_PSST_WRITE(emu, ch, 0); + EMU8000_CSL_WRITE(emu, ch, 0); + if (mode == EMU8000_RAM_WRITE) /* DMA write */ + EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit); + else /* DMA read */ + EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit); +} + +/* + */ +static void __init +snd_emu8000_read_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + */ +static void __init +snd_emu8000_write_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + * detect a card at the given port + */ +static int __init +snd_emu8000_detect(emu8000_t *emu) +{ + /* Initialise */ + EMU8000_HWCF1_WRITE(emu, 0x0059); + EMU8000_HWCF2_WRITE(emu, 0x0020); + EMU8000_HWCF3_WRITE(emu, 0x0000); + /* Check for a recognisable emu8000 */ + /* + if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) + return -ENODEV; + */ + if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) + return -ENODEV; + if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) + return -ENODEV; + + snd_printdd("EMU8000 [0x%lx]: Synth chip found\n", + emu->port1); + return 0; +} + + +/* + * intiailize audio channels + */ +static void __init +init_audio(emu8000_t *emu) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < EMU8000_CHANNELS; ch++) + EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); + + /* reset all other parameters to zero */ + for (ch = 0; ch < EMU8000_CHANNELS; ch++) { + EMU8000_ENVVOL_WRITE(emu, ch, 0); + EMU8000_ENVVAL_WRITE(emu, ch, 0); + EMU8000_DCYSUS_WRITE(emu, ch, 0); + EMU8000_ATKHLDV_WRITE(emu, ch, 0); + EMU8000_LFO1VAL_WRITE(emu, ch, 0); + EMU8000_ATKHLD_WRITE(emu, ch, 0); + EMU8000_LFO2VAL_WRITE(emu, ch, 0); + EMU8000_IP_WRITE(emu, ch, 0); + EMU8000_IFATN_WRITE(emu, ch, 0); + EMU8000_PEFE_WRITE(emu, ch, 0); + EMU8000_FMMOD_WRITE(emu, ch, 0); + EMU8000_TREMFRQ_WRITE(emu, ch, 0); + EMU8000_FM2FRQ2_WRITE(emu, ch, 0); + EMU8000_PTRX_WRITE(emu, ch, 0); + EMU8000_VTFT_WRITE(emu, ch, 0); + EMU8000_PSST_WRITE(emu, ch, 0); + EMU8000_CSL_WRITE(emu, ch, 0); + EMU8000_CCCA_WRITE(emu, ch, 0); + } + + for (ch = 0; ch < EMU8000_CHANNELS; ch++) { + EMU8000_CPF_WRITE(emu, ch, 0); + EMU8000_CVCF_WRITE(emu, ch, 0); + } +} + + +/* + * initialize DMA address + */ +static void __init +init_dma(emu8000_t *emu) +{ + EMU8000_SMALR_WRITE(emu, 0); + EMU8000_SMARR_WRITE(emu, 0); + EMU8000_SMALW_WRITE(emu, 0); + EMU8000_SMARW_WRITE(emu, 0); +} + +/* + * initialization arrays; from ADIP + */ +static unsigned short init1[128] /*__devinitdata*/ = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] /*__devinitdata*/ = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] /*__devinitdata*/ = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] /*__devinitdata*/ = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +/* send an initialization array + * Taken from the oss driver, not obvious from the doc how this + * is meant to work + */ +static void __init +send_array(emu8000_t *emu, unsigned short *data, int size) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < size; i++, p++) + EMU8000_INIT1_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT2_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT3_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT4_WRITE(emu, i, *p); +} + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + + +/* + * Send initialization arrays to start up, this just follows the + * initialisation sequence in the adip. + */ +static void __init +init_arrays(emu8000_t *emu) +{ + send_array(emu, init1, NELEM(init1)/4); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */ + send_array(emu, init2, NELEM(init2)/4); + send_array(emu, init3, NELEM(init3)/4); + + EMU8000_HWCF4_WRITE(emu, 0); + EMU8000_HWCF5_WRITE(emu, 0x83); + EMU8000_HWCF6_WRITE(emu, 0x8000); + + send_array(emu, init4, NELEM(init4)/4); +} + + +#define UNIQUE_ID1 0xa5b9 +#define UNIQUE_ID2 0x9d53 + +/* + * Size the onboard memory. + * This is written so as not to need arbitary delays after the write. It + * seems that the only way to do this is to use the one channel and keep + * reallocating between read and write. + */ +static void __init +size_dram(emu8000_t *emu) +{ + int i, size; + + if (emu->dram_checked) + return; + + size = 0; + + /* write out a magic number */ + snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE); + snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ); + EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_WRITE(emu, UNIQUE_ID1); + snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */ + + while (size < EMU8000_MAX_DRAM) { + + size += 512 * 1024; /* increment 512kbytes */ + + /* Write a unique data on the test address. + * if the address is out of range, the data is written on + * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is + * changed by this data. + */ + /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/ + EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); + EMU8000_SMLD_WRITE(emu, UNIQUE_ID2); + snd_emu8000_write_wait(emu); + + /* + * read the data on the just written DRAM address + * if not the same then we have reached the end of ram. + */ + /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); + /*snd_emu8000_read_wait(emu);*/ + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2) + break; /* we must have wrapped around */ + + snd_emu8000_read_wait(emu); + + /* + * If it is the same it could be that the address just + * wraps back to the beginning; so check to see if the + * initial value has been overwritten. + */ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) + break; /* we must have wrapped around */ + snd_emu8000_read_wait(emu); + } + + /* wait until FULL bit in SMAxW register is false */ + for (i = 0; i < 10000; i++) { + if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } + snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE); + snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); + + snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n", + emu->port1, size/1024); + + emu->mem_size = size; + emu->dram_checked = 1; +} + + +/* + * Initiailise the FM section. You have to do this to use sample RAM + * and therefore lose 2 voices. + */ +/*exported*/ void +snd_emu8000_init_fm(emu8000_t *emu) +{ + unsigned long flags; + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + EMU8000_DCYSUSV_WRITE(emu, 30, 0x80); + EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */ + EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24)); + EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8)); + EMU8000_CPF_WRITE(emu, 30, 0); + EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + EMU8000_DCYSUSV_WRITE(emu, 31, 0x80); + EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */ + EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24)); + EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8)); + EMU8000_CPF_WRITE(emu, 31, 0x8000); + EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3); + + snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0); + + spin_lock_irqsave(&emu->reg_lock, flags); + while (!(inw(EMU8000_PTR(emu)) & 0x1000)) + ; + while ((inw(EMU8000_PTR(emu)) & 0x1000)) + ; + spin_unlock_irqrestore(&emu->reg_lock, flags); + snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828); + /* this is really odd part.. */ + outb(0x3C, EMU8000_PTR(emu)); + outb(0, EMU8000_DATA1(emu)); + + /* skew volume & cutoff */ + EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF); + EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF); +} + + +/* + * The main initialization routine. + */ +static void __init +snd_emu8000_init_hw(emu8000_t *emu) +{ + int i; + + emu->last_reg = 0xffff; /* reset the last register index */ + + /* initialize hardware configuration */ + EMU8000_HWCF1_WRITE(emu, 0x0059); + EMU8000_HWCF2_WRITE(emu, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + EMU8000_HWCF3_WRITE(emu, 0); + + /* initialize audio channels */ + init_audio(emu); + + /* initialize DMA */ + init_dma(emu); + + /* initialize init arrays */ + init_arrays(emu); + + /* + * Initialize the FM section of the AWE32, this is needed + * for DRAM refresh as well + */ + snd_emu8000_init_fm(emu); + + /* terminate all voices */ + for (i = 0; i < EMU8000_DRAM_VOICES; i++) + EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F); + + /* check DRAM memory size */ + size_dram(emu); + + /* enable audio */ + EMU8000_HWCF3_WRITE(emu, 0x4); + + /* set equzlier, chorus and reverb modes */ + snd_emu8000_update_equalizer(emu); + snd_emu8000_update_chorus_mode(emu); + snd_emu8000_update_reverb_mode(emu); +} + + +/*---------------------------------------------------------------- + * Bass/Treble Equalizer + *----------------------------------------------------------------*/ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC384, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +/*exported*/ void +snd_emu8000_update_equalizer(emu8000_t *emu) +{ + unsigned short w; + int bass = emu->bass_level; + int treble = emu->treble_level; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]); + EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]); + EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]); + EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]); + EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]); + EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]); + EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]); + EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]); + EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]); + EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262)); + EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362)); +} + + +/*---------------------------------------------------------------- + * Chorus mode control + *----------------------------------------------------------------*/ + +/* + * chorus mode parameters + */ +#define SNDRV_EMU8000_CHORUS_1 0 +#define SNDRV_EMU8000_CHORUS_2 1 +#define SNDRV_EMU8000_CHORUS_3 2 +#define SNDRV_EMU8000_CHORUS_4 3 +#define SNDRV_EMU8000_CHORUS_FEEDBACK 4 +#define SNDRV_EMU8000_CHORUS_FLANGER 5 +#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6 +#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7 +#define SNDRV_EMU8000_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define SNDRV_EMU8000_CHORUS_NUMBERS 32 + +typedef struct soundfont_chorus_fx_t { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} soundfont_chorus_fx_t; + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; +static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */ +}; + +/*exported*/ int +snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len) +{ + soundfont_chorus_fx_t rec; + if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) { + snd_printk(KERN_WARNING "illegal chorus mode %d for uploading\n", mode); + return -EINVAL; + } + if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) + return -EFAULT; + chorus_parm[mode] = rec; + chorus_defined[mode] = 1; + return 0; +} + +/*exported*/ void +snd_emu8000_update_chorus_mode(emu8000_t *emu) +{ + int effect = emu->chorus_mode; + if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS || + (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback); + EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset); + EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth); + EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay); + EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq); + EMU8000_HWCF6_WRITE(emu, 0x8000); + EMU8000_HWCF7_WRITE(emu, 0x0000); +} + +/*---------------------------------------------------------------- + * Reverb mode control + *----------------------------------------------------------------*/ + +/* + * reverb mode parameters + */ +#define SNDRV_EMU8000_REVERB_ROOM1 0 +#define SNDRV_EMU8000_REVERB_ROOM2 1 +#define SNDRV_EMU8000_REVERB_ROOM3 2 +#define SNDRV_EMU8000_REVERB_HALL1 3 +#define SNDRV_EMU8000_REVERB_HALL2 4 +#define SNDRV_EMU8000_REVERB_PLATE 5 +#define SNDRV_EMU8000_REVERB_DELAY 6 +#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7 +#define SNDRV_EMU8000_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define SNDRV_EMU8000_REVERB_NUMBERS 32 + +typedef struct soundfont_reverb_fx_t { + unsigned short parms[28]; +} soundfont_reverb_fx_t; + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; +static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +enum { DATA1, DATA2 }; +#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1 +#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2 +#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1 +#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2 + +static struct reverb_cmd_pair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +/*exported*/ int +snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len) +{ + soundfont_reverb_fx_t rec; + + if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) { + snd_printk(KERN_WARNING "illegal reverb mode %d for uploading\n", mode); + return -EINVAL; + } + if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) + return -EFAULT; + reverb_parm[mode] = rec; + reverb_defined[mode] = 1; + return 0; +} + +/*exported*/ void +snd_emu8000_update_reverb_mode(emu8000_t *emu) +{ + int effect = emu->reverb_mode; + int i; + + if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS || + (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) { + int port; + if (reverb_cmds[i].port == DATA1) + port = EMU8000_DATA1(emu); + else + port = EMU8000_DATA2(emu); + snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]); + } +} + + +/*---------------------------------------------------------------- + * mixer interface + *----------------------------------------------------------------*/ + +#define chip_t emu8000_t + +/* + * bass/treble + */ +static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 11; + return 0; +} + +static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level; + return 0; +} + +static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + val1 = ucontrol->value.integer.value[0] % 12; + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + change = val1 != emu->treble_level; + emu->treble_level = val1; + } else { + change = val1 != emu->bass_level; + emu->bass_level = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + snd_emu8000_update_equalizer(emu); + return change; +} + +static snd_kcontrol_new_t mixer_bass_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Synth Tone Control - Bass", + .info = mixer_bass_treble_info, + .get = mixer_bass_treble_get, + .put = mixer_bass_treble_put, + .private_value = 0, +}; + +static snd_kcontrol_new_t mixer_treble_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Synth Tone Control - Treble", + .info = mixer_bass_treble_info, + .get = mixer_bass_treble_get, + .put = mixer_bass_treble_put, + .private_value = 1, +}; + +/* + * chorus/reverb mode + */ +static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1); + return 0; +} + +static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode; + return 0; +} + +static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS; + change = val1 != emu->chorus_mode; + emu->chorus_mode = val1; + } else { + val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS; + change = val1 != emu->reverb_mode; + emu->reverb_mode = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + if (change) { + if (kcontrol->private_value) + snd_emu8000_update_chorus_mode(emu); + else + snd_emu8000_update_reverb_mode(emu); + } + return change; +} + +static snd_kcontrol_new_t mixer_chorus_mode_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Chorus Mode", + .info = mixer_chorus_reverb_info, + .get = mixer_chorus_reverb_get, + .put = mixer_chorus_reverb_put, + .private_value = 1, +}; + +static snd_kcontrol_new_t mixer_reverb_mode_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Reverb Mode", + .info = mixer_chorus_reverb_info, + .get = mixer_chorus_reverb_get, + .put = mixer_chorus_reverb_put, + .private_value = 0, +}; + +/* + * FM OPL3 chorus/reverb depth + */ +static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth; + return 0; +} + +static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + val1 = ucontrol->value.integer.value[0] % 256; + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + change = val1 != emu->fm_chorus_depth; + emu->fm_chorus_depth = val1; + } else { + change = val1 != emu->fm_reverb_depth; + emu->fm_reverb_depth = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + if (change) + snd_emu8000_init_fm(emu); + return change; +} + +static snd_kcontrol_new_t mixer_fm_chorus_depth_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "FM Chorus Depth", + .info = mixer_fm_depth_info, + .get = mixer_fm_depth_get, + .put = mixer_fm_depth_put, + .private_value = 1, +}; + +static snd_kcontrol_new_t mixer_fm_reverb_depth_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "FM Reverb Depth", + .info = mixer_fm_depth_info, + .get = mixer_fm_depth_get, + .put = mixer_fm_depth_put, + .private_value = 0, +}; + + +static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = { + &mixer_bass_control, + &mixer_treble_control, + &mixer_chorus_mode_control, + &mixer_reverb_mode_control, + &mixer_fm_chorus_depth_control, + &mixer_fm_reverb_depth_control, +}; + +/* + * create and attach mixer elements for WaveTable treble/bass controls + */ +static int __init +snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu) +{ + int i, err = 0; + + snd_assert(emu != NULL && card != NULL, return -EINVAL); + + spin_lock_init(&emu->control_lock); + + memset(emu->controls, 0, sizeof(emu->controls)); + for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { + if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0) + goto __error; + } + return 0; + +__error: + for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { + if (emu->controls[i]) + snd_ctl_remove(card, emu->controls[i]); + } + return err; +} + + +/* + * free resources + */ +static int snd_emu8000_free(emu8000_t *hw) +{ + if (hw->res_port1) { + release_resource(hw->res_port1); + kfree_nocheck(hw->res_port1); + } + if (hw->res_port2) { + release_resource(hw->res_port2); + kfree_nocheck(hw->res_port2); + } + if (hw->res_port3) { + release_resource(hw->res_port3); + kfree_nocheck(hw->res_port3); + } + snd_magic_kfree(hw); + return 0; +} + +/* + */ +static int snd_emu8000_dev_free(snd_device_t *device) +{ + emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO); + return snd_emu8000_free(hw); +} + +/* + * initialize and register emu8000 synth device. + */ +int __init +snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret) +{ + snd_seq_device_t *awe; + emu8000_t *hw; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_emu8000_dev_free, + }; + + if (awe_ret) + *awe_ret = NULL; + + if (seq_ports <= 0) + return 0; + + hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL); + if (hw == NULL) + return -ENOMEM; + spin_lock_init(&hw->reg_lock); + hw->index = index; + hw->port1 = port; + hw->port2 = port + 0x400; + hw->port3 = port + 0x800; + if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) || + !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) || + !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) { + snd_emu8000_free(hw); + return -EBUSY; + } + hw->mem_size = 0; + hw->card = card; + hw->seq_ports = seq_ports; + hw->bass_level = 5; + hw->treble_level = 9; + hw->chorus_mode = 2; + hw->reverb_mode = 4; + hw->fm_chorus_depth = 0; + hw->fm_reverb_depth = 0; + + if (snd_emu8000_detect(hw) < 0) { + snd_emu8000_free(hw); + return -ENODEV; + } + + snd_emu8000_init_hw(hw); + if ((err = snd_emu8000_create_mixer(card, hw)) < 0) { + snd_emu8000_free(hw); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hw, &ops)) < 0) { + snd_emu8000_free(hw); + return err; + } +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000, + sizeof(emu8000_t*), &awe) >= 0) { + strcpy(awe->name, "EMU-8000"); + *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw; + } +#endif + if (awe_ret) + *awe_ret = awe; + + return 0; +} + + +/* + * exported stuff + */ + +EXPORT_SYMBOL(snd_emu8000_poke); +EXPORT_SYMBOL(snd_emu8000_peek); +EXPORT_SYMBOL(snd_emu8000_poke_dw); +EXPORT_SYMBOL(snd_emu8000_peek_dw); +EXPORT_SYMBOL(snd_emu8000_dma_chan); +EXPORT_SYMBOL(snd_emu8000_init_fm); +EXPORT_SYMBOL(snd_emu8000_load_chorus_fx); +EXPORT_SYMBOL(snd_emu8000_load_reverb_fx); +EXPORT_SYMBOL(snd_emu8000_update_chorus_mode); +EXPORT_SYMBOL(snd_emu8000_update_reverb_mode); +EXPORT_SYMBOL(snd_emu8000_update_equalizer); diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_callback.c linux/sound/isa/sb/emu8000_callback.c --- linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_callback.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/emu8000_callback.c 2002-08-13 10:13:38.000000000 -0600 @@ -0,0 +1,539 @@ +/* + * synth callback routines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu8000_local.h" +#include + +/* + * prototypes + */ +static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); +static int start_voice(snd_emux_voice_t *vp); +static void trigger_voice(snd_emux_voice_t *vp); +static void release_voice(snd_emux_voice_t *vp); +static void update_voice(snd_emux_voice_t *vp, int update); +static void reset_voice(snd_emux_t *emu, int ch); +static void terminate_voice(snd_emux_voice_t *vp); +static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +#ifdef CONFIG_SND_SEQUENCER_OSS +static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2); +#endif +static int load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len); + +static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp); +static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch); + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + + +/* + * set up operators + */ +static snd_emux_operators_t emu8000_ops = { + .owner = THIS_MODULE, + .get_voice = get_voice, + .prepare = start_voice, + .trigger = trigger_voice, + .release = release_voice, + .update = update_voice, + .terminate = terminate_voice, + .reset = reset_voice, + .sample_new = snd_emu8000_sample_new, + .sample_free = snd_emu8000_sample_free, + .sample_reset = snd_emu8000_sample_reset, + .load_fx = load_fx, + .sysex = sysex, +#ifdef CONFIG_SND_SEQUENCER_OSS + .oss_ioctl = oss_ioctl, +#endif +}; + +void +snd_emu8000_ops_setup(emu8000_t *hw) +{ + hw->emu->ops = emu8000_ops; +} + + + +/* + * Terminate a voice + */ +static void +release_voice(snd_emux_voice_t *vp) +{ + int dcysusv; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; + EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; + EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv); +} + + +/* + */ +static void +terminate_voice(snd_emux_voice_t *vp) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); +} + + +/* + */ +static void +update_voice(snd_emux_voice_t *vp, int update) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + if (update & SNDRV_EMUX_UPDATE_VOLUME) + set_volume(hw, vp); + if (update & SNDRV_EMUX_UPDATE_PITCH) + set_pitch(hw, vp); + if ((update & SNDRV_EMUX_UPDATE_PAN) && + vp->port->ctrls[EMUX_MD_REALTIME_PAN]) + set_pan(hw, vp); + if (update & SNDRV_EMUX_UPDATE_FMMOD) + set_fmmod(hw, vp); + if (update & SNDRV_EMUX_UPDATE_TREMFREQ) + set_tremfreq(hw, vp); + if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) + set_fm2frq2(hw, vp); + if (update & SNDRV_EMUX_UPDATE_Q) + set_filterQ(hw, vp); +} + + +/* + * Find a channel (voice) within the EMU that is not in use or at least + * less in use than other channels. Always returns a valid pointer + * no matter what. If there is a real shortage of voices then one + * will be cut. Such is life. + * + * The channel index (vp->ch) must be initialized in this routine. + * In Emu8k, it is identical with the array index. + */ +static snd_emux_voice_t * +get_voice(snd_emux_t *emu, snd_emux_port_t *port) +{ + int i; + snd_emux_voice_t *vp; + emu8000_t *hw; + + /* what we are looking for, in order of preference */ + enum { + OFF=0, RELEASED, PLAYING, END + }; + + /* Keeps track of what we are finding */ + struct best { + unsigned int time; + int voice; + } best[END]; + struct best *bp; + + hw = snd_magic_cast(emu8000_t, emu->hw, return NULL); + + for (i = 0; i < END; i++) { + best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* + * Go through them all and get a best one to use. + */ + for (i = 0; i < emu->max_voices; i++) { + int state, val; + + vp = &emu->voices[i]; + state = vp->state; + + if (state == SNDRV_EMUX_ST_OFF) + bp = best + OFF; + else if (state == SNDRV_EMUX_ST_RELEASED || + state == SNDRV_EMUX_ST_PENDING) { + bp = best + RELEASED; + val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff; + if (! val) + bp = best + OFF; + } + else if (state & SNDRV_EMUX_ST_ON) + bp = best + PLAYING; + else + continue; + + /* check if sample is finished playing (non-looping only) */ + if (state != SNDRV_EMUX_ST_OFF && + (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { + val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; + if (val >= vp->reg.loopstart) + bp = best + OFF; + } + + if (vp->time < bp->time) { + bp->time = vp->time; + bp->voice = i; + } + } + + for (i = 0; i < END; i++) { + if (best[i].voice >= 0) { + vp = &emu->voices[best[i].voice]; + vp->ch = best[i].voice; + return vp; + } + } + + /* not found */ + return NULL; +} + +/* + */ +static int +start_voice(snd_emux_voice_t *vp) +{ + unsigned int temp; + int ch; + int addr; + snd_midi_channel_t *chan; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return -EINVAL); + ch = vp->ch; + chan = vp->chan; + + /* channel to be silent and idle */ + EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); + EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); + EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); + EMU8000_PTRX_WRITE(hw, ch, 0); + EMU8000_CPF_WRITE(hw, ch, 0); + + /* set pitch offset */ + set_pitch(hw, vp); + + /* set envelope parameters */ + EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay); + EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld); + EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus); + EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay); + EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + + /* cutoff and volume */ + set_volume(hw, vp); + + /* modulation envelope heights */ + EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe); + + /* lfo1/2 delay */ + EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay); + EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay); + + /* lfo1 pitch & cutoff shift */ + set_fmmod(hw, vp); + /* lfo1 volume & freq */ + set_tremfreq(hw, vp); + /* lfo2 pitch & freq */ + set_fm2frq2(hw, vp); + /* pan & loop start */ + set_pan(hw, vp); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->reg.loopend - 1; + temp = vp->reg.parm.chorus; + temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp <<24) | (unsigned int)addr; + EMU8000_CSL_WRITE(hw, ch, temp); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->reg.start - 1; + temp = vp->reg.parm.filterQ; + temp = (temp<<28) | (unsigned int)addr; + EMU8000_CCCA_WRITE(hw, ch, temp); + + /* clear unknown registers */ + EMU8000_00A0_WRITE(hw, ch, 0); + EMU8000_0080_WRITE(hw, ch, 0); + + /* reset volume */ + temp = vp->vtarget << 16; + EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget); + EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00); + + return 0; +} + +/* + * Start envelope + */ +static void +trigger_voice(snd_emux_voice_t *vp) +{ + int ch = vp->ch; + unsigned int temp; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + + /* set reverb and pitch target */ + temp = vp->reg.parm.reverb; + temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux; + EMU8000_PTRX_WRITE(hw, ch, temp); + EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16); + EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus); +} + +/* + * reset voice parameters + */ +static void +reset_voice(snd_emux_t *emu, int ch) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return); + EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); + snd_emu8000_tweak_voice(hw, ch); +} + +/* + * Set the pitch of a possibly playing note. + */ +static void +set_pitch(emu8000_t *hw, snd_emux_voice_t *vp) +{ + EMU8000_IP_WRITE(hw, vp->ch, vp->apitch); +} + +/* + * Set the volume of a possibly already playing note + */ +static void +set_volume(emu8000_t *hw, snd_emux_voice_t *vp) +{ + int ifatn; + + ifatn = (unsigned char)vp->acutoff; + ifatn = (ifatn << 8); + ifatn |= (unsigned char)vp->avol; + EMU8000_IFATN_WRITE(hw, vp->ch, ifatn); +} + +/* + * Set pan and loop start address. + */ +static void +set_pan(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned int temp; + + temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1); + EMU8000_PSST_WRITE(hw, vp->ch, temp); +} + +#define MOD_SENSE 18 + +static void +set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fmmod; + short pitch; + unsigned char cutoff; + int modulation; + + pitch = (char)(vp->reg.parm.fmmod>>8); + cutoff = (vp->reg.parm.fmmod & 0xff); + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fmmod = ((unsigned char)pitch<<8) | cutoff; + EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp) +{ + EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq); +} + +/* set lfo2 pitch & frequency */ +static void +set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fm2frq2; + short pitch; + unsigned char freq; + int modulation; + + pitch = (char)(vp->reg.parm.fm2frq2>>8); + freq = vp->reg.parm.fm2frq2 & 0xff; + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fm2frq2 = ((unsigned char)pitch<<8) | freq; + EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2); +} + +/* set filterQ */ +static void +set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned int addr; + addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; + addr |= (vp->reg.parm.filterQ << 28); + EMU8000_CCCA_WRITE(hw, vp->ch, addr); +} + +/* + * set the envelope & LFO parameters to the default values + */ +static void +snd_emu8000_tweak_voice(emu8000_t *emu, int i) +{ + /* set all mod/vol envelope shape to minimum */ + EMU8000_ENVVOL_WRITE(emu, i, 0x8000); + EMU8000_ENVVAL_WRITE(emu, i, 0x8000); + EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F); + EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F); + EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F); + EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */ + EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */ + EMU8000_LFO2VAL_WRITE(emu, i, 0x8000); + EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */ + EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */ + EMU8000_FMMOD_WRITE(emu, i, 0); + EMU8000_TREMFRQ_WRITE(emu, i, 0); + EMU8000_FM2FRQ2_WRITE(emu, i, 0); +} + +/* + * sysex callback + */ +static void +sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return); + + switch (parsed) { + case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: + hw->chorus_mode = chset->gs_chorus_mode; + snd_emu8000_update_chorus_mode(hw); + break; + + case SNDRV_MIDI_SYSEX_GS_REVERB_MODE: + hw->reverb_mode = chset->gs_reverb_mode; + snd_emu8000_update_reverb_mode(hw); + break; + } +} + + +#ifdef CONFIG_SND_SEQUENCER_OSS +/* + * OSS ioctl callback + */ +static int +oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + + switch (cmd) { + case _EMUX_OSS_REVERB_MODE: + hw->reverb_mode = p1; + snd_emu8000_update_reverb_mode(hw); + break; + + case _EMUX_OSS_CHORUS_MODE: + hw->chorus_mode = p1; + snd_emu8000_update_chorus_mode(hw); + break; + + case _EMUX_OSS_INITIALIZE_CHIP: + /* snd_emu8000_init(hw); */ /*ignored*/ + break; + + case _EMUX_OSS_EQUALIZER: + hw->bass_level = p1; + hw->treble_level = p2; + snd_emu8000_update_equalizer(hw); + break; + } + return 0; +} +#endif + + +/* + * additional patch keys + */ + +#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */ +#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */ + + +/* + * callback routine + */ + +static int +load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len) +{ + emu8000_t *hw; + hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + + switch (type) { + case SNDRV_EMU8000_LOAD_CHORUS_FX: + return snd_emu8000_load_chorus_fx(hw, mode, buf, len); + case SNDRV_EMU8000_LOAD_REVERB_FX: + return snd_emu8000_load_reverb_fx(hw, mode, buf, len); + } + return -EINVAL; +} + diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_local.h linux/sound/isa/sb/emu8000_local.h --- linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_local.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/emu8000_local.h 2002-04-29 08:30:52.000000000 -0600 @@ -0,0 +1,45 @@ +#ifndef __EMU8000_LOCAL_H +#define __EMU8000_LOCAL_H +/* + * Local defininitons for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + +/* emu8000_patch.c */ +int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count); +int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); +void snd_emu8000_sample_reset(snd_emux_t *rec); + +/* emu8000_callback.c */ +void snd_emu8000_ops_setup(emu8000_t *emu); + +/* emu8000_pcm.c */ +int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index); + +#endif /* __EMU8000_LOCAL_H */ diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_patch.c linux/sound/isa/sb/emu8000_patch.c --- linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_patch.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/emu8000_patch.c 2002-08-12 02:43:46.000000000 -0600 @@ -0,0 +1,307 @@ +/* + * Patch routines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu8000_local.h" +#include + +MODULE_PARM(emu8000_reset_addr, "i"); +MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)"); + +int emu8000_reset_addr = 0; + + +/* + * Open up channels. + */ +static int +snd_emu8000_open_dma(emu8000_t *emu, int write) +{ + int i; + + /* reserve all 30 voices for loading */ + for (i = 0; i < EMU8000_DRAM_VOICES; i++) { + snd_emux_lock_voice(emu->emu, i); + snd_emu8000_dma_chan(emu, i, write); + } + + /* assign voice 31 and 32 to ROM */ + EMU8000_VTFT_WRITE(emu, 30, 0); + EMU8000_PSST_WRITE(emu, 30, 0x1d8); + EMU8000_CSL_WRITE(emu, 30, 0x1e0); + EMU8000_CCCA_WRITE(emu, 30, 0x1d8); + EMU8000_VTFT_WRITE(emu, 31, 0); + EMU8000_PSST_WRITE(emu, 31, 0x1d8); + EMU8000_CSL_WRITE(emu, 31, 0x1e0); + EMU8000_CCCA_WRITE(emu, 31, 0x1d8); + + return 0; +} + +/* + * Close all dram channels. + */ +static void +snd_emu8000_close_dma(emu8000_t *emu) +{ + int i; + + for (i = 0; i < EMU8000_DRAM_VOICES; i++) { + snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); + snd_emux_unlock_voice(emu->emu, i); + } +} + +/* + */ + +#define BLANK_LOOP_START 4 +#define BLANK_LOOP_END 8 +#define BLANK_LOOP_SIZE 12 +#define BLANK_HEAD_SIZE 48 + +/* + * Read a word from userland, taking care of conversions from + * 8bit samples etc. + */ +static unsigned short +read_word(const void *buf, int offset, int mode) +{ + unsigned short c; + if (mode & SNDRV_SFNT_SAMPLE_8BITS) { + unsigned char cc; + get_user(cc, (unsigned char*)buf + offset); + c = cc << 8; /* convert 8bit -> 16bit */ + } else { +#ifdef SNDRV_LITTLE_ENDIAN + get_user(c, (unsigned short*)buf + offset); +#else + unsigned short cc; + get_user(cc, (unsigned short*)buf + offset); + c = swab16(cc); +#endif + } + if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ + return c; +} + +/* + */ +static void +snd_emu8000_write_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + * write sample word data + * + * You should not have to keep resetting the address each time + * as the chip is supposed to step on the next address automatically. + * It mostly does, but during writes of some samples at random it + * completely loses words (every one in 16 roughly but with no + * obvious pattern). + * + * This is therefore much slower than need be, but is at least + * working. + */ +inline static void +write_word(emu8000_t *emu, int *offset, unsigned short data) +{ + if (emu8000_reset_addr) { + if (emu8000_reset_addr > 1) + snd_emu8000_write_wait(emu); + EMU8000_SMALW_WRITE(emu, *offset); + } + EMU8000_SMLD_WRITE(emu, data); + *offset += 1; +} + +/* + * Write the sample to EMU800 memory. This routine is invoked out of + * the generic soundfont routines as a callback. + */ +int +snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *data, long count) +{ + int i; + int rc; + int offset; + int truesize; + int dram_offset, dram_start; + emu8000_t *emu; + + emu = snd_magic_cast(emu8000_t, rec->hw, return -EINVAL); + snd_assert(sp != NULL, return -EINVAL); + + if (sp->v.size == 0) + return 0; + + /* be sure loop points start < end */ + if (sp->v.loopstart > sp->v.loopend) { + int tmp = sp->v.loopstart; + sp->v.loopstart = sp->v.loopend; + sp->v.loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->v.size; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) + truesize += sp->v.loopend - sp->v.loopstart; + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + + sp->block = snd_util_mem_alloc(hdr, truesize * 2); + if (sp->block == NULL) { + /*snd_printd("EMU8000: out of memory\n");*/ + /* not ENOMEM (for compatibility) */ + return -ENOSPC; + } + + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + if (verify_area(VERIFY_READ, data, sp->v.size)) + return -EFAULT; + } else { + if (verify_area(VERIFY_READ, data, sp->v.size * 2)) + return -EFAULT; + } + + /* recalculate address offset */ + sp->v.end -= sp->v.start; + sp->v.loopstart -= sp->v.start; + sp->v.loopend -= sp->v.start; + sp->v.start = 0; + + /* dram position (in word) -- mem_offset is byte */ + dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1); + dram_start = dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + sp->v.truesize = truesize * 2; /* in bytes */ + + snd_emux_terminate_all(emu->emu); + if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0) + return rc; + + /* Set the address to start writing at */ + snd_emu8000_write_wait(emu); + EMU8000_SMALW_WRITE(emu, dram_offset); + + /*snd_emu8000_init_fm(emu);*/ + +#if 0 + /* first block - write 48 samples for silence */ + if (! sp->block->offset) { + for (i = 0; i < BLANK_HEAD_SIZE; i++) { + write_word(emu, &dram_offset, 0); + } + } +#endif + + offset = 0; + for (i = 0; i < sp->v.size; i++) { + unsigned short s; + + s = read_word(data, offset, sp->v.mode_flags); + offset++; + write_word(emu, &dram_offset, s); + + /* we may take too long time in this loop. + * so give controls back to kernel if needed. + */ + if (need_resched()) { + if (current->state != TASK_RUNNING) + set_current_state(TASK_RUNNING); + schedule(); + } + + if (i == sp->v.loopend && + (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))) + { + int looplen = sp->v.loopend - sp->v.loopstart; + int k; + + /* copy reverse loop */ + for (k = 1; k <= looplen; k++) { + s = read_word(data, offset - k, sp->v.mode_flags); + write_word(emu, &dram_offset, s); + } + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { + sp->v.loopend += looplen; + } else { + sp->v.loopstart += looplen; + sp->v.loopend += looplen; + } + sp->v.end += looplen; + } + } + + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) { + write_word(emu, &dram_offset, 0); + } + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + + /* add dram offset */ + sp->v.start += dram_start; + sp->v.end += dram_start; + sp->v.loopstart += dram_start; + sp->v.loopend += dram_start; + + snd_emu8000_close_dma(emu); + snd_emu8000_init_fm(emu); + + return 0; +} + +/* + * free a sample block + */ +int +snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr) +{ + if (sp->block) { + snd_util_mem_free(hdr, sp->block); + sp->block = NULL; + } + return 0; +} + + +/* + * sample_reset callback - terminate voices + */ +void +snd_emu8000_sample_reset(snd_emux_t *rec) +{ + snd_emux_terminate_all(rec); +} diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_pcm.c linux/sound/isa/sb/emu8000_pcm.c --- linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_pcm.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/emu8000_pcm.c 2003-02-10 04:53:49.000000000 -0700 @@ -0,0 +1,711 @@ +/* + * pcm emulation on emu8000 wavetable + * + * Copyright (C) 2002 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu8000_local.h" +#include +#include +#include + +#define chip_t emu8000_t + +/* + * define the following if you want to use this pcm with non-interleaved mode + */ +/* #define USE_NONINTERLEAVE */ + +/* NOTE: for using the non-interleaved mode with alsa-lib, you have to set + * mmap_emulation flag to 1 in your .asoundrc, such like + * + * pcm.emu8k { + * type plug + * slave.pcm { + * type hw + * card 0 + * device 1 + * mmap_emulation 1 + * } + * } + * + * besides, for the time being, the non-interleaved mode doesn't work well on + * alsa-lib... + */ + + +typedef struct snd_emu8k_pcm emu8k_pcm_t; + +struct snd_emu8k_pcm { + emu8000_t *emu; + snd_pcm_substream_t *substream; + + unsigned int allocated_bytes; + snd_util_memblk_t *block; + unsigned int offset; + unsigned int buf_size; + unsigned int period_size; + unsigned int loop_start[2]; + unsigned int pitch; + int panning[2]; + int last_ptr; + int period_pos; + int voices; + unsigned int dram_opened: 1; + unsigned int running: 1; + unsigned int timer_running: 1; + struct timer_list timer; + spinlock_t timer_lock; +}; + +#define LOOP_BLANK_SIZE 8 + + +/* + * open up channels for the simultaneous data transfer and playback + */ +static int +emu8k_open_dram_for_pcm(emu8000_t *emu, int channels) +{ + int i; + + /* reserve up to 2 voices for playback */ + snd_emux_lock_voice(emu->emu, 0); + if (channels > 1) + snd_emux_lock_voice(emu->emu, 1); + + /* reserve 28 voices for loading */ + for (i = channels + 1; i < EMU8000_DRAM_VOICES; i++) { + unsigned int mode = EMU8000_RAM_WRITE; + snd_emux_lock_voice(emu->emu, i); +#ifndef USE_NONINTERLEAVE + if (channels > 1 && (i & 1) != 0) + mode |= EMU8000_RAM_RIGHT; +#endif + snd_emu8000_dma_chan(emu, i, mode); + } + + /* assign voice 31 and 32 to ROM */ + EMU8000_VTFT_WRITE(emu, 30, 0); + EMU8000_PSST_WRITE(emu, 30, 0x1d8); + EMU8000_CSL_WRITE(emu, 30, 0x1e0); + EMU8000_CCCA_WRITE(emu, 30, 0x1d8); + EMU8000_VTFT_WRITE(emu, 31, 0); + EMU8000_PSST_WRITE(emu, 31, 0x1d8); + EMU8000_CSL_WRITE(emu, 31, 0x1e0); + EMU8000_CCCA_WRITE(emu, 31, 0x1d8); + + return 0; +} + +/* + */ +static void +snd_emu8000_write_wait(emu8000_t *emu, int can_schedule) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + if (can_schedule) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } + } +} + +/* + * close all channels + */ +static void +emu8k_close_dram(emu8000_t *emu) +{ + int i; + + for (i = 0; i < 2; i++) + snd_emux_unlock_voice(emu->emu, i); + for (; i < EMU8000_DRAM_VOICES; i++) { + snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); + snd_emux_unlock_voice(emu->emu, i); + } +} + +/* + * convert Hz to AWE32 rate offset (see emux/soundfont.c) + */ + +#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ +#define SAMPLERATE_RATIO 4096 + +static int calc_rate_offset(int hz) +{ + return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); +} + + +/* + */ + +static snd_pcm_hardware_t emu8k_pcm_hw = { +#ifdef USE_NONINTERLEAVE + .info = SNDRV_PCM_INFO_NONINTERLEAVED, +#else + .info = SNDRV_PCM_INFO_INTERLEAVED, +#endif + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 1024, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, + +}; + +/* + * get the current position at the given channel from CCCA register + */ +static inline int emu8k_get_curpos(emu8k_pcm_t *rec, int ch) +{ + int val = EMU8000_CCCA_READ(rec->emu, ch) & 0xfffffff; + val -= rec->loop_start[ch] - 1; + return val; +} + + +/* + * timer interrupt handler + * check the current position and update the period if necessary. + */ +static void emu8k_pcm_timer_func(unsigned long data) +{ + emu8k_pcm_t *rec = (emu8k_pcm_t *)data; + int ptr, delta; + + spin_lock(&rec->timer_lock); + /* update the current pointer */ + ptr = emu8k_get_curpos(rec, 0); + if (ptr < rec->last_ptr) + delta = ptr + rec->buf_size - rec->last_ptr; + else + delta = ptr - rec->last_ptr; + rec->period_pos += delta; + rec->last_ptr = ptr; + + /* reprogram timer */ + rec->timer.expires = jiffies + 1; + add_timer(&rec->timer); + + /* update period */ + if (rec->period_pos >= (int)rec->period_size) { + rec->period_pos %= rec->period_size; + spin_unlock(&rec->timer_lock); + snd_pcm_period_elapsed(rec->substream); + return; + } + spin_unlock(&rec->timer_lock); +} + + +/* + * open pcm + * creating an instance here + */ +static int emu8k_pcm_open(snd_pcm_substream_t *subs) +{ + emu8000_t *emu = snd_pcm_substream_chip(subs); + emu8k_pcm_t *rec; + snd_pcm_runtime_t *runtime = subs->runtime; + + rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL); + if (! rec) + return -ENOMEM; + + rec->emu = emu; + rec->substream = subs; + runtime->private_data = rec; + + spin_lock_init(&rec->timer_lock); + init_timer(&rec->timer); + rec->timer.function = emu8k_pcm_timer_func; + rec->timer.data = (unsigned long)rec; + + runtime->hw = emu8k_pcm_hw; + runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3; + runtime->hw.period_bytes_max = runtime->hw.buffer_bytes_max / 2; + + /* use timer to update periods.. (specified in msec) */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + (1000000 + HZ - 1) / HZ, UINT_MAX); + + return 0; +} + +static int emu8k_pcm_close(snd_pcm_substream_t *subs) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + if (rec) + kfree(rec); + subs->runtime->private_data = 0; + return 0; +} + +/* + * calculate pitch target + */ +static int calc_pitch_target(int pitch) +{ + int ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) ptarget += (ptarget * 0x102e) / 0x2710; + if (pitch & 0x400) ptarget += (ptarget * 0x764) / 0x2710; + if (pitch & 0x200) ptarget += (ptarget * 0x389) / 0x2710; + ptarget += (ptarget >> 1); + if (ptarget > 0xffff) ptarget = 0xffff; + return ptarget; +} + +/* + * set up the voice + */ +static void setup_voice(emu8k_pcm_t *rec, int ch) +{ + emu8000_t *hw = rec->emu; + unsigned int temp; + + /* channel to be silent and idle */ + EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); + EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); + EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); + EMU8000_PTRX_WRITE(hw, ch, 0); + EMU8000_CPF_WRITE(hw, ch, 0); + + /* pitch offset */ + EMU8000_IP_WRITE(hw, ch, rec->pitch); + /* set envelope parameters */ + EMU8000_ENVVAL_WRITE(hw, ch, 0x8000); + EMU8000_ATKHLD_WRITE(hw, ch, 0x7f7f); + EMU8000_DCYSUS_WRITE(hw, ch, 0x7f7f); + EMU8000_ENVVOL_WRITE(hw, ch, 0x8000); + EMU8000_ATKHLDV_WRITE(hw, ch, 0x7f7f); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + /* modulation envelope heights */ + EMU8000_PEFE_WRITE(hw, ch, 0x0); + /* lfo1/2 delay */ + EMU8000_LFO1VAL_WRITE(hw, ch, 0x8000); + EMU8000_LFO2VAL_WRITE(hw, ch, 0x8000); + /* lfo1 pitch & cutoff shift */ + EMU8000_FMMOD_WRITE(hw, ch, 0); + /* lfo1 volume & freq */ + EMU8000_TREMFRQ_WRITE(hw, ch, 0); + /* lfo2 pitch & freq */ + EMU8000_FM2FRQ2_WRITE(hw, ch, 0); + /* pan & loop start */ + temp = rec->panning[ch]; + temp = (temp <<24) | ((unsigned int)rec->loop_start[ch] - 1); + EMU8000_PSST_WRITE(hw, ch, temp); + /* chorus & loop end (chorus 8bit, MSB) */ + temp = 0; // chorus + temp = (temp << 24) | ((unsigned int)rec->loop_start[ch] + rec->buf_size - 1); + EMU8000_CSL_WRITE(hw, ch, temp); + /* Q & current address (Q 4bit value, MSB) */ + temp = 0; // filterQ + temp = (temp << 28) | ((unsigned int)rec->loop_start[ch] - 1); + EMU8000_CCCA_WRITE(hw, ch, temp); + /* clear unknown registers */ + EMU8000_00A0_WRITE(hw, ch, 0); + EMU8000_0080_WRITE(hw, ch, 0); +} + +/* + * trigger the voice + */ +static void start_voice(emu8k_pcm_t *rec, int ch) +{ + unsigned long flags; + emu8000_t *hw = rec->emu; + unsigned int temp, aux; + int pt = calc_pitch_target(rec->pitch); + + /* cutoff and volume */ + EMU8000_IFATN_WRITE(hw, ch, 0xff00); + EMU8000_VTFT_WRITE(hw, ch, 0xffff); + EMU8000_CVCF_WRITE(hw, ch, 0xffff); + /* trigger envelope */ + EMU8000_DCYSUSV_WRITE(hw, ch, 0x7f7f); + /* set reverb and pitch target */ + temp = 0; // reverb + if (rec->panning[ch] == 0) + aux = 0xff; + else + aux = (-rec->panning[ch]) & 0xff; + temp = (temp << 8) | (pt << 16) | aux; + EMU8000_PTRX_WRITE(hw, ch, temp); + EMU8000_CPF_WRITE(hw, ch, pt << 16); + + /* start timer */ + spin_lock_irqsave(&rec->timer_lock, flags); + if (! rec->timer_running) { + rec->timer.expires = jiffies + 1; + add_timer(&rec->timer); + rec->timer_running = 1; + } + spin_unlock_irqrestore(&rec->timer_lock, flags); +} + +/* + * stop the voice immediately + */ +static void stop_voice(emu8k_pcm_t *rec, int ch) +{ + unsigned long flags; + emu8000_t *hw = rec->emu; + + EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); + + /* stop timer */ + spin_lock_irqsave(&rec->timer_lock, flags); + if (rec->timer_running) { + del_timer(&rec->timer); + rec->timer_running = 0; + } + spin_unlock_irqrestore(&rec->timer_lock, flags); +} + +static int emu8k_pcm_trigger(snd_pcm_substream_t *subs, int cmd) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + int ch; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + for (ch = 0; ch < rec->voices; ch++) + start_voice(rec, ch); + rec->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + rec->running = 0; + for (ch = 0; ch < rec->voices; ch++) + stop_voice(rec, ch); + break; + default: + return -EINVAL; + } + return 0; +} + + +/* + * copy / silence ops + */ + +/* + * this macro should be inserted in the copy/silence loops + * to reduce the latency. without this, the system will hang up + * during the whole loop. + */ +#define CHECK_SCHEDULER() \ +do { \ + if (need_resched()) {\ + if (current->state != TASK_RUNNING)\ + set_current_state(TASK_RUNNING);\ + schedule();\ + if (signal_pending(current))\ + return -EAGAIN;\ + }\ +} while (0) + + +#ifdef USE_NONINTERLEAVE +/* copy one channel block */ +static int emu8k_transfer_block(emu8000_t *emu, int offset, unsigned short *buf, int count) +{ + EMU8000_SMALW_WRITE(emu, offset); + while (count > 0) { + unsigned short sval; + CHECK_SCHEDULER(); + get_user(sval, buf); + EMU8000_SMLD_WRITE(emu, sval); + buf++; + count--; + } + return 0; +} + +static int emu8k_pcm_copy(snd_pcm_substream_t *subs, + int voice, + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + emu8000_t *emu = rec->emu; + + snd_emu8000_write_wait(emu, 1); + if (voice == -1) { + unsigned short *buf = src; + int i, err; + count /= rec->voices; + for (i = 0; i < rec->voices; i++) { + err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count); + if (err < 0) + return err; + buf += count; + } + return 0; + } else { + return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count); + } +} + +/* make a channel block silence */ +static int emu8k_silence_block(emu8000_t *emu, int offset, int count) +{ + EMU8000_SMALW_WRITE(emu, offset); + while (count > 0) { + CHECK_SCHEDULER(); + EMU8000_SMLD_WRITE(emu, 0); + count--; + } + return 0; +} + +static int emu8k_pcm_silence(snd_pcm_substream_t *subs, + int voice, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + emu8000_t *emu = rec->emu; + + snd_emu8000_write_wait(emu, 1); + if (voice == -1 && rec->voices == 1) + voice = 0; + if (voice == -1) { + int err; + err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2); + if (err < 0) + return err; + return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2); + } else { + return emu8k_silence_block(emu, pos + rec->loop_start[voice], count); + } +} + +#else /* interleave */ + +/* + * copy the interleaved data can be done easily by using + * DMA "left" and "right" channels on emu8k engine. + */ +static int emu8k_pcm_copy(snd_pcm_substream_t *subs, + int voice, + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + emu8000_t *emu = rec->emu; + unsigned short *buf = src; + + snd_emu8000_write_wait(emu, 1); + EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); + if (rec->voices > 1) + EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); + + while (count-- > 0) { + unsigned short sval; + CHECK_SCHEDULER(); + get_user(sval, buf); + EMU8000_SMLD_WRITE(emu, sval); + buf++; + if (rec->voices > 1) { + CHECK_SCHEDULER(); + get_user(sval, buf); + EMU8000_SMRD_WRITE(emu, sval); + buf++; + } + } + return 0; +} + +static int emu8k_pcm_silence(snd_pcm_substream_t *subs, + int voice, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + emu8000_t *emu = rec->emu; + + snd_emu8000_write_wait(emu, 1); + EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos); + if (rec->voices > 1) + EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos); + while (count-- > 0) { + CHECK_SCHEDULER(); + EMU8000_SMLD_WRITE(emu, 0); + if (rec->voices > 1) { + CHECK_SCHEDULER(); + EMU8000_SMRD_WRITE(emu, 0); + } + } + return 0; +} +#endif + + +/* + * allocate a memory block + */ +static int emu8k_pcm_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw_params) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + + if (rec->block) { + /* reallocation - release the old block */ + snd_util_mem_free(rec->emu->memhdr, rec->block); + rec->block = NULL; + } + + rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4; + rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes); + if (! rec->block) + return -ENOMEM; + rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */ + /* at least dma_bytes must be set for non-interleaved mode */ + subs->dma_buffer.bytes = params_buffer_bytes(hw_params); + + return 0; +} + +/* + * free the memory block + */ +static int emu8k_pcm_hw_free(snd_pcm_substream_t *subs) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + + if (rec->block) { + int ch; + for (ch = 0; ch < rec->voices; ch++) + stop_voice(rec, ch); // to be sure + if (rec->dram_opened) + emu8k_close_dram(rec->emu); + snd_util_mem_free(rec->emu->memhdr, rec->block); + rec->block = NULL; + } + return 0; +} + +/* + */ +static int emu8k_pcm_prepare(snd_pcm_substream_t *subs) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + + rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate); + rec->last_ptr = 0; + rec->period_pos = 0; + + rec->buf_size = subs->runtime->buffer_size; + rec->period_size = subs->runtime->period_size; + rec->voices = subs->runtime->channels; + rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE; + if (rec->voices > 1) + rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE; + if (rec->voices > 1) { + rec->panning[0] = 0xff; + rec->panning[1] = 0x00; + } else + rec->panning[0] = 0x80; + + if (! rec->dram_opened) { + int err, i, ch; + + snd_emux_terminate_all(rec->emu->emu); + if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0) + return err; + rec->dram_opened = 1; + + /* clear loop blanks */ + snd_emu8000_write_wait(rec->emu, 0); + EMU8000_SMALW_WRITE(rec->emu, rec->offset); + for (i = 0; i < LOOP_BLANK_SIZE; i++) + EMU8000_SMLD_WRITE(rec->emu, 0); + for (ch = 0; ch < rec->voices; ch++) { + EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size); + for (i = 0; i < LOOP_BLANK_SIZE; i++) + EMU8000_SMLD_WRITE(rec->emu, 0); + } + } + + setup_voice(rec, 0); + if (rec->voices > 1) + setup_voice(rec, 1); + return 0; +} + +static snd_pcm_uframes_t emu8k_pcm_pointer(snd_pcm_substream_t *subs) +{ + emu8k_pcm_t *rec = subs->runtime->private_data; + if (rec->running) + return emu8k_get_curpos(rec, 0); + return 0; +} + + +static snd_pcm_ops_t emu8k_pcm_ops = { + .open = emu8k_pcm_open, + .close = emu8k_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = emu8k_pcm_hw_params, + .hw_free = emu8k_pcm_hw_free, + .prepare = emu8k_pcm_prepare, + .trigger = emu8k_pcm_trigger, + .pointer = emu8k_pcm_pointer, + .copy = emu8k_pcm_copy, + .silence = emu8k_pcm_silence, +}; + + +static void snd_emu8000_pcm_free(snd_pcm_t *pcm) +{ + emu8000_t *emu = pcm->private_data; + emu->pcm = NULL; +} + +int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = emu; + pcm->private_free = snd_emu8000_pcm_free; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops); + emu->pcm = pcm; + + snd_device_register(card, pcm); + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_synth.c linux/sound/isa/sb/emu8000_synth.c --- linux-2.4.21-rc1.orig/sound/isa/sb/emu8000_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/emu8000_synth.c 2002-05-25 04:26:10.000000000 -0600 @@ -0,0 +1,134 @@ +/* + * Copyright (c) by Jaroslav Kysela + * and (c) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * Emu8000 synth plug-in routine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu8000_local.h" +#include +#include + +MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); +MODULE_DESCRIPTION("Emu8000 synth plug-in routine"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +/*----------------------------------------------------------------*/ + +/* + * create a new hardware dependent device for Emu8000 + */ +static int snd_emu8000_new_device(snd_seq_device_t *dev) +{ + emu8000_t *hw; + snd_emux_t *emu; + + hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (hw == NULL) + return -EINVAL; + + if (hw->emu) + return -EBUSY; /* already exists..? */ + + if (snd_emux_new(&emu) < 0) + return -ENOMEM; + + hw->emu = emu; + snd_emu8000_ops_setup(hw); + + emu->hw = hw; + emu->max_voices = EMU8000_DRAM_VOICES; + emu->num_ports = hw->seq_ports; + + if (hw->memhdr) { + snd_printk("memhdr is already initialized!?\n"); + snd_util_memhdr_free(hw->memhdr); + } + hw->memhdr = snd_util_memhdr_new(hw->mem_size); + if (hw->memhdr == NULL) { + snd_emux_free(emu); + hw->emu = NULL; + return -ENOMEM; + } + + emu->memhdr = hw->memhdr; + emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */ + emu->midi_devidx = 1; + emu->linear_panning = 1; + + if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) { + snd_emux_free(emu); + snd_util_memhdr_free(hw->memhdr); + hw->emu = NULL; + hw->memhdr = NULL; + return -ENOMEM; + } + + if (hw->mem_size > 0) + snd_emu8000_pcm_new(dev->card, hw, 1); + + dev->driver_data = hw; + + return 0; +} + + +/* + * free all resources + */ +static int snd_emu8000_delete_device(snd_seq_device_t *dev) +{ + emu8000_t *hw; + + if (dev->driver_data == NULL) + return 0; /* no synth was allocated actually */ + + hw = dev->driver_data; + if (hw->pcm) + snd_device_free(dev->card, hw->pcm); + if (hw->emu) + snd_emux_free(hw->emu); + if (hw->memhdr) + snd_util_memhdr_free(hw->memhdr); + hw->emu = NULL; + hw->memhdr = NULL; + return 0; +} + +/* + * INIT part + */ + +static int __init alsa_emu8000_init(void) +{ + + static snd_seq_dev_ops_t ops = { + snd_emu8000_new_device, + snd_emu8000_delete_device, + }; + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*)); +} + +static void __exit alsa_emu8000_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); +} + +module_init(alsa_emu8000_init) +module_exit(alsa_emu8000_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/es968.c linux/sound/isa/sb/es968.c --- linux-2.4.21-rc1.orig/sound/isa/sb/es968.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/es968.c 2003-02-19 02:33:44.000000000 -0700 @@ -0,0 +1,300 @@ + +/* + card-es968.c - driver for ESS AudioDrive ES968 based soundcards. + Copyright (C) 1999 by Massimo Piccioni + + Thanks to Pierfrancesco 'qM2' Passerini. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include + +#define chip_t sb_t + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("ESS AudioDrive ES968"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,AudioDrive ES968}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for es968 based soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for es968 based soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable es968 based soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for es968 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for es968 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); + +struct snd_card_es968 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_es968_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_es968_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_es968_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_es968_pnpids[] __devinitdata = { + { + ISAPNP_CARD_ID('E','S','S',0x0968), + .devs = { ISAPNP_DEVICE_ID('E','S','S',0x0968), } + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_es968_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-es968" + + +static void snd_card_es968_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + + if (chip->open & SB_OPEN_PCM) { + snd_sb8dsp_interrupt(chip); + } else { + snd_sb8dsp_midi_interrupt(chip); + } +} + +#ifdef __ISAPNP__ +static int __init snd_card_es968_isapnp(int dev, struct snd_card_es968 *acard) +{ + const struct isapnp_card_id *id = snd_es968_isapnp_id[dev]; + struct isapnp_card *card = snd_es968_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + if (dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma8[dev], + 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + port[dev] = pdev->resource[0].start; + dma8[dev] = pdev->dma_resource[0].start; + irq[dev] = pdev->irq_resource[0].start; + + return 0; +} + +static void snd_card_es968_deactivate(struct snd_card_es968 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_es968_free(snd_card_t *card) +{ + struct snd_card_es968 *acard = (struct snd_card_es968 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_es968_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_es968_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_es968 *acard; + + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_card_es968))) == NULL) + return -ENOMEM; + acard = (struct snd_card_es968 *)card->private_data; + card->private_free = snd_card_es968_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_es968_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + snd_printk("you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, port[dev], + irq[dev], + snd_card_es968_interrupt, + dma8[dev], + -1, + SB_HW_AUTO, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + strcpy(card->driver, "ES968"); + strcpy(card->shortname, "ESS ES968"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, chip->name, chip->port, irq[dev], dma8[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_es968_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_es968_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; + snd_es968_isapnp_cards[dev] = card; + snd_es968_isapnp_id[dev] = id; + res = snd_card_es968_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_es968_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_es968_pnpids, snd_es968_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no ES968 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_es968_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_es968_cards[dev]); +} + +module_init(alsa_card_es968_init) +module_exit(alsa_card_es968_exit) + +#ifndef MODULE + +/* format is: snd-es968=enable,index,id, + port,irq,dma1 */ + +static int __init alsa_card_es968_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es968=", alsa_card_es968_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb16.c linux/sound/isa/sb/sb16.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb16.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb16.c 2003-03-06 06:53:59.000000000 -0700 @@ -0,0 +1,715 @@ +/* + * Driver for SoundBlaster 16/AWE32/AWE64 soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define chip_t sb_t + +#ifdef SNDRV_SBAWE +#define PFX "sbawe: " +#else +#define PFX "sb16: " +#endif + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifndef SNDRV_SBAWE +MODULE_DESCRIPTION("Sound Blaster 16"); +MODULE_DEVICES("{{Creative Labs,SB 16}," + "{Creative Labs,SB Vibra16S}," + "{Creative Labs,SB Vibra16C}," + "{Creative Labs,SB Vibra16CL}," + "{Creative Labs,SB Vibra16X}}"); +#else +MODULE_DESCRIPTION("Sound Blaster AWE"); +MODULE_DEVICES("{{Creative Labs,SB AWE 32}," + "{Creative Labs,SB AWE 64}," + "{Creative Labs,SB AWE 64 Gold}}"); +#endif + +#if 0 +#define SNDRV_DEBUG_IRQ +#endif + +#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)) +#define SNDRV_SBAWE_EMU8000 +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ +static long mpu_port[SNDRV_CARDS] = {0x330, 0x300,[2 ... (SNDRV_CARDS - 1)] = -1}; +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#ifdef SNDRV_SBAWE_EMU8000 +static long awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#endif +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */ +static int mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#ifdef CONFIG_SND_SB16_CSP +static int csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +#endif +#ifdef SNDRV_SBAWE_EMU8000 +static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; +#endif + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for SB16 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260},{0x280}},dialog:list"); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x38c},{0x390},{0x394}},dialog:list"); +#ifdef SNDRV_SBAWE_EMU8000 +MODULE_PARM(awe_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver."); +MODULE_PARM_SYNTAX(awe_port, SNDRV_ENABLED ",allows:{{0x620},{0x640},{0x660},{0x680}},dialog:list"); +#endif +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for SB16 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); +MODULE_PARM(dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver."); +MODULE_PARM_SYNTAX(dma16, SNDRV_DMA16_DESC); +MODULE_PARM(mic_agc, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch."); +MODULE_PARM_SYNTAX(mic_agc, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +#ifdef CONFIG_SND_SB16_CSP +MODULE_PARM(csp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(csp, "ASP/CSP chip support."); +MODULE_PARM_SYNTAX(csp, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +#endif +#ifdef SNDRV_SBAWE_EMU8000 +MODULE_PARM(seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(seq_ports, "Number of sequencer ports for WaveTable synth."); +MODULE_PARM_SYNTAX(seq_ports, SNDRV_ENABLED ",allows:{{0,8}},skill:advanced"); +#endif + +struct snd_sb16 { + struct resource *fm_res; /* used to block FM i/o region for legacy cards */ +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#ifdef SNDRV_SBAWE_EMU8000 + struct isapnp_dev *devwt; +#endif +#endif +}; + +static snd_card_t *snd_sb16_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_sb16_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_sb16_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#define ISAPNP_SB16(_va, _vb, _vc, _device, _audio) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \ + } +#define ISAPNP_SBAWE(_va, _vb, _vc, _device, _audio, _awe) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _awe), } \ + } + +static struct isapnp_card_id snd_sb16_pnpids[] __devinitdata = { +#ifndef SNDRV_SBAWE + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0024,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0025,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0026,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0027,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0028,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0029,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x002a,0x0031), + /* Sound Blaster 16 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SB16('C','T','L',0x002b,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x002c,0x0031), + /* Sound Blaster Vibra16S */ + ISAPNP_SB16('C','T','L',0x0051,0x0001), + /* Sound Blaster Vibra16C */ + ISAPNP_SB16('C','T','L',0x0070,0x0001), + /* Sound Blaster Vibra16CL - added by ctm@ardi.com */ + ISAPNP_SB16('C','T','L',0x0080,0x0041), + /* Sound Blaster 16 'value' PnP. It says model ct4130 on the pcb, */ + /* but ct4131 on a sticker on the board.. */ + ISAPNP_SB16('C','T','L',0x0086,0x0041), + /* Sound Blaster Vibra16X */ + ISAPNP_SB16('C','T','L',0x00f0,0x0043), +#else /* SNDRV_SBAWE defined */ + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0035,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0039,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0042,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0043,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SBAWE('C','T','L',0x0044,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SBAWE('C','T','L',0x0045,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0046,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0047,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0048,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0054,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009a,0x0041,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009c,0x0041,0x0021), + /* Sound Blaster 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009f,0x0041,0x0021), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x009d,0x0042,0x0022), + /* Sound Blaster AWE 64 PnP Gold */ + ISAPNP_SBAWE('C','T','L',0x009e,0x0044,0x0023), + /* Sound Blaster AWE 64 PnP Gold */ + ISAPNP_SBAWE('C','T','L',0x00b2,0x0044,0x0023), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c1,0x0042,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c3,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c5,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c7,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00e4,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00e9,0x0045,0x0022), + /* Sound Blaster 16 PnP (AWE) */ + ISAPNP_SBAWE('C','T','L',0x00ed,0x0041,0x0070), + /* Generic entries */ + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0031,0x0021), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0041,0x0021), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0042,0x0022), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0044,0x0023), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0045,0x0022), +#endif /* SNDRV_SBAWE */ + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_sb16_pnpids); + +static int __init snd_sb16_isapnp(int dev, struct snd_sb16 *acard) +{ + const struct isapnp_card_id *id = snd_sb16_isapnp_id[dev]; + struct isapnp_card *card = snd_sb16_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } +#ifdef SNDRV_SBAWE_EMU8000 + acard->devwt = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devwt->active) { + acard->dev = acard->devwt = NULL; + return -EBUSY; + } +#endif + /* Audio initialization */ + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + if (port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], port[dev], 16); + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], mpu_port[dev], 2); + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], fm_port[dev], 4); + if (dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma8[dev], 1); + if (dma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma16[dev], 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], irq[dev], 1); + if (pdev->activate(pdev) < 0) { + printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + port[dev] = pdev->resource[0].start; + mpu_port[dev] = pdev->resource[1].start; + fm_port[dev] = pdev->resource[2].start; + dma8[dev] = pdev->dma_resource[0].start; + dma16[dev] = pdev->dma_resource[1].start; + irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n", + port[dev], mpu_port[dev], fm_port[dev]); + snd_printdd("isapnp SB16: dma1=%i, dma2=%i, irq=%i\n", + dma8[dev], dma16[dev], irq[dev]); +#ifdef SNDRV_SBAWE_EMU8000 + /* WaveTable initialization */ + pdev = acard->devwt; + if (pdev->prepare(pdev)<0) { + acard->dev->deactivate(acard->dev); + return -EAGAIN; + } + if (awe_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], awe_port[dev], 4); + isapnp_resource_change(&pdev->resource[1], awe_port[dev] + 0x400, 4); + isapnp_resource_change(&pdev->resource[2], awe_port[dev] + 0x800, 4); + } + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "WaveTable isapnp configure failure (out of resources?)\n"); + acard->dev->deactivate(acard->dev); + return -EBUSY; + } + awe_port[dev] = pdev->resource[0].start; + snd_printdd("isapnp SB16: wavetable port=0x%lx\n", pdev->resource[0].start); +#endif + return 0; +} + +static void snd_sb16_deactivate(struct snd_sb16 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +#ifdef SNDRV_SBAWE_EMU8000 + if (acard->devwt) { + acard->devwt->deactivate(acard->devwt); + acard->devwt = NULL; + } +#endif +} + +#endif /* __ISAPNP__ */ + +static void snd_sb16_free(snd_card_t *card) +{ + struct snd_sb16 *acard = (struct snd_sb16 *)card->private_data; + + if (acard == NULL) + return; + if (acard->fm_res) { + release_resource(acard->fm_res); + kfree_nocheck(acard->fm_res); + } +#ifdef __ISAPNP__ + snd_sb16_deactivate(acard); +#endif +} + +static int __init snd_sb16_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dmas8[] = {1, 3, 0, -1}; + static int possible_dmas16[] = {5, 6, 7, -1}; + int xirq, xdma8, xdma16; + sb_t *chip; + snd_card_t *card; + struct snd_sb16 *acard; + opl3_t *opl3; + snd_hwdep_t *synth = NULL; +#ifdef CONFIG_SND_SB16_CSP + snd_hwdep_t *xcsp = NULL; +#endif + unsigned long flags; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_sb16)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_sb16 *) card->private_data; + card->private_free = snd_sb16_free; +#ifdef __ISAPNP__ + if (isapnp[dev] && snd_sb16_isapnp(dev, acard) < 0) { + snd_card_free(card); + return -EBUSY; + } +#endif + + xirq = irq[dev]; + xdma8 = dma8[dev]; + xdma16 = dma16[dev]; +#ifdef __ISAPNP__ + if (!isapnp[dev]) { +#endif + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + printk(KERN_ERR PFX "unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (xdma8 == SNDRV_AUTO_DMA) { + if ((xdma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) { + snd_card_free(card); + printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n"); + return -EBUSY; + } + } + if (xdma16 == SNDRV_AUTO_DMA) { + if ((xdma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) { + snd_card_free(card); + printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n"); + return -EBUSY; + } + } + /* non-PnP FM port address is hardwired with base port address */ + fm_port[dev] = port[dev]; + /* block the 0x388 port to avoid PnP conflicts */ + acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); +#ifdef SNDRV_SBAWE_EMU8000 + /* non-PnP AWE port address is hardwired with base port address */ + awe_port[dev] = port[dev] + 0x400; +#endif +#ifdef __ISAPNP__ + } +#endif + + if ((err = snd_sbdsp_create(card, + port[dev], + xirq, + snd_sb16dsp_interrupt, + xdma8, + xdma16, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware != SB_HW_16) { + snd_card_free(card); + snd_printdd("SB 16 chip was not detected at 0x%lx\n", port[dev]); + return -ENODEV; + } + chip->mpu_port = mpu_port[dev]; +#ifdef __ISAPNP__ + if (!isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) { +#else + if ((err = snd_sb16dsp_configure(chip)) < 0) { +#endif + snd_card_free(card); + return -ENXIO; + } + if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return -ENXIO; + } + + if (chip->mpu_port) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB, + chip->mpu_port, 0, + xirq, 0, &chip->rmidi)) < 0) { + snd_card_free(card); + return -ENXIO; + } + chip->rmidi_callback = snd_mpu401_uart_interrupt; + } + + if (fm_port[dev] > 0) { + if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2, + OPL3_HW_OPL3, fm_port[dev] == port[dev], + &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", + fm_port[dev], fm_port[dev] + 2); + } else { +#ifdef SNDRV_SBAWE_EMU8000 + int seqdev = awe_port[dev] > 0 ? 2 : 1; +#else + int seqdev = 1; +#endif + if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) { + snd_card_free(card); + return -ENXIO; + } + } + } + + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return -ENXIO; + } + +#ifdef CONFIG_SND_SB16_CSP + /* CSP chip on SB16ASP/AWE32 */ + if ((chip->hardware == SB_HW_16) && csp[dev]) { + snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &xcsp); + if (xcsp) { + chip->csp = xcsp->private_data; + chip->hardware = SB_HW_16CSP; + } else { + printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1); + } + } +#endif +#ifdef SNDRV_SBAWE_EMU8000 + if (awe_port[dev] > 0) { + if (snd_emu8000_new(card, 1, awe_port[dev], + seq_ports[dev], NULL) < 0) { + printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", awe_port[dev]); + snd_card_free(card); + return -ENXIO; + } + } +#endif + + /* setup Mic AGC */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, SB_DSP4_MIC_AGC, + (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) | + (mic_agc[dev] ? 0x00 : 0x01)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + strcpy(card->driver, +#ifdef SNDRV_SBAWE_EMU8000 + awe_port[dev] > 0 ? "SB AWE" : +#endif + "SB16"); + strcpy(card->shortname, chip->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma ", + chip->name, + chip->port, + xirq); + if (xdma8 >= 0) + sprintf(card->longname + strlen(card->longname), "%d", xdma8); + if (xdma16 >= 0) + sprintf(card->longname + strlen(card->longname), "%s%d", + xdma8 >= 0 ? "&" : "", xdma16); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sb16_cards[dev] = card; + return 0; +} + +static int __init snd_sb16_probe_legacy_port(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + port[dev] = xport; + res = snd_sb16_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +#ifdef __ISAPNP__ + +static int __init snd_sb16_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || !isapnp[dev]) + continue; + snd_sb16_isapnp_cards[dev] = card; + snd_sb16_isapnp_id[dev] = id; + res = snd_sb16_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_sb16_init(void) +{ + int dev, cards = 0; + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; + + /* legacy non-auto cards at first */ + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (!snd_sb16_probe(dev)) { + cards++; + continue; + } +#ifdef MODULE + printk(KERN_ERR "Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, port[dev]); +#endif + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards at last */ + cards += isapnp_probe_cards(snd_sb16_pnpids, snd_sb16_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n"); +#ifdef SNDRV_SBAWE_EMU8000 + printk(KERN_ERR "In case, if you have non-AWE card, try snd-sb16 module\n"); +#else + printk(KERN_ERR "In case, if you have AWE card, try snd-sbawe module\n"); +#endif +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_sb16_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_sb16_cards[dev]); +} + +module_init(alsa_card_sb16_init) +module_exit(alsa_card_sb16_exit) + +#ifndef MODULE + +/* format is: snd-sb16=enable,index,id,isapnp, + port,mpu_port,fm_port, + irq,dma8,dma16, + mic_agc,csp, + [awe_port,seq_ports] */ + +static int __init alsa_card_sb16_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + int __attribute__ ((__unused__)) xcsp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2 && + get_option(&str,&dma16[nr_dev]) == 2 && + get_option(&str,&mic_agc[nr_dev]) == 2 +#ifdef CONFIG_SND_SB16_CSP + && + get_option(&str,&xcsp) == 2 +#endif +#ifdef SNDRV_SBAWE_EMU8000 + && + get_option(&str,(int *)&awe_port[nr_dev]) == 2 && + get_option(&str,&seq_ports[nr_dev]) == 2 +#endif + ); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + isapnp[nr_dev] = pnp; +#endif +#ifdef CONFIG_SND_SB16_CSP + if (xcsp != INT_MAX) + csp[nr_dev] = xcsp; +#endif + nr_dev++; + return 1; +} + +#ifndef SNDRV_SBAWE_EMU8000 +__setup("snd-sb16=", alsa_card_sb16_setup); +#else +__setup("snd-sbawe=", alsa_card_sb16_setup); +#endif + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb16_csp.c linux/sound/isa/sb/sb16_csp.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb16_csp.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb16_csp.c 2003-02-18 09:23:13.000000000 -0700 @@ -0,0 +1,1180 @@ +/* + * Copyright (c) 1999 by Uros Bizjak + * Takashi Iwai + * + * SB16ASP/AWE32 CSP control + * + * CSP microcode loader: + * alsa-tools/sb16_csp/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t snd_sb_csp_t + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#ifdef SNDRV_LITTLE_ENDIAN +#define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) +#else +#define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) +#endif +#define LE_SHORT(v) le16_to_cpu(v) +#define LE_INT(v) le32_to_cpu(v) + +#define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F') +#define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ') +#define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T') +#define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c') +#define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e') +#define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't') +#define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n') + +/* + * RIFF data format + */ +typedef struct riff_header { + __u32 name; + __u32 len; +} riff_header_t; + +typedef struct desc_header { + riff_header_t info; + __u16 func_nr; + __u16 VOC_type; + __u16 flags_play_rec; + __u16 flags_16bit_8bit; + __u16 flags_stereo_mono; + __u16 flags_rates; +} desc_header_t; + +/* + * prototypes + */ +static void snd_sb_csp_free(snd_hwdep_t *hw); +static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file); +static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg); +static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file); + +static int csp_detect(sb_t *chip, int *version); +static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val); +static int set_register(sb_t *chip, unsigned char reg, unsigned char val); +static int read_register(sb_t *chip, unsigned char reg); +static int set_mode_register(sb_t *chip, unsigned char mode); +static int get_version(sb_t *chip); + +static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * code); +static int snd_sb_csp_unload(snd_sb_csp_t * p); +static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags); +static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); +static int snd_sb_csp_check_version(snd_sb_csp_t * p); + +static int snd_sb_csp_use(snd_sb_csp_t * p); +static int snd_sb_csp_unuse(snd_sb_csp_t * p); +static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels); +static int snd_sb_csp_stop(snd_sb_csp_t * p); +static int snd_sb_csp_pause(snd_sb_csp_t * p); +static int snd_sb_csp_restart(snd_sb_csp_t * p); + +static int snd_sb_qsound_build(snd_sb_csp_t * p); +static void snd_sb_qsound_destroy(snd_sb_csp_t * p); +static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p); + +static int init_proc_entry(snd_sb_csp_t * p, int device); +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); + +/* + * Detect CSP chip and create a new instance + */ +int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep) +{ + snd_sb_csp_t *p; + int version, err; + snd_hwdep_t *hw; + + if (rhwdep) + *rhwdep = NULL; + + if (csp_detect(chip, &version)) + return -ENODEV; + + if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) + return err; + + if ((p = snd_magic_kcalloc(snd_sb_csp_t, 0, GFP_KERNEL)) == NULL) { + snd_device_free(chip->card, hw); + return -ENOMEM; + } + p->chip = chip; + p->version = version; + + /* CSP operators */ + p->ops.csp_use = snd_sb_csp_use; + p->ops.csp_unuse = snd_sb_csp_unuse; + p->ops.csp_autoload = snd_sb_csp_autoload; + p->ops.csp_start = snd_sb_csp_start; + p->ops.csp_stop = snd_sb_csp_stop; + p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer; + + init_MUTEX(&p->access_mutex); + sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f)); + hw->iface = SNDRV_HWDEP_IFACE_SB16CSP; + hw->private_data = p; + hw->private_free = snd_sb_csp_free; + + /* operators - only write/ioctl */ + hw->ops.open = snd_sb_csp_open; + hw->ops.ioctl = snd_sb_csp_ioctl; + hw->ops.release = snd_sb_csp_release; + + /* create a proc entry */ + init_proc_entry(p, device); + if (rhwdep) + *rhwdep = hw; + return 0; +} + +/* + * free_private for hwdep instance + */ +static void snd_sb_csp_free(snd_hwdep_t *hwdep) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hwdep->private_data, return); + if (p) { + if (p->running & SNDRV_SB_CSP_ST_RUNNING) + snd_sb_csp_stop(p); + snd_magic_kfree(p); + } +} + +/* ------------------------------ */ + +/* + * open the device exclusively + */ +static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + return (snd_sb_csp_use(p)); +} + +/* + * ioctl for hwdep device: + */ +static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + snd_sb_csp_info_t info; + snd_sb_csp_start_t start_info; + int err; + + snd_assert(p != NULL, return -EINVAL); + + if (snd_sb_csp_check_version(p)) + return -ENODEV; + + switch (cmd) { + /* get information */ + case SNDRV_SB_CSP_IOCTL_INFO: + *info.codec_name = *p->codec_name; + info.func_nr = p->func_nr; + info.acc_format = p->acc_format; + info.acc_channels = p->acc_channels; + info.acc_width = p->acc_width; + info.acc_rates = p->acc_rates; + info.csp_mode = p->mode; + info.run_channels = p->run_channels; + info.run_width = p->run_width; + info.version = p->version; + info.state = p->running; + if (copy_to_user((void *) arg, &info, sizeof(info))) + err = -EFAULT; + else + err = 0; + break; + + /* load CSP microcode */ + case SNDRV_SB_CSP_IOCTL_LOAD_CODE: + err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? + -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t *) arg)); + break; + case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE: + err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? + -EBUSY : snd_sb_csp_unload(p)); + break; + + /* change CSP running state */ + case SNDRV_SB_CSP_IOCTL_START: + if (copy_from_user(&start_info, (void *) arg, sizeof(start_info))) + err = -EFAULT; + else + err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels); + break; + case SNDRV_SB_CSP_IOCTL_STOP: + err = snd_sb_csp_stop(p); + break; + case SNDRV_SB_CSP_IOCTL_PAUSE: + err = snd_sb_csp_pause(p); + break; + case SNDRV_SB_CSP_IOCTL_RESTART: + err = snd_sb_csp_restart(p); + break; + default: + err = -ENOTTY; + break; + } + + return err; +} + +/* + * close the device + */ +static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + return (snd_sb_csp_unuse(p)); +} + +/* ------------------------------ */ + +/* + * acquire device + */ +static int snd_sb_csp_use(snd_sb_csp_t * p) +{ + down(&p->access_mutex); + if (p->used) { + up(&p->access_mutex); + return -EAGAIN; + } + p->used++; + up(&p->access_mutex); + + return 0; + +} + +/* + * release device + */ +static int snd_sb_csp_unuse(snd_sb_csp_t * p) +{ + down(&p->access_mutex); + p->used--; + up(&p->access_mutex); + + return 0; +} + +/* + * load microcode via ioctl: + * code is user-space pointer + */ +static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * mcode) +{ + snd_sb_csp_mc_header_t info; + + unsigned char *data_ptr, *data_end; + unsigned short func_nr = 0; + + riff_header_t file_h, item_h, code_h; + __u32 item_type; + desc_header_t funcdesc_h; + + unsigned long flags; + int err; + + if (copy_from_user(&info, mcode, sizeof(info))) + return -EFAULT; + data_ptr = mcode->data; + + if (copy_from_user(&file_h, data_ptr, sizeof(file_h))) + return -EFAULT; + if ((file_h.name != RIFF_HEADER) || + (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { + snd_printd("%s: Invalid RIFF header\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof(file_h); + data_end = data_ptr + LE_INT(file_h.len); + + if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) + return -EFAULT; + if (item_type != CSP__HEADER) { + snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof (item_type); + + for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) { + if (copy_from_user(&item_h, data_ptr, sizeof(item_h))) + return -EFAULT; + data_ptr += sizeof(item_h); + if (item_h.name != LIST_HEADER) + continue; + + if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) + return -EFAULT; + switch (item_type) { + case FUNC_HEADER: + if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h))) + return -EFAULT; + func_nr = LE_SHORT(funcdesc_h.func_nr); + break; + case CODE_HEADER: + if (func_nr != info.func_req) + break; /* not required function, try next */ + data_ptr += sizeof(item_type); + + /* destroy QSound mixer element */ + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_sb_qsound_destroy(p); + } + /* Clear all flags */ + p->running = 0; + p->mode = 0; + + /* load microcode blocks */ + for (;;) { + if (data_ptr >= data_end) + return -EINVAL; + if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) + return -EFAULT; + + /* init microcode blocks */ + if (code_h.name != INIT_HEADER) + break; + data_ptr += sizeof(code_h); + err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len), + SNDRV_SB_CSP_LOAD_INITBLOCK | SNDRV_SB_CSP_LOAD_FROMUSER); + if (err) + return err; + data_ptr += LE_INT(code_h.len); + } + /* main microcode block */ + if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) + return -EFAULT; + + if (code_h.name != MAIN_HEADER) { + snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof(code_h); + err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len), + SNDRV_SB_CSP_LOAD_FROMUSER); + if (err) + return err; + + /* fill in codec header */ + strncpy(p->codec_name, info.codec_name, sizeof(p->codec_name) - 1); + p->codec_name[sizeof(p->codec_name) - 1] = 0; + p->func_nr = func_nr; + p->mode = LE_SHORT(funcdesc_h.flags_play_rec); + switch (LE_SHORT(funcdesc_h.VOC_type)) { + case 0x0001: /* QSound decoder */ + if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) { + if (snd_sb_qsound_build(p) == 0) + /* set QSound flag and clear all other mode flags */ + p->mode = SNDRV_SB_CSP_MODE_QSOUND; + } + p->acc_format = 0; + break; + case 0x0006: /* A Law codec */ + p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; + break; + case 0x0007: /* Mu Law codec */ + p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; + break; + case 0x0011: /* what Creative thinks is IMA ADPCM codec */ + case 0x0200: /* Creative ADPCM codec */ + p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + case 201: /* Text 2 Speech decoder */ + /* TODO: Text2Speech handling routines */ + p->acc_format = 0; + break; + case 0x0202: /* Fast Speech 8 codec */ + case 0x0203: /* Fast Speech 10 codec */ + p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL; + break; + default: /* other codecs are unsupported */ + p->acc_format = p->acc_width = p->acc_rates = 0; + p->mode = 0; + snd_printd("%s: Unsupported CSP codec type: 0x%04x\n", + __FUNCTION__, + LE_SHORT(funcdesc_h.VOC_type)); + return -EINVAL; + } + p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono); + p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit); + p->acc_rates = LE_SHORT(funcdesc_h.flags_rates); + + /* Decouple CSP from IRQ and DMAREQ lines */ + spin_lock_irqsave(&p->chip->reg_lock, flags); + set_mode_register(p->chip, 0xfc); + set_mode_register(p->chip, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + + /* finished loading successfully */ + p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */ + return 0; + } + } + snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req); + return -EINVAL; +} + +/* + * unload CSP microcode + */ +static int snd_sb_csp_unload(snd_sb_csp_t * p) +{ + if (p->running & SNDRV_SB_CSP_ST_RUNNING) + return -EBUSY; + if (!(p->running & SNDRV_SB_CSP_ST_LOADED)) + return -ENXIO; + + /* clear supported formats */ + p->acc_format = 0; + p->acc_channels = p->acc_width = p->acc_rates = 0; + /* destroy QSound mixer element */ + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_sb_qsound_destroy(p); + } + /* clear all flags */ + p->running = 0; + p->mode = 0; + return 0; +} + +/* + * send command sequence to DSP + */ +static inline int command_seq(sb_t *chip, const unsigned char *seq, int size) +{ + int i; + for (i = 0; i < size; i++) { + if (!snd_sbdsp_command(chip, seq[i])) + return -EIO; + } + return 0; +} + +/* + * set CSP codec parameter + */ +static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val) +{ + unsigned char dsp_cmd[3]; + + dsp_cmd[0] = 0x05; /* CSP set codec parameter */ + dsp_cmd[1] = val; /* Parameter value */ + dsp_cmd[2] = par; /* Parameter */ + command_seq(chip, dsp_cmd, 3); + snd_sbdsp_command(chip, 0x03); /* DSP read? */ + if (snd_sbdsp_get_byte(chip) != par) + return -EIO; + return 0; +} + +/* + * set CSP register + */ +static int set_register(sb_t *chip, unsigned char reg, unsigned char val) +{ + unsigned char dsp_cmd[3]; + + dsp_cmd[0] = 0x0e; /* CSP set register */ + dsp_cmd[1] = reg; /* CSP Register */ + dsp_cmd[2] = val; /* value */ + return command_seq(chip, dsp_cmd, 3); +} + +/* + * read CSP register + * return < 0 -> error + */ +static int read_register(sb_t *chip, unsigned char reg) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x0f; /* CSP read register */ + dsp_cmd[1] = reg; /* CSP Register */ + command_seq(chip, dsp_cmd, 2); + return snd_sbdsp_get_byte(chip); /* Read DSP value */ +} + +/* + * set CSP mode register + */ +static int set_mode_register(sb_t *chip, unsigned char mode) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x04; /* CSP set mode register */ + dsp_cmd[1] = mode; /* mode */ + return command_seq(chip, dsp_cmd, 2); +} + +/* + * Detect CSP + * return 0 if CSP exists. + */ +static int csp_detect(sb_t *chip, int *version) +{ + unsigned char csp_test1, csp_test2; + unsigned long flags; + int result = -ENODEV; + + spin_lock_irqsave(&chip->reg_lock, flags); + + set_codec_parameter(chip, 0x00, 0x00); + set_mode_register(chip, 0xfc); /* 0xfc = ?? */ + + csp_test1 = read_register(chip, 0x83); + set_register(chip, 0x83, ~csp_test1); + csp_test2 = read_register(chip, 0x83); + if (csp_test2 != (csp_test1 ^ 0xff)) + goto __fail; + + set_register(chip, 0x83, csp_test1); + csp_test2 = read_register(chip, 0x83); + if (csp_test2 != csp_test1) + goto __fail; + + set_mode_register(chip, 0x00); /* 0x00 = ? */ + + *version = get_version(chip); + snd_sbdsp_reset(chip); /* reset DSP after getversion! */ + if (*version >= 0x10 && *version <= 0x1f) + result = 0; /* valid version id */ + + __fail: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* + * get CSP version number + */ +static int get_version(sb_t *chip) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x08; /* SB_DSP_!something! */ + dsp_cmd[1] = 0x03; /* get chip version id? */ + command_seq(chip, dsp_cmd, 2); + + return (snd_sbdsp_get_byte(chip)); +} + +/* + * check if the CSP version is valid + */ +static int snd_sb_csp_check_version(snd_sb_csp_t * p) +{ + if (p->version < 0x10 || p->version > 0x1f) { + snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version); + return 1; + } + return 0; +} + +/* + * download microcode to CSP (microcode should have one "main" block). + */ +static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags) +{ + int status, i; + int err; + int result = -EIO; + unsigned long flags; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + snd_sbdsp_command(p->chip, 0x01); /* CSP download command */ + if (snd_sbdsp_get_byte(p->chip)) { + snd_printd("%s: Download command failed\n", __FUNCTION__); + goto __fail; + } + /* Send CSP low byte (size - 1) */ + snd_sbdsp_command(p->chip, (unsigned char)(size - 1)); + /* Send high byte */ + snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8)); + /* send microcode sequence */ + if (load_flags & SNDRV_SB_CSP_LOAD_FROMUSER) { + /* copy microcode from user space */ + unsigned char *kbuf, *_kbuf; + _kbuf = kbuf = kmalloc (size, GFP_KERNEL); + if (copy_from_user(kbuf, buf, size)) { + result = -EFAULT; + kfree (_kbuf); + goto __fail; + } + while (size--) { + if (!snd_sbdsp_command(p->chip, *kbuf++)) { + kfree (_kbuf); + goto __fail; + } + } + kfree (_kbuf); + } else { + /* load from kernel space */ + while (size--) { + if (!snd_sbdsp_command(p->chip, *buf++)) + goto __fail; + } + } + if (snd_sbdsp_get_byte(p->chip)) + goto __fail; + + if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) { + i = 0; + /* some codecs (FastSpeech) take some time to initialize */ + while (1) { + snd_sbdsp_command(p->chip, 0x03); + status = snd_sbdsp_get_byte(p->chip); + if (status == 0x55 || ++i >= 10) + break; + udelay (10); + } + if (status != 0x55) { + snd_printd("%s: Microcode initialization failed\n", __FUNCTION__); + goto __fail; + } + } else { + /* + * Read mixer register SB_DSP4_DMASETUP after loading 'main' code. + * Start CSP chip if no 16bit DMA channel is set - some kind + * of autorun or perhaps a bugfix? + */ + spin_lock(&p->chip->mixer_lock); + status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP); + spin_unlock(&p->chip->mixer_lock); + if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) { + err = (set_codec_parameter(p->chip, 0xaa, 0x00) || + set_codec_parameter(p->chip, 0xff, 0x00)); + snd_sbdsp_reset(p->chip); /* really! */ + if (err) + goto __fail; + set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + set_mode_register(p->chip, 0x70); /* 70 = RUN */ + } + } + result = 0; + + __fail: + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + return result; +} + +#include "sb16_csp_codecs.h" + +/* + * autoload hardware codec if necessary + * return 0 if CSP is loaded and ready to run (p->running != 0) + */ +static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode) +{ + unsigned long flags; + int err = 0; + + /* if CSP is running or manually loaded then exit */ + if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED)) + return -EBUSY; + + /* autoload microcode only if requested hardware codec is not already loaded */ + if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) { + p->running = SNDRV_SB_CSP_ST_AUTO; + } else { + switch (pcm_sfmt) { + case SNDRV_PCM_FORMAT_MU_LAW: + err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0); + p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; + p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; + break; + case SNDRV_PCM_FORMAT_A_LAW: + err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0); + p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; + p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; + break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init), + SNDRV_SB_CSP_LOAD_INITBLOCK); + if (err) + break; + if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) { + err = snd_sb_csp_load(p, &ima_adpcm_playback[0], + sizeof(ima_adpcm_playback), 0); + p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE; + } else { + err = snd_sb_csp_load(p, &ima_adpcm_capture[0], + sizeof(ima_adpcm_capture), 0); + p->mode = SNDRV_SB_CSP_MODE_DSP_READ; + } + p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + default: + /* Decouple CSP from IRQ and DMAREQ lines */ + if (p->running & SNDRV_SB_CSP_ST_AUTO) { + spin_lock_irqsave(&p->chip->reg_lock, flags); + set_mode_register(p->chip, 0xfc); + set_mode_register(p->chip, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + p->running = 0; /* clear autoloaded flag */ + } + return -EINVAL; + } + if (err) { + p->acc_format = 0; + p->acc_channels = p->acc_width = p->acc_rates = 0; + + p->running = 0; /* clear autoloaded flag */ + p->mode = 0; + return (err); + } else { + p->running = SNDRV_SB_CSP_ST_AUTO; /* set autoloaded flag */ + p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT; /* only 16 bit data */ + p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO; + p->acc_rates = SNDRV_SB_CSP_RATE_ALL; /* HW codecs accept all rates */ + } + + } + return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO; +} + +/* + * start CSP + */ +static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels) +{ + unsigned char s_type; /* sample type */ + unsigned char mixL, mixR; + int result = -EIO; + unsigned long flags; + + if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) { + snd_printd("%s: Microcode not loaded\n", __FUNCTION__); + return -ENXIO; + } + if (p->running & SNDRV_SB_CSP_ST_RUNNING) { + snd_printd("%s: CSP already running\n", __FUNCTION__); + return -EBUSY; + } + if (!(sample_width & p->acc_width)) { + snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__); + return -EINVAL; + } + if (!(channels & p->acc_channels)) { + snd_printd("%s: Invalid number of channels\n", __FUNCTION__); + return -EINVAL; + } + + /* Mute PCM volume */ + spin_lock_irqsave(&p->chip->mixer_lock, flags); + mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); + mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); + + spin_lock(&p->chip->reg_lock); + set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + set_mode_register(p->chip, 0x70); /* 70 = RUN */ + + s_type = 0x00; + if (channels == SNDRV_SB_CSP_MONO) + s_type = 0x11; /* 000n 000n (n = 1 if mono) */ + if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT) + s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */ + + if (set_codec_parameter(p->chip, 0x81, s_type)) { + snd_printd("%s: Set sample type command failed\n", __FUNCTION__); + goto __fail; + } + if (set_codec_parameter(p->chip, 0x80, 0x00)) { + snd_printd("%s: Codec start command failed\n", __FUNCTION__); + goto __fail; + } + p->run_width = sample_width; + p->run_channels = channels; + + p->running |= SNDRV_SB_CSP_ST_RUNNING; + + if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* enable QSound decoder */ + set_codec_parameter(p->chip, 0x00, 0xff); + set_codec_parameter(p->chip, 0x01, 0xff); + p->running |= SNDRV_SB_CSP_ST_QSOUND; + /* set QSound startup value */ + snd_sb_csp_qsound_transfer(p); + } + result = 0; + + __fail: + spin_unlock(&p->chip->reg_lock); + + /* restore PCM volume */ + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); + spin_unlock_irqrestore(&p->chip->mixer_lock, flags); + + return result; +} + +/* + * stop CSP + */ +static int snd_sb_csp_stop(snd_sb_csp_t * p) +{ + int result; + unsigned char mixL, mixR; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) + return 0; + + /* Mute PCM volume */ + spin_lock_irqsave(&p->chip->mixer_lock, flags); + mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); + mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); + + spin_lock(&p->chip->reg_lock); + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* disable QSound decoder */ + set_codec_parameter(p->chip, 0x00, 0x00); + set_codec_parameter(p->chip, 0x01, 0x00); + + p->running &= ~SNDRV_SB_CSP_ST_QSOUND; + } + result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + spin_unlock(&p->chip->reg_lock); + + /* restore PCM volume */ + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); + spin_unlock_irqrestore(&p->chip->mixer_lock, flags); + + if (!(result)) + p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING); + return result; +} + +/* + * pause CSP codec and hold DMA transfer + */ +static int snd_sb_csp_pause(snd_sb_csp_t * p) +{ + int result; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) + return -EBUSY; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + result = set_codec_parameter(p->chip, 0x80, 0xff); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + if (!(result)) + p->running |= SNDRV_SB_CSP_ST_PAUSED; + + return result; +} + +/* + * restart CSP codec and resume DMA transfer + */ +static int snd_sb_csp_restart(snd_sb_csp_t * p) +{ + int result; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_PAUSED)) + return -EBUSY; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + result = set_codec_parameter(p->chip, 0x80, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + if (!(result)) + p->running &= ~SNDRV_SB_CSP_ST_PAUSED; + + return result; +} + +/* ------------------------------ */ + +/* + * QSound mixer control for PCM + */ + +static int snd_sb_qsound_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0; + return 0; +} + +static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval; + + nval = ucontrol->value.integer.value[0] & 0x01; + spin_lock_irqsave(&p->q_lock, flags); + change = p->q_enabled != nval; + p->q_enabled = nval; + spin_unlock_irqrestore(&p->q_lock, flags); + return change; +} + +static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + return 0; +} + +static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&p->q_lock, flags); + ucontrol->value.integer.value[0] = p->qpos_left; + ucontrol->value.integer.value[1] = p->qpos_right; + spin_unlock_irqrestore(&p->q_lock, flags); + return 0; +} + +static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval1, nval2; + + nval1 = ucontrol->value.integer.value[0]; + if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) + nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + nval2 = ucontrol->value.integer.value[1]; + if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) + nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + spin_lock_irqsave(&p->q_lock, flags); + change = p->qpos_left != nval1 || p->qpos_right != nval2; + p->qpos_left = nval1; + p->qpos_right = nval2; + p->qpos_changed = change; + spin_unlock_irqrestore(&p->q_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_sb_qsound_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "3D Control - Switch", + .info = snd_sb_qsound_switch_info, + .get = snd_sb_qsound_switch_get, + .put = snd_sb_qsound_switch_put +}; + +static snd_kcontrol_new_t snd_sb_qsound_space = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "3D Control - Space", + .info = snd_sb_qsound_space_info, + .get = snd_sb_qsound_space_get, + .put = snd_sb_qsound_space_put +}; + +static int snd_sb_qsound_build(snd_sb_csp_t * p) +{ + snd_card_t * card; + int err; + + snd_assert(p != NULL, return -EINVAL); + + card = p->chip->card; + p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2; + p->qpos_changed = 0; + + spin_lock_init(&p->q_lock); + + if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0) + goto __error; + if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0) + goto __error; + + return 0; + + __error: + snd_sb_qsound_destroy(p); + return err; +} + +static void snd_sb_qsound_destroy(snd_sb_csp_t * p) +{ + snd_card_t * card; + unsigned long flags; + + snd_assert(p != NULL, return); + + card = p->chip->card; + + if (p->qsound_switch) + snd_ctl_remove(card, p->qsound_switch); + if (p->qsound_space) + snd_ctl_remove(card, p->qsound_space); + + /* cancel pending transfer of QSound parameters */ + spin_lock_irqsave (&p->q_lock, flags); + p->qpos_changed = 0; + spin_unlock_irqrestore (&p->q_lock, flags); +} + +/* + * Transfer qsound parameters to CSP, + * function should be called from interrupt routine + */ +static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p) +{ + int err = -ENXIO; + + spin_lock(&p->q_lock); + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* left channel */ + set_codec_parameter(p->chip, 0x00, p->qpos_left); + set_codec_parameter(p->chip, 0x02, 0x00); + /* right channel */ + set_codec_parameter(p->chip, 0x00, p->qpos_right); + set_codec_parameter(p->chip, 0x03, 0x00); + err = 0; + } + p->qpos_changed = 0; + spin_unlock(&p->q_lock); + return err; +} + +/* ------------------------------ */ + +/* + * proc interface + */ +static int init_proc_entry(snd_sb_csp_t * p, int device) +{ + char name[16]; + snd_info_entry_t *entry; + sprintf(name, "cspD%d", device); + if (! snd_card_proc_new(p->chip->card, name, &entry)) + snd_info_set_text_ops(entry, p, info_read); + return 0; +} + +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return); + + snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f)); + snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'), + ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'), + ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'), + ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-')); + if (p->running & SNDRV_SB_CSP_ST_LOADED) { + snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr); + snd_iprintf(buffer, "Sample rates: "); + if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) { + snd_iprintf(buffer, "All\n"); + } else { + snd_iprintf(buffer, "%s%s%s%s\n", + ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : "")); + } + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_iprintf(buffer, "QSound decoder %sabled\n", + p->q_enabled ? "en" : "dis"); + } else { + snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n", + p->acc_format, + ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"), + ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"), + ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"), + ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"), + ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"), + ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-")); + } + } + if (p->running & SNDRV_SB_CSP_ST_AUTO) { + snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n"); + } + if (p->running & SNDRV_SB_CSP_ST_RUNNING) { + snd_iprintf(buffer, "Processing %dbit %s PCM samples\n", + ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8), + ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo")); + } + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n", + p->qpos_left, p->qpos_right); + } +} + +/* */ + +EXPORT_SYMBOL(snd_sb_csp_new); + +/* + * INIT part + */ + +static int __init alsa_sb_csp_init(void) +{ + return 0; +} + +static void __exit alsa_sb_csp_exit(void) +{ +} + +module_init(alsa_sb_csp_init) +module_exit(alsa_sb_csp_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb16_csp_codecs.h linux/sound/isa/sb/sb16_csp_codecs.h --- linux-2.4.21-rc1.orig/sound/isa/sb/sb16_csp_codecs.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb16_csp_codecs.h 2001-12-30 02:26:46.000000000 -0700 @@ -0,0 +1,949 @@ +/* + * Copyright (c) 1994 Creative Technology Ltd. + * Microcode files for SB16 Advanced Signal Processor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static unsigned char mulaw_main[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, + 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, + 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, + 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, + 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, + 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, + 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, + 0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, + 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, + 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, + 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, + 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, + 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, + 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, + 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, + 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, + 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, + 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, + 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, + 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, + 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, + 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, + 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, + 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, + 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, + 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, + 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, + 0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54, + 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, + 0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, + 0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, + 0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, + 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b, + 0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, + 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, + 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, + 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, + 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, + 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, + 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, + 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, + 0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c, + 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, + 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, + 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, + 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, + 0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80, + 0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39, + 0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, + 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, + 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, + 0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, + 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, + 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, + 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, + 0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, + 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, + 0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, + 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, + 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, + 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, + 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, + 0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0, + 0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, + 0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c, + 0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e, + 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, + 0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18, + 0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10, + 0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10, + 0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10, + 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, + 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, + 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, + 0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54, + 0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44, + 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54, + 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, + 0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54, + 0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44, + 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54, + 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00 +}; + +static unsigned char alaw_main[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, + 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, + 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, + 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, + 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, + 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, + 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, + 0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, + 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, + 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, + 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, + 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, + 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, + 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, + 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, + 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, + 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, + 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, + 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, + 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, + 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, + 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, + 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, + 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, + 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, + 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, + 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, + 0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54, + 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, + 0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, + 0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, + 0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, + 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b, + 0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80, + 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, + 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, + 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, + 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, + 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, + 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, + 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, + 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, + 0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c, + 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, + 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, + 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, + 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, + 0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49, + 0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39, + 0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49, + 0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39, + 0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, + 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, + 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, + 0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, + 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, + 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, + 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, + 0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, + 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, + 0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, + 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, + 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, + 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, + 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, + 0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0, + 0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44, + 0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c, + 0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e, + 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, + 0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10, + 0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b, + 0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80, + 0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80, + 0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19, + 0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49, + 0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18, + 0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb, + 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80, + 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, + 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, + 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, + 0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54, + 0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44, + 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54, + 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, + 0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54, + 0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44, + 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54, + 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00 +}; + + +static unsigned char ima_adpcm_init[] = { + 0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e, + 0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19, + 0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19, + 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, + 0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49, + 0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00 +}; + +static unsigned char ima_adpcm_playback[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44, + 0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80, + 0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b, + 0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b, + 0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, + 0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14, + 0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8, + 0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49, + 0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, + 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, + 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, + 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, + 0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4, + 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, + 0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, + 0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, + 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, + 0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, + 0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, + 0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, + 0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, + 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, + 0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, + 0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, + 0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, + 0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, + 0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, + 0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, + 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, + 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, + 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, + 0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, + 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, + 0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, + 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, + 0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, + 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, + 0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, + 0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84, + 0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39, + 0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19, + 0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19, + 0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19, + 0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54, + 0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84, + 0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49, + 0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a, + 0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19, + 0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b, + 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19, + 0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39, + 0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a, + 0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, + 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, + 0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11, + 0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10, + 0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18, + 0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44, + 0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4, + 0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19, + 0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, + 0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, + 0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19, + 0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, + 0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a, + 0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39, + 0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39, + 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, + 0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e, + 0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44, + 0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, + 0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b, + 0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80, + 0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e, + 0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf, + 0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b, + 0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b, + 0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b, + 0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80, + 0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b, + 0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80, + 0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb, + 0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19, + 0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a, + 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44, + 0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44, + 0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60, + 0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80, + 0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2, + 0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22, + 0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b, + 0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a, + 0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02, + 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60, + 0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82, + 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb, + 0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b, + 0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f, + 0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a, + 0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, + 0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, + 0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a, + 0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, + 0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, + 0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39, + 0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a, + 0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19, + 0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44, + 0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf, + 0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80, + 0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22, + 0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82, + 0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82, + 0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, + 0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80, + 0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a, + 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80, + 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00 +}; + +static unsigned char ima_adpcm_capture[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44, + 0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b, + 0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19, + 0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b, + 0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b, + 0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19, + 0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19, + 0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, + 0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14, + 0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8, + 0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18, + 0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19, + 0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8, + 0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8, + 0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, + 0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a, + 0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, + 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, + 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, + 0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4, + 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, + 0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, + 0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, + 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, + 0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, + 0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, + 0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, + 0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, + 0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, + 0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, + 0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, + 0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, + 0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, + 0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, + 0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, + 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, + 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, + 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, + 0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, + 0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, + 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, + 0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, + 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, + 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, + 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, + 0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, + 0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84, + 0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19, + 0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19, + 0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19, + 0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, + 0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14, + 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80, + 0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49, + 0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80, + 0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b, + 0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44, + 0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44, + 0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, + 0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4, + 0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e, + 0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84, + 0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10, + 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10, + 0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80, + 0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4, + 0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4, + 0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4, + 0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44, + 0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18, + 0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, + 0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19, + 0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19, + 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, + 0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02, + 0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, + 0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f, + 0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44, + 0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44, + 0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f, + 0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b, + 0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b, + 0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80, + 0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84, + 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a, + 0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44, + 0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18, + 0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb, + 0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a, + 0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8, + 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, + 0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, + 0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44, + 0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80, + 0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, + 0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02, + 0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44, + 0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f, + 0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82, + 0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f, + 0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80, + 0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14, + 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, + 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, + 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, + 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60, + 0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0, + 0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14, + 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, + 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, + 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, + 0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80, + 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, + 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46, + 0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14, + 0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a, + 0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19, + 0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a, + 0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19, + 0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a, + 0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19, + 0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a, + 0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19, + 0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a, + 0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19, + 0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a, + 0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19, + 0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a, + 0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19, + 0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a, + 0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19, + 0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b, + 0xa6, 0xc5, 0x11, 0x00 +}; diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb16_main.c linux/sound/isa/sb/sb16_main.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb16_main.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb16_main.c 2003-01-31 08:19:59.000000000 -0700 @@ -0,0 +1,915 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of 16-bit SoundBlaster cards and clones + * Note: This is very ugly hardware which uses one 8-bit DMA channel and + * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't + * transfer 16-bit samples and 16-bit DMA channels can't transfer + * 8-bit samples. This make full duplex more complicated than + * can be... People, don't buy these soundcards for full 16-bit + * duplex!!! + * Note: 16-bit wide is assigned to first direction which made request. + * With full duplex - playback is preferred with abstract layer. + * + * Note: Some chip revisions have hardware bug. Changing capture + * channel from full-duplex 8bit DMA to 16bit DMA will block + * 16bit DMA transfers from DSP chip (capture) until 8bit transfer + * to DSP chip (playback) starts. This bug can be avoided with + * "16bit DMA Allocation" setting set to Playback or Capture. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones"); +MODULE_LICENSE("GPL"); + +#define chip_t sb_t + +#ifdef CONFIG_SND_SB16_CSP +static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && + ((1U << runtime->format) == csp->acc_format)) { + /* Supported runtime PCM format for playback */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { + /* QSound decoder is loaded and enabled */ + if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { + /* Only for simple PCM formats */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } + } + } else if (csp->ops.csp_use(csp) == 0) { + /* Acquire CSP and try to autoload hardware codec */ + if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { + /* Unsupported format, release CSP */ + csp->ops.csp_unuse(csp); + } else { + __start_CSP: + /* Try to start CSP */ + if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? + SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, + (runtime->channels > 1) ? + SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { + /* Failed, release CSP */ + csp->ops.csp_unuse(csp); + } else { + /* Success, CSP acquired and running */ + chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; + } + } + } + } +} + +static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && + ((1U << runtime->format) == csp->acc_format)) { + /* Supported runtime PCM format for capture */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } + } else if (csp->ops.csp_use(csp) == 0) { + /* Acquire CSP and try to autoload hardware codec */ + if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { + /* Unsupported format, release CSP */ + csp->ops.csp_unuse(csp); + } else { + __start_CSP: + /* Try to start CSP */ + if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? + SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, + (runtime->channels > 1) ? + SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { + /* Failed, release CSP */ + csp->ops.csp_unuse(csp); + } else { + /* Success, CSP acquired and running */ + chip->open = SNDRV_SB_CSP_MODE_DSP_READ; + } + } + } + } +} + +static void snd_sb16_csp_update(sb_t *chip) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->qpos_changed) { + spin_lock(&chip->reg_lock); + csp->ops.csp_qsound_transfer (csp); + spin_unlock(&chip->reg_lock); + } + } +} + +static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + /* CSP decoders (QSound excluded) support only 16bit transfers */ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { + runtime->hw.formats |= csp->acc_format; + } + } else { + /* autoloaded codecs */ + runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM; + } + } +} + +static void snd_sb16_csp_playback_close(sb_t *chip) +{ + if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->ops.csp_stop(csp) == 0) { + csp->ops.csp_unuse(csp); + chip->open = 0; + } + } +} + +static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + /* CSP coders support only 16bit transfers */ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { + runtime->hw.formats |= csp->acc_format; + } + } else { + /* autoloaded codecs */ + runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM; + } + } +} + +static void snd_sb16_csp_capture_close(sb_t *chip) +{ + if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->ops.csp_stop(csp) == 0) { + csp->ops.csp_unuse(csp); + chip->open = 0; + } + } +} +#else +#define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ +#define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ +#define snd_sb16_csp_update(chip) /*nop*/ +#define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ +#define snd_sb16_csp_playback_close(chip) /*nop*/ +#define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ +#define snd_sb16_csp_capture_close(chip) /*nop*/ +#endif + + +static void snd_sb16_setup_rate(sb_t *chip, + unsigned short rate, + int channel) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) + snd_sb_ack_16bit(chip); + else + snd_sb_ack_8bit(chip); + if (!(chip->mode & SB_RATE_LOCK)) { + chip->locked_rate = rate; + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); + snd_sbdsp_command(chip, rate >> 8); + snd_sbdsp_command(chip, rate & 0xff); + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); + snd_sbdsp_command(chip, rate >> 8); + snd_sbdsp_command(chip, rate & 0xff); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int snd_sb16_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sb16_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char format; + unsigned int size, count, dma; + + snd_sb16_csp_playback_prepare(chip, runtime); + if (snd_pcm_format_unsigned(runtime->format) > 0) { + format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; + } else { + format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; + } + + snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); + size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); + dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; + snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & SB_MODE_PLAYBACK_16) { + count >>= 1; + count--; + snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + } else { + count--; + snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->mode |= SB_RATE_LOCK_PLAYBACK; + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); + /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ + if (chip->mode & SB_RATE_LOCK_CAPTURE) + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + chip->mode &= ~SB_RATE_LOCK_PLAYBACK; + break; + default: + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char format; + unsigned int size, count, dma; + + snd_sb16_csp_capture_prepare(chip, runtime); + if (snd_pcm_format_unsigned(runtime->format) > 0) { + format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; + } else { + format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; + } + snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); + size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; + snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + + count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & SB_MODE_CAPTURE_16) { + count >>= 1; + count--; + snd_sbdsp_command(chip, SB_DSP4_IN16_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + } else { + count--; + snd_sbdsp_command(chip, SB_DSP4_IN8_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); + /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ + if (chip->mode & SB_RATE_LOCK_PLAYBACK) + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + break; + default: + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + unsigned char status; + int ok; + + spin_lock(&chip->mixer_lock); + status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); + spin_unlock(&chip->mixer_lock); + if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback) + chip->rmidi_callback(irq, chip->rmidi->private_data, regs); + if (status & SB_IRQTYPE_8BIT) { + ok = 0; + if (chip->mode & SB_MODE_PLAYBACK_8) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_sb16_csp_update(chip); + ok++; + } + if (chip->mode & SB_MODE_CAPTURE_8) { + snd_pcm_period_elapsed(chip->capture_substream); + ok++; + } + spin_lock(&chip->reg_lock); + if (!ok) + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + snd_sb_ack_8bit(chip); + spin_unlock(&chip->reg_lock); + } + if (status & SB_IRQTYPE_16BIT) { + ok = 0; + if (chip->mode & SB_MODE_PLAYBACK_16) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_sb16_csp_update(chip); + ok++; + } + if (chip->mode & SB_MODE_CAPTURE_16) { + snd_pcm_period_elapsed(chip->capture_substream); + ok++; + } + spin_lock(&chip->reg_lock); + if (!ok) + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + snd_sb_ack_16bit(chip); + spin_unlock(&chip->reg_lock); + } +} + +/* + + */ + +static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int dma; + size_t ptr; + + dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; + ptr = snd_dma_pointer(dma, chip->p_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int dma; + size_t ptr; + + dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; + ptr = snd_dma_pointer(dma, chip->c_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_sb16_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = 0, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + .rate_min = 4000, + .rate_max = 44100, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_sb16_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = 0, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + .rate_min = 4000, + .rate_max = 44100, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * open/close + */ + +int snd_sb16_playback_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->mode & SB_MODE_PLAYBACK) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + runtime->hw = snd_sb16_playback; + + /* skip if 16 bit DMA was reserved for capture */ + if (chip->force_mode16 & SB_MODE_CAPTURE_16) + goto __skip_16bit; + + if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) { + chip->mode |= SB_MODE_PLAYBACK_16; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + /* Vibra16X hack */ + if (chip->dma16 <= 3) { + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + } else { + snd_sb16_csp_playback_open(chip, runtime); + } + goto __open_ok; + } + + __skip_16bit: + if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) { + chip->mode |= SB_MODE_PLAYBACK_8; + /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ + if (chip->dma16 < 0) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + chip->mode |= SB_MODE_PLAYBACK_16; + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + goto __open_ok; + } + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + + __open_ok: + if (chip->hardware == SB_HW_ALS100) + runtime->hw.rate_max = 48000; + if (chip->mode & SB_RATE_LOCK) + runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; + chip->playback_substream = substream; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_playback_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + snd_sb16_csp_playback_close(chip); + spin_lock_irqsave(&chip->open_lock, flags); + chip->playback_substream = NULL; + chip->mode &= ~SB_MODE_PLAYBACK; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_capture_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->mode & SB_MODE_CAPTURE) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + runtime->hw = snd_sb16_capture; + + /* skip if 16 bit DMA was reserved for playback */ + if (chip->force_mode16 & SB_MODE_PLAYBACK_16) + goto __skip_16bit; + + if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { + chip->mode |= SB_MODE_CAPTURE_16; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + /* Vibra16X hack */ + if (chip->dma16 <= 3) { + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + } else { + snd_sb16_csp_capture_open(chip, runtime); + } + goto __open_ok; + } + + __skip_16bit: + if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { + chip->mode |= SB_MODE_CAPTURE_8; + /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ + if (chip->dma16 < 0) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + chip->mode |= SB_MODE_CAPTURE_16; + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + goto __open_ok; + } + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + + __open_ok: + if (chip->hardware == SB_HW_ALS100) + runtime->hw.rate_max = 48000; + if (chip->mode & SB_RATE_LOCK) + runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; + chip->capture_substream = substream; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + snd_sb16_csp_capture_close(chip); + spin_lock_irqsave(&chip->open_lock, flags); + chip->capture_substream = NULL; + chip->mode &= ~SB_MODE_CAPTURE; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +/* + * DMA control interface + */ + +static int snd_sb16_set_dma_mode(sb_t *chip, int what) +{ + if (chip->dma8 < 0 || chip->dma16 < 0) { + snd_assert(what == 0, return -EINVAL); + return 0; + } + if (what == 0) { + chip->force_mode16 = 0; + } else if (what == 1) { + chip->force_mode16 = SB_MODE_PLAYBACK_16; + } else if (what == 2) { + chip->force_mode16 = SB_MODE_CAPTURE_16; + } else { + return -EINVAL; + } + return 0; +} + +static int snd_sb16_get_dma_mode(sb_t *chip) +{ + if (chip->dma8 < 0 || chip->dma16 < 0) + return 0; + switch (chip->force_mode16) { + case SB_MODE_PLAYBACK_16: + return 1; + case SB_MODE_CAPTURE_16: + return 2; + default: + return 0; + } +} + +static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Auto", "Playback", "Capture" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char nval, oval; + int change; + + if ((nval = ucontrol->value.enumerated.item[0]) > 2) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_sb16_get_dma_mode(chip); + change = nval != oval; + snd_sb16_set_dma_mode(chip, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +snd_kcontrol_new_t snd_sb16_dma_control = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "16-bit DMA Allocation", + .info = snd_sb16_dma_control_info, + .get = snd_sb16_dma_control_get, + .put = snd_sb16_dma_control_put +}; + +/* + * Initialization part + */ + +int snd_sb16dsp_configure(sb_t * chip) +{ + unsigned long flags; + unsigned char irqreg = 0, dmareg = 0, mpureg; + unsigned char realirq, realdma, realmpureg; + /* note: mpu register should be present only on SB16 Vibra soundcards */ + + // printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); + spin_lock_irqsave(&chip->mixer_lock, flags); + mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; + spin_unlock_irqrestore(&chip->mixer_lock, flags); + switch (chip->irq) { + case 2: + case 9: + irqreg |= SB_IRQSETUP_IRQ9; + break; + case 5: + irqreg |= SB_IRQSETUP_IRQ5; + break; + case 7: + irqreg |= SB_IRQSETUP_IRQ7; + break; + case 10: + irqreg |= SB_IRQSETUP_IRQ10; + break; + default: + return -EINVAL; + } + if (chip->dma8 >= 0) { + switch (chip->dma8) { + case 0: + dmareg |= SB_DMASETUP_DMA0; + break; + case 1: + dmareg |= SB_DMASETUP_DMA1; + break; + case 3: + dmareg |= SB_DMASETUP_DMA3; + break; + default: + return -EINVAL; + } + } + if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { + switch (chip->dma16) { + case 5: + dmareg |= SB_DMASETUP_DMA5; + break; + case 6: + dmareg |= SB_DMASETUP_DMA6; + break; + case 7: + dmareg |= SB_DMASETUP_DMA7; + break; + default: + return -EINVAL; + } + } + switch (chip->mpu_port) { + case 0x300: + mpureg |= 0x04; + break; + case 0x330: + mpureg |= 0x00; + break; + default: + mpureg |= 0x02; /* disable MPU */ + } + spin_lock_irqsave(&chip->mixer_lock, flags); + + snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); + realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); + + snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); + realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); + + snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); + realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); + + spin_unlock_irqrestore(&chip->mixer_lock, flags); + if ((~realirq) & irqreg || (~realdma) & dmareg) { + snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); + snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); + snd_printk("SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg); + return -ENODEV; + } + return 0; +} + +static snd_pcm_ops_t snd_sb16_playback_ops = { + .open = snd_sb16_playback_open, + .close = snd_sb16_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sb16_hw_params, + .hw_free = snd_sb16_hw_free, + .prepare = snd_sb16_playback_prepare, + .trigger = snd_sb16_playback_trigger, + .pointer = snd_sb16_playback_pointer, +}; + +static snd_pcm_ops_t snd_sb16_capture_ops = { + .open = snd_sb16_capture_open, + .close = snd_sb16_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sb16_hw_params, + .hw_free = snd_sb16_hw_free, + .prepare = snd_sb16_capture_prepare, + .trigger = snd_sb16_capture_trigger, + .pointer = snd_sb16_capture_pointer, +}; + +static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_card_t *card = chip->card; + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) + return err; + sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + pcm->private_data = chip; + pcm->private_free = snd_sb16dsp_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); + + if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) + snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); + else + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction) +{ + return direction == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_sb16_playback_ops : &snd_sb16_capture_ops; +} + +EXPORT_SYMBOL(snd_sb16dsp_pcm); +EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); +EXPORT_SYMBOL(snd_sb16dsp_configure); +EXPORT_SYMBOL(snd_sb16dsp_interrupt); + +/* + * INIT part + */ + +static int __init alsa_sb16_init(void) +{ + return 0; +} + +static void __exit alsa_sb16_exit(void) +{ +} + +module_init(alsa_sb16_init) +module_exit(alsa_sb16_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb8.c linux/sound/isa/sb/sb8.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb8.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb8.c 2003-03-04 12:43:22.000000000 -0700 @@ -0,0 +1,254 @@ +/* + * Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_GET_ID +#include + +#define chip_t sb_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(port, "Port # for SB8 driver."); +MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for SB8 driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); +MODULE_PARM(dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver."); +MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); + +struct snd_sb8 { + struct resource *fm_res; /* used to block FM i/o region for legacy cards */ +}; + +static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static void snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + + if (chip->open & SB_OPEN_PCM) { + snd_sb8dsp_interrupt(chip); + } else { + snd_sb8dsp_midi_interrupt(chip); + } +} + +static void snd_sb8_free(snd_card_t *card) +{ + struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data; + + if (acard == NULL) + return; + if (acard->fm_res) { + release_resource(acard->fm_res); + kfree_nocheck(acard->fm_res); + } +} + +static int __init snd_sb8_probe(int dev) +{ + sb_t *chip; + snd_card_t *card; + struct snd_sb8 *acard; + opl3_t *opl3; + int err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_sb8)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_sb8 *)card->private_data; + card->private_free = snd_sb8_free; + + /* block the 0x388 port to avoid PnP conflicts */ + acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); + + if ((err = snd_sbdsp_create(card, port[dev], irq[dev], + snd_sb8_interrupt, + dma8[dev], + -1, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware >= SB_HW_16) { + snd_card_free(card); + if (chip->hardware == SB_HW_ALS100) + snd_printdd("ALS100 chip detected at 0x%lx, try snd-als100 module\n", + port[dev]); + else + snd_printdd("SB 16 chip detected at 0x%lx, try snd-sb16 module\n", + port[dev]); + return -ENODEV; + } + + if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) { + if ((err = snd_opl3_create(card, chip->port + 8, 0, + OPL3_HW_AUTO, 1, + &opl3)) < 0) { + printk(KERN_ERR "sb8: no OPL device at 0x%lx\n", chip->port + 8); + } + } else { + if ((err = snd_opl3_create(card, chip->port, chip->port + 2, + OPL3_HW_AUTO, 1, + &opl3)) < 0) { + printk(KERN_ERR "sb8: no OPL device at 0x%lx-0x%lx\n", + chip->port, chip->port + 2); + } + } + if (err >= 0) { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8"); + strcpy(card->shortname, chip->name); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + chip->name, + chip->port, + irq[dev], dma8[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sb8_cards[dev] = card; + return 0; +} + +static int __init snd_card_sb8_legacy_auto_probe(unsigned long xport) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) + continue; + port[dev] = xport; + res = snd_sb8_probe(dev); + if (res < 0) + port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_sb8_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_sb8_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Sound Blaster soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_sb8_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_sb8_cards[idx]); +} + +module_init(alsa_card_sb8_init) +module_exit(alsa_card_sb8_exit) + +#ifndef MODULE + +/* format is: snd-sb8=enable,index,id, + port,irq,dma8 */ + +static int __init alsa_card_sb8_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&port[nr_dev]) == 2 && + get_option(&str,&irq[nr_dev]) == 2 && + get_option(&str,&dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sb8=", alsa_card_sb8_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb8_main.c linux/sound/isa/sb/sb8_main.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb8_main.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb8_main.c 2003-03-01 12:04:30.000000000 -0700 @@ -0,0 +1,564 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Uros Bizjak + * + * Routines for control of 8-bit SoundBlaster cards and clones + * Please note: I don't have access to old SB8 soundcards. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Thu Apr 29 20:36:17 BST 1999 George David Morrison + * DSP can't respond to commands whilst in "high speed" mode. Caused + * glitching during playback. Fixed. + * + * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak + * Cleaned up and rewrote lowlevel routines. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Uros Bizjak "); +MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones"); +MODULE_LICENSE("GPL"); + +#define chip_t sb_t + +#define SB8_CLOCK 1000000 +#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) +#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) + +static ratnum_t clock = { + .num = SB8_CLOCK, + .den_min = 1, + .den_max = 256, + .den_step = 1, +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = { + .nrats = 1, + .rats = &clock, +}; + +static ratnum_t stereo_clocks[] = { + { + .num = SB8_CLOCK, + .den_min = SB8_DEN(22050), + .den_max = SB8_DEN(22050), + .den_step = 1, + }, + { + .num = SB8_CLOCK, + .den_min = SB8_DEN(11025), + .den_max = SB8_DEN(11025), + .den_step = 1, + } +}; + +static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (c->min > 1) { + unsigned int num = 0, den = 0; + int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), + 2, stereo_clocks, &num, &den); + if (err >= 0 && den) { + params->rate_num = num; + params->rate_den = den; + } + return err; + } + return 0; +} + +static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { + snd_interval_t t = { .min = 1, .max = 1 }; + return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); + } + return 0; +} + +static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mixreg, rate, size, count; + + rate = runtime->rate; + switch (chip->hardware) { + case SB_HW_PRO: + if (runtime->channels > 1) { + snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); + chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_201: + if (rate > 23000) { + chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_20: + chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; + break; + case SB_HW_10: + chip->playback_format = SB_DSP_OUTPUT; + break; + default: + return -EINVAL; + } + size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); + count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); + if (runtime->channels > 1) { + /* set playback stereo mode */ + spin_lock(&chip->mixer_lock); + mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); + snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); + spin_unlock(&chip->mixer_lock); + + /* Soundblaster hardware programming reference guide, 3-23 */ + snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); + runtime->dma_area[0] = 0x80; + snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE); + /* force interrupt */ + chip->mode = SB_MODE_HALT; + snd_sbdsp_command(chip, SB_DSP_OUTPUT); + snd_sbdsp_command(chip, 0); + snd_sbdsp_command(chip, 0); + } + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); + if (runtime->channels > 1) { + snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); + spin_lock(&chip->mixer_lock); + /* save output filter status and turn it off */ + mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); + snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); + spin_unlock(&chip->mixer_lock); + /* just use force_mode16 for temporary storate... */ + chip->force_mode16 = mixreg; + } else { + snd_sbdsp_command(chip, 256 - runtime->rate_den); + } + if (chip->playback_format != SB_DSP_OUTPUT) { + count--; + snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_dma_program(chip->dma8, runtime->dma_addr, + size, DMA_MODE_WRITE | DMA_AUTOINIT); + return 0; +} + +static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int count; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_sbdsp_command(chip, chip->playback_format); + if (chip->playback_format == SB_DSP_OUTPUT) { + count = chip->p_period_size - 1; + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) { + snd_pcm_runtime_t *runtime = substream->runtime; + snd_sbdsp_reset(chip); + if (runtime->channels > 1) { + spin_lock(&chip->mixer_lock); + /* restore output filter and set hardware to mono mode */ + snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02); + spin_unlock(&chip->mixer_lock); + } + } else { + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT; + return 0; +} + +static int snd_sb8_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sb8_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mixreg, rate, size, count; + + rate = runtime->rate; + switch (chip->hardware) { + case SB_HW_PRO: + if (runtime->channels > 1) { + snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); + chip->capture_format = SB_DSP_HI_INPUT_AUTO; + break; + } + chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO; + break; + case SB_HW_201: + if (rate > 13000) { + chip->capture_format = SB_DSP_HI_INPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_20: + chip->capture_format = SB_DSP_LO_INPUT_AUTO; + break; + case SB_HW_10: + chip->capture_format = SB_DSP_INPUT; + break; + default: + return -EINVAL; + } + size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + if (runtime->channels > 1) + snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); + if (runtime->channels > 1) { + snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); + spin_lock(&chip->mixer_lock); + /* save input filter status and turn it off */ + mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT); + snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20); + spin_unlock(&chip->mixer_lock); + /* just use force_mode16 for temporary storate... */ + chip->force_mode16 = mixreg; + } else { + snd_sbdsp_command(chip, 256 - runtime->rate_den); + } + if (chip->capture_format != SB_DSP_OUTPUT) { + count--; + snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_dma_program(chip->dma8, runtime->dma_addr, + size, DMA_MODE_READ | DMA_AUTOINIT); + return 0; +} + +static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int count; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_sbdsp_command(chip, chip->capture_format); + if (chip->capture_format == SB_DSP_INPUT) { + count = chip->c_period_size - 1; + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) { + snd_pcm_runtime_t *runtime = substream->runtime; + snd_sbdsp_reset(chip); + if (runtime->channels > 1) { + /* restore input filter status */ + spin_lock(&chip->mixer_lock); + snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16); + spin_unlock(&chip->mixer_lock); + /* set hardware to mono mode */ + snd_sbdsp_command(chip, SB_DSP_MONO_8BIT); + } + } else { + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT; + return 0; +} + +void snd_sb8dsp_interrupt(sb_t *chip) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + +#if 0 + snd_printk("sb8: interrupt\n"); +#endif + snd_sb_ack_8bit(chip); + switch (chip->mode) { + case SB_MODE_PLAYBACK_8: /* ok.. playback is active */ + substream = chip->playback_substream; + runtime = substream->runtime; + if (chip->playback_format == SB_DSP_OUTPUT) + snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_period_elapsed(substream); + break; + case SB_MODE_CAPTURE_8: + substream = chip->capture_substream; + runtime = substream->runtime; + if (chip->capture_format == SB_DSP_INPUT) + snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_period_elapsed(substream); + break; + } +} + +static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->mode != SB_MODE_PLAYBACK_8) + return 0; + ptr = snd_dma_pointer(chip->dma8, chip->p_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->mode != SB_MODE_CAPTURE_8) + return 0; + ptr = snd_dma_pointer(chip->dma8, chip->c_dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_sb8_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), + .rate_min = 4000, + .rate_max = 23000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_sb8_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_11025), + .rate_min = 4000, + .rate_max = 13000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * + */ + +int snd_sb8_open(snd_pcm_substream_t *substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_PCM; + spin_unlock_irqrestore(&chip->open_lock, flags); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + chip->playback_substream = substream; + runtime->hw = snd_sb8_playback; + } else { + chip->capture_substream = substream; + runtime->hw = snd_sb8_capture; + } + switch (chip->hardware) { + case SB_HW_PRO: + runtime->hw.rate_max = 44100; + runtime->hw.channels_max = 2; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_sb8_hw_constraint_rate_channels, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_sb8_hw_constraint_channels_rate, 0, + SNDRV_PCM_HW_PARAM_RATE, -1); + break; + case SB_HW_201: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.rate_max = 44100; + } else { + runtime->hw.rate_max = 15000; + } + default: + break; + } + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clock); + return 0; +} + +int snd_sb8_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + chip->capture_substream = NULL; + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~SB_OPEN_PCM; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +/* + * Initialization part + */ + +static snd_pcm_ops_t snd_sb8_playback_ops = { + .open = snd_sb8_open, + .close = snd_sb8_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sb8_hw_params, + .hw_free = snd_sb8_hw_free, + .prepare = snd_sb8_playback_prepare, + .trigger = snd_sb8_playback_trigger, + .pointer = snd_sb8_playback_pointer, +}; + +static snd_pcm_ops_t snd_sb8_capture_ops = { + .open = snd_sb8_open, + .close = snd_sb8_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sb8_hw_params, + .hw_free = snd_sb8_hw_free, + .prepare = snd_sb8_capture_prepare, + .trigger = snd_sb8_capture_trigger, + .pointer = snd_sb8_capture_pointer, +}; + +static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_card_t *card = chip->card; + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) + return err; + sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + pcm->private_data = chip; + pcm->private_free = snd_sb8dsp_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +EXPORT_SYMBOL(snd_sb8dsp_pcm); +EXPORT_SYMBOL(snd_sb8dsp_interrupt); + /* sb8_midi.c */ +EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); +EXPORT_SYMBOL(snd_sb8dsp_midi); + +/* + * INIT part + */ + +static int __init alsa_sb8_init(void) +{ + return 0; +} + +static void __exit alsa_sb8_exit(void) +{ +} + +module_init(alsa_sb8_init) +module_exit(alsa_sb8_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb8_midi.c linux/sound/isa/sb/sb8_midi.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb8_midi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb8_midi.c 2002-11-23 03:57:14.000000000 -0700 @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of SoundBlaster cards - MIDI interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Sun May 9 22:54:38 BST 1999 George David Morrison + * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from + * working. + */ + +#include +#include +#include +#include +#include + +/* + + */ + +void snd_sb8dsp_midi_interrupt(sb_t * chip) +{ + snd_rawmidi_t *rmidi; + int max = 64; + char byte; + + if (chip == NULL || (rmidi = chip->rmidi) == NULL) { + inb(SBP(chip, READ)); /* ack interrupt */ + return; + } + while (max-- > 0) { + spin_lock(&chip->midi_input_lock); + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + byte = inb(SBP(chip, READ)); + spin_unlock(&chip->midi_input_lock); + snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); + } else { + spin_unlock(&chip->midi_input_lock); + } + } +} + +/* + + */ + +static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_MIDI_INPUT; + chip->midi_substream_input = substream; + if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_MIDI_OUTPUT; + chip->midi_substream_output = substream; + if (!(chip->open & SB_OPEN_MIDI_INPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_TRIGGER); + chip->midi_substream_input = NULL; + if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~SB_OPEN_MIDI_OUTPUT; + chip->midi_substream_output = NULL; + if (!(chip->open & SB_OPEN_MIDI_INPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&chip->open_lock, flags); + if (up) { + if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) { + snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); + chip->open |= SB_OPEN_MIDI_TRIGGER; + } + } else { + if (chip->open & SB_OPEN_MIDI_TRIGGER) { + snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + } + } + spin_unlock_irqrestore(&chip->open_lock, flags); +} + +static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + char byte; + int max = 32; + + /* how big is Tx FIFO? */ + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + while (max-- > 0) { + spin_lock_irqsave(&chip->open_lock, flags); + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + del_timer(&chip->midi_timer); + spin_unlock_irqrestore(&chip->open_lock, flags); + return; + } + snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); + snd_sbdsp_command(chip, byte); + spin_unlock_irqrestore(&chip->open_lock, flags); + } +} + +static void snd_sb8dsp_midi_output_timer(unsigned long data) +{ + snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data; + sb_t * chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->open_lock, flags); + chip->midi_timer.expires = 1 + jiffies; + add_timer(&chip->midi_timer); + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sb8dsp_midi_output_write(substream); +} + +static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&chip->open_lock, flags); + if (up) { + if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) { + init_timer(&chip->midi_timer); + chip->midi_timer.function = snd_sb8dsp_midi_output_timer; + chip->midi_timer.data = (unsigned long) substream; + chip->midi_timer.expires = 1 + jiffies; + add_timer(&chip->midi_timer); + chip->open |= SB_OPEN_MIDI_TRIGGER; + } + } else { + if (chip->open & SB_OPEN_MIDI_TRIGGER) { + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + } + } + spin_unlock_irqrestore(&chip->open_lock, flags); + + if (up) + snd_sb8dsp_midi_output_write(substream); +} + +/* + + */ + +static snd_rawmidi_ops_t snd_sb8dsp_midi_output = +{ + .open = snd_sb8dsp_midi_output_open, + .close = snd_sb8dsp_midi_output_close, + .trigger = snd_sb8dsp_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_sb8dsp_midi_input = +{ + .open = snd_sb8dsp_midi_input_open, + .close = snd_sb8dsp_midi_input_close, + .trigger = snd_sb8dsp_midi_input_trigger, +}; + +int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "SB8 MIDI"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb_common.c linux/sound/isa/sb/sb_common.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb_common.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb_common.c 2003-01-09 03:51:09.000000000 -0700 @@ -0,0 +1,316 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Uros Bizjak + * + * Lowlevel routines for control of Sound Blaster cards + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define chip_t sb_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#define BUSY_LOOPS 100000 + +#undef IO_DEBUG + +int snd_sbdsp_command(sb_t *chip, unsigned char val) +{ + int i; +#ifdef IO_DEBUG + snd_printk("command 0x%x\n", val); +#endif + for (i = BUSY_LOOPS; i; i--) + if ((inb(SBP(chip, STATUS)) & 0x80) == 0) { + outb(val, SBP(chip, COMMAND)); + return 1; + } + snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val); + return 0; +} + +int snd_sbdsp_get_byte(sb_t *chip) +{ + int val; + int i; + for (i = BUSY_LOOPS; i; i--) { + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + val = inb(SBP(chip, READ)); +#ifdef IO_DEBUG + snd_printk("get_byte 0x%x\n", val); +#endif + return val; + } + } + snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port); + return -ENODEV; +} + +int snd_sbdsp_reset(sb_t *chip) +{ + int i; + + outb(1, SBP(chip, RESET)); + udelay(10); + outb(0, SBP(chip, RESET)); + udelay(30); + for (i = BUSY_LOOPS; i; i--) + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + if (inb(SBP(chip, READ)) == 0xaa) + return 0; + else + break; + } + snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port); + return -ENODEV; +} + +int snd_sbdsp_version(sb_t * chip) +{ + unsigned int result = -ENODEV; + + snd_sbdsp_command(chip, SB_DSP_GET_VERSION); + result = (short) snd_sbdsp_get_byte(chip) << 8; + result |= (short) snd_sbdsp_get_byte(chip); + return result; +} + +static int snd_sbdsp_probe(sb_t * chip) +{ + int version; + int major, minor; + char *str; + unsigned long flags; + + /* + * initialization sequence + */ + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_sbdsp_reset(chip) < 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + version = snd_sbdsp_version(chip); + if (version < 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + major = version >> 8; + minor = version & 0xff; + snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", + chip->port, major, minor); + + switch (chip->hardware) { + case SB_HW_AUTO: + switch (major) { + case 1: + chip->hardware = SB_HW_10; + str = "1.0"; + break; + case 2: + if (minor) { + chip->hardware = SB_HW_201; + str = "2.01+"; + } else { + chip->hardware = SB_HW_20; + str = "2.0"; + } + break; + case 3: + chip->hardware = SB_HW_PRO; + str = "Pro"; + break; + case 4: + chip->hardware = SB_HW_16; + str = "16"; + break; + default: + snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n", + chip->port, major, minor); + return -ENODEV; + } + break; + case SB_HW_ALS100: + str = "16 (ALS-100)"; + break; + case SB_HW_ALS4000: + str = "16 (ALS-4000)"; + break; + case SB_HW_DT019X: + str = "(DT019X/ALS007)"; + break; + default: + return -ENODEV; + } + sprintf(chip->name, "Sound Blaster %s", str); + chip->version = (major << 8) | minor; + return 0; +} + +static int snd_sbdsp_free(sb_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_alt_port) { + release_resource(chip->res_alt_port); + kfree_nocheck(chip->res_alt_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); +#ifdef CONFIG_ISA + if (chip->dma8 >= 0) { + disable_dma(chip->dma8); + free_dma(chip->dma8); + } + if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { + disable_dma(chip->dma16); + free_dma(chip->dma16); + } +#endif + snd_magic_kfree(chip); + return 0; +} + +static int snd_sbdsp_dev_free(snd_device_t *device) +{ + sb_t *chip = snd_magic_cast(sb_t, device->device_data, return -ENXIO); + return snd_sbdsp_free(chip); +} + +int snd_sbdsp_create(snd_card_t *card, + unsigned long port, + int irq, + void (*irq_handler)(int, void *, struct pt_regs *), + int dma8, + int dma16, + unsigned short hardware, + sb_t **r_chip) +{ + sb_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_sbdsp_dev_free, + }; + + snd_assert(r_chip != NULL, return -EINVAL); + *r_chip = NULL; + chip = snd_magic_kcalloc(sb_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->open_lock); + spin_lock_init(&chip->midi_input_lock); + spin_lock_init(&chip->mixer_lock); + chip->irq = -1; + chip->dma8 = -1; + chip->dma16 = -1; + chip->port = port; + + if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ? + SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT, + "SoundBlaster", (void *) chip)) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->irq = irq; + + if (hardware == SB_HW_ALS4000) + goto __skip_allocation; + + if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) { + snd_sbdsp_free(chip); + return -EBUSY; + } + +#ifdef CONFIG_ISA + if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->dma8 = dma8; + if (dma16 >= 0) { + if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) { + /* no duplex */ + dma16 = -1; + } else if (request_dma(dma16, "SoundBlaster - 16bit")) { + snd_sbdsp_free(chip); + return -EBUSY; + } + } + chip->dma16 = dma16; +#endif + + __skip_allocation: + chip->card = card; + chip->hardware = hardware; + if ((err = snd_sbdsp_probe(chip)) < 0) { + snd_sbdsp_free(chip); + return err; + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_sbdsp_free(chip); + return err; + } + *r_chip = chip; + return 0; +} + +EXPORT_SYMBOL(snd_sbdsp_command); +EXPORT_SYMBOL(snd_sbdsp_get_byte); +EXPORT_SYMBOL(snd_sbdsp_reset); +EXPORT_SYMBOL(snd_sbdsp_create); +/* sb_mixer.c */ +EXPORT_SYMBOL(snd_sbmixer_write); +EXPORT_SYMBOL(snd_sbmixer_read); +EXPORT_SYMBOL(snd_sbmixer_new); +EXPORT_SYMBOL(snd_sbmixer_add_ctl); + +/* + * INIT part + */ + +static int __init alsa_sb_common_init(void) +{ + return 0; +} + +static void __exit alsa_sb_common_exit(void) +{ +} + +module_init(alsa_sb_common_init) +module_exit(alsa_sb_common_exit) diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sb_mixer.c linux/sound/isa/sb/sb_mixer.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sb_mixer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sb_mixer.c 2003-01-09 03:51:10.000000000 -0700 @@ -0,0 +1,846 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for Sound Blaster mixer control + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define chip_t sb_t + +#undef IO_DEBUG + +void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data) +{ + outb(reg, SBP(chip, MIXER_ADDR)); + udelay(10); + outb(data, SBP(chip, MIXER_DATA)); + udelay(10); +#ifdef IO_DEBUG + snd_printk("mixer_write 0x%x 0x%x\n", reg, data); +#endif +} + +unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) +{ + unsigned char result; + + outb(reg, SBP(chip, MIXER_ADDR)); + udelay(10); + result = inb(SBP(chip, MIXER_DATA)); + udelay(10); +#ifdef IO_DEBUG + snd_printk("mixer_read 0x%x 0x%x\n", reg, result); +#endif + return result; +} + +/* + * Single channel mixer element + */ + +static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sbmixer_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + spin_lock_irqsave(&sb->mixer_lock, flags); + val = (snd_sbmixer_read(sb, reg) >> shift) & mask; + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char val, oval; + + val = (ucontrol->value.integer.value[0] & mask) << shift; + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + if (change) + snd_sbmixer_write(sb, reg, val); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * Double channel mixer element + */ + +static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sbmixer_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + unsigned char left, right; + + spin_lock_irqsave(&sb->mixer_lock, flags); + left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask; + right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask; + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + return 0; +} + +static int snd_sbmixer_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char left, right, oleft, oright; + + left = (ucontrol->value.integer.value[0] & mask) << left_shift; + right = (ucontrol->value.integer.value[1] & mask) << right_shift; + spin_lock_irqsave(&sb->mixer_lock, flags); + if (left_reg == right_reg) { + oleft = snd_sbmixer_read(sb, left_reg); + left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right; + change = left != oleft; + if (change) + snd_sbmixer_write(sb, left_reg, left); + } else { + oleft = snd_sbmixer_read(sb, left_reg); + oright = snd_sbmixer_read(sb, right_reg); + left = (oleft & ~(mask << left_shift)) | left; + right = (oright & ~(mask << right_shift)) | right; + change = left != oleft || right != oright; + if (change) { + snd_sbmixer_write(sb, left_reg, left); + snd_sbmixer_write(sb, right_reg, right); + } + } + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * DT-019x / ALS-007 capture/input switch + */ + +static int snd_dt019x_input_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[5] = { + "CD", "Mic", "Line", "Synth", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + if (uinfo->value.enumerated.item > 4) + uinfo->value.enumerated.item = 4; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_dt019x_input_sw_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + switch (oval & 0x07) { + case SB_DT019X_CAP_CD: + ucontrol->value.enumerated.item[0] = 0; + break; + case SB_DT019X_CAP_MIC: + ucontrol->value.enumerated.item[0] = 1; + break; + case SB_DT019X_CAP_LINE: + ucontrol->value.enumerated.item[0] = 2; + break; + case SB_DT019X_CAP_MAIN: + ucontrol->value.enumerated.item[0] = 4; + break; + /* To record the synth on these cards you must record the main. */ + /* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */ + /* duplicate case labels if left uncommented. */ + /* case SB_DT019X_CAP_SYNTH: + * ucontrol->value.enumerated.item[0] = 3; + * break; + */ + default: + ucontrol->value.enumerated.item[0] = 4; + break; + } + return 0; +} + +static int snd_dt019x_input_sw_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval, oval; + + if (ucontrol->value.enumerated.item[0] > 4) + return -EINVAL; + switch (ucontrol->value.enumerated.item[0]) { + case 0: + nval = SB_DT019X_CAP_CD; + break; + case 1: + nval = SB_DT019X_CAP_MIC; + break; + case 2: + nval = SB_DT019X_CAP_LINE; + break; + case 3: + nval = SB_DT019X_CAP_SYNTH; + break; + case 4: + nval = SB_DT019X_CAP_MAIN; + break; + default: + nval = SB_DT019X_CAP_MAIN; + } + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW); + change = nval != oval; + if (change) + snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * SBPRO input multiplexer + */ + +static int snd_sb8mixer_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Mic", "CD", "Line" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + + +static int snd_sb8mixer_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + switch ((oval >> 0x01) & 0x03) { + case SB_DSP_MIXS_CD: + ucontrol->value.enumerated.item[0] = 1; + break; + case SB_DSP_MIXS_LINE: + ucontrol->value.enumerated.item[0] = 2; + break; + default: + ucontrol->value.enumerated.item[0] = 0; + break; + } + return 0; +} + +static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval, oval; + + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + switch (ucontrol->value.enumerated.item[0]) { + case 1: + nval = SB_DSP_MIXS_CD; + break; + case 2: + nval = SB_DSP_MIXS_LINE; + break; + default: + nval = SB_DSP_MIXS_MIC; + } + nval <<= 1; + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); + nval |= oval & ~0x06; + change = nval != oval; + if (change) + snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * SB16 input switch + */ + +static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_sb16mixer_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + unsigned char val1, val2; + + spin_lock_irqsave(&sb->mixer_lock, flags); + val1 = snd_sbmixer_read(sb, reg1); + val2 = snd_sbmixer_read(sb, reg2); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01; + ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01; + ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01; + ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01; + return 0; +} + +static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + int change; + unsigned char val1, val2, oval1, oval2; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval1 = snd_sbmixer_read(sb, reg1); + oval2 = snd_sbmixer_read(sb, reg2); + val1 = oval1 & ~((1 << left_shift) | (1 << right_shift)); + val2 = oval2 & ~((1 << left_shift) | (1 << right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; + change = val1 != oval1 || val2 != oval2; + if (change) { + snd_sbmixer_write(sb, reg1, val1); + snd_sbmixer_write(sb, reg2, val2); + } + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + + +/* + */ +/* + */ +int snd_sbmixer_add_ctl(sb_t *chip, const char *name, int index, int type, unsigned long value) +{ + static snd_kcontrol_new_t newctls[] = { + [SB_MIX_SINGLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sbmixer_info_single, + .get = snd_sbmixer_get_single, + .put = snd_sbmixer_put_single, + }, + [SB_MIX_DOUBLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sbmixer_info_double, + .get = snd_sbmixer_get_double, + .put = snd_sbmixer_put_double, + }, + [SB_MIX_INPUT_SW] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sb16mixer_info_input_sw, + .get = snd_sb16mixer_get_input_sw, + .put = snd_sb16mixer_put_input_sw, + }, + [SB_MIX_CAPTURE_PRO] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sb8mixer_info_mux, + .get = snd_sb8mixer_get_mux, + .put = snd_sb8mixer_put_mux, + }, + [SB_MIX_CAPTURE_DT019X] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_dt019x_input_sw_info, + .get = snd_dt019x_input_sw_get, + .put = snd_dt019x_input_sw_put, + }, + }; + snd_kcontrol_t *ctl; + int err; + + ctl = snd_ctl_new1(&newctls[type], chip); + if (! ctl) + return -ENOMEM; + strncpy(ctl->id.name, name, sizeof(ctl->id.name)-1); + ctl->id.index = index; + ctl->private_value = value; + if ((err = snd_ctl_add(chip->card, ctl)) < 0) { + snd_ctl_free_one(ctl); + return err; + } + return 0; +} + +/* + * SB 2.0 specific mixer elements + */ + +static struct sbmix_elem snd_sb20_ctl_master_play_vol = + SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7); +static struct sbmix_elem snd_sb20_ctl_pcm_play_vol = + SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3); +static struct sbmix_elem snd_sb20_ctl_synth_play_vol = + SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7); +static struct sbmix_elem snd_sb20_ctl_cd_play_vol = + SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7); + +static struct sbmix_elem *snd_sb20_controls[] = { + &snd_sb20_ctl_master_play_vol, + &snd_sb20_ctl_pcm_play_vol, + &snd_sb20_ctl_synth_play_vol, + &snd_sb20_ctl_cd_play_vol +}; + +static unsigned char snd_sb20_init_values[][2] = { + { SB_DSP20_MASTER_DEV, 0 }, + { SB_DSP20_FM_DEV, 0 }, +}; + +/* + * SB Pro specific mixer elements + */ +static struct sbmix_elem snd_sbpro_ctl_master_play_vol = + SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7); +static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol = + SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7); +static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter = + SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1); +static struct sbmix_elem snd_sbpro_ctl_synth_play_vol = + SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7); +static struct sbmix_elem snd_sbpro_ctl_cd_play_vol = + SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7); +static struct sbmix_elem snd_sbpro_ctl_line_play_vol = + SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7); +static struct sbmix_elem snd_sbpro_ctl_mic_play_vol = + SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3); +static struct sbmix_elem snd_sbpro_ctl_capture_source = + { + .name = "Capture Source", + .type = SB_MIX_CAPTURE_PRO + }; +static struct sbmix_elem snd_sbpro_ctl_capture_filter = + SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1); +static struct sbmix_elem snd_sbpro_ctl_capture_low_filter = + SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1); + +static struct sbmix_elem *snd_sbpro_controls[] = { + &snd_sbpro_ctl_master_play_vol, + &snd_sbpro_ctl_pcm_play_vol, + &snd_sbpro_ctl_pcm_play_filter, + &snd_sbpro_ctl_synth_play_vol, + &snd_sbpro_ctl_cd_play_vol, + &snd_sbpro_ctl_line_play_vol, + &snd_sbpro_ctl_mic_play_vol, + &snd_sbpro_ctl_capture_source, + &snd_sbpro_ctl_capture_filter, + &snd_sbpro_ctl_capture_low_filter +}; + +static unsigned char snd_sbpro_init_values[][2] = { + { SB_DSP_MASTER_DEV, 0 }, + { SB_DSP_PCM_DEV, 0 }, + { SB_DSP_FM_DEV, 0 }, +}; + +/* + * SB16 specific mixer elements + */ +static struct sbmix_elem snd_sb16_ctl_master_play_vol = + SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31); +static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch = + SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1); +static struct sbmix_elem snd_sb16_ctl_tone_bass = + SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15); +static struct sbmix_elem snd_sb16_ctl_tone_treble = + SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15); +static struct sbmix_elem snd_sb16_ctl_pcm_play_vol = + SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31); +static struct sbmix_elem snd_sb16_ctl_synth_capture_route = + SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5); +static struct sbmix_elem snd_sb16_ctl_synth_play_vol = + SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31); +static struct sbmix_elem snd_sb16_ctl_cd_capture_route = + SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1); +static struct sbmix_elem snd_sb16_ctl_cd_play_switch = + SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1); +static struct sbmix_elem snd_sb16_ctl_cd_play_vol = + SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31); +static struct sbmix_elem snd_sb16_ctl_line_capture_route = + SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3); +static struct sbmix_elem snd_sb16_ctl_line_play_switch = + SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1); +static struct sbmix_elem snd_sb16_ctl_line_play_vol = + SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31); +static struct sbmix_elem snd_sb16_ctl_mic_capture_route = + SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0); +static struct sbmix_elem snd_sb16_ctl_mic_play_switch = + SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1); +static struct sbmix_elem snd_sb16_ctl_mic_play_vol = + SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31); +static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol = + SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3); +static struct sbmix_elem snd_sb16_ctl_capture_vol = + SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3); +static struct sbmix_elem snd_sb16_ctl_play_vol = + SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3); +static struct sbmix_elem snd_sb16_ctl_auto_mic_gain = + SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1); + +static struct sbmix_elem *snd_sb16_controls[] = { + &snd_sb16_ctl_master_play_vol, + &snd_sb16_ctl_3d_enhance_switch, + &snd_sb16_ctl_tone_bass, + &snd_sb16_ctl_tone_treble, + &snd_sb16_ctl_pcm_play_vol, + &snd_sb16_ctl_synth_capture_route, + &snd_sb16_ctl_synth_play_vol, + &snd_sb16_ctl_cd_capture_route, + &snd_sb16_ctl_cd_play_switch, + &snd_sb16_ctl_cd_play_vol, + &snd_sb16_ctl_line_capture_route, + &snd_sb16_ctl_line_play_switch, + &snd_sb16_ctl_line_play_vol, + &snd_sb16_ctl_mic_capture_route, + &snd_sb16_ctl_mic_play_switch, + &snd_sb16_ctl_mic_play_vol, + &snd_sb16_ctl_pc_speaker_vol, + &snd_sb16_ctl_capture_vol, + &snd_sb16_ctl_play_vol, + &snd_sb16_ctl_auto_mic_gain +}; + +static unsigned char snd_sb16_init_values[][2] = { + { SB_DSP4_MASTER_DEV + 0, 0 }, + { SB_DSP4_MASTER_DEV + 1, 0 }, + { SB_DSP4_PCM_DEV + 0, 0 }, + { SB_DSP4_PCM_DEV + 1, 0 }, + { SB_DSP4_SYNTH_DEV + 0, 0 }, + { SB_DSP4_SYNTH_DEV + 1, 0 }, + { SB_DSP4_INPUT_LEFT, 0 }, + { SB_DSP4_INPUT_RIGHT, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DSP4_SPEAKER_DEV, 0 }, +}; + +/* + * DT019x specific mixer elements + */ +static struct sbmix_elem snd_dt019x_ctl_master_play_vol = + SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15); +static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol = + SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15); +static struct sbmix_elem snd_dt019x_ctl_synth_play_vol = + SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15); +static struct sbmix_elem snd_dt019x_ctl_cd_play_vol = + SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15); +static struct sbmix_elem snd_dt019x_ctl_mic_play_vol = + SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7); +static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol = + SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0, 7); +static struct sbmix_elem snd_dt019x_ctl_line_play_vol = + SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15); +static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch = + SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1); +static struct sbmix_elem snd_dt019x_ctl_synth_play_switch = + SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1); +static struct sbmix_elem snd_dt019x_ctl_capture_source = + { + .name = "Capture Source", + .type = SB_MIX_CAPTURE_DT019X + }; + +static struct sbmix_elem *snd_dt019x_controls[] = { + &snd_dt019x_ctl_master_play_vol, + &snd_dt019x_ctl_pcm_play_vol, + &snd_dt019x_ctl_synth_play_vol, + &snd_dt019x_ctl_cd_play_vol, + &snd_dt019x_ctl_mic_play_vol, + &snd_dt019x_ctl_pc_speaker_vol, + &snd_dt019x_ctl_line_play_vol, + &snd_sb16_ctl_mic_play_switch, + &snd_sb16_ctl_cd_play_switch, + &snd_sb16_ctl_line_play_switch, + &snd_dt019x_ctl_pcm_play_switch, + &snd_dt019x_ctl_synth_play_switch, + &snd_dt019x_ctl_capture_source +}; + +static unsigned char snd_dt019x_init_values[][2] = { + { SB_DT019X_MASTER_DEV, 0 }, + { SB_DT019X_PCM_DEV, 0 }, + { SB_DT019X_SYNTH_DEV, 0 }, + { SB_DT019X_CD_DEV, 0 }, + { SB_DT019X_MIC_DEV, 0 }, /* Includes PC-speaker in high nibble */ + { SB_DT019X_LINE_DEV, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DT019X_OUTPUT_SW2, 0 }, + { SB_DT019X_CAPTURE_SW, 0x06 }, +}; + +/* + * ALS4000 specific mixer elements + */ +/* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl ! */ +static struct sbmix_elem snd_als4000_ctl_mono_output_switch = + SB_SINGLE("Mono Output Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1); +/* FIXME: mono input switch also available on DT019X ? */ +static struct sbmix_elem snd_als4000_ctl_mono_input_switch = + SB_SINGLE("Mono Input Switch", SB_DT019X_OUTPUT_SW2, 0, 1); +static struct sbmix_elem snd_als4000_ctl_mic_20db_boost = + SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03); +static struct sbmix_elem snd_als4000_ctl_mixer_out_to_in = + SB_SINGLE("Mixer Out To In", SB_ALS4000_MIC_IN_GAIN, 7, 0x01); +/* FIXME: 3D needs much more sophisticated controls, many more features ! */ +static struct sbmix_elem snd_als4000_ctl_3d_output_switch = + SB_SINGLE("3D Output Switch", SB_ALS4000_3D_SND_FX, 6, 0x01); +static struct sbmix_elem snd_als4000_ctl_3d_output_ratio = + SB_SINGLE("3D Output Ratio", SB_ALS4000_3D_SND_FX, 0, 0x07); +static struct sbmix_elem snd_als4000_ctl_3d_poweroff_switch = + SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01); +static struct sbmix_elem snd_als4000_ctl_3d_delay = + SB_SINGLE("3D Delay", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f); +#if NOT_AVAILABLE +static struct sbmix_elem snd_als4000_ctl_fmdac = + SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01); +static struct sbmix_elem snd_als4000_ctl_qsound = + SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f); +#endif + +static struct sbmix_elem *snd_als4000_controls[] = { + &snd_sb16_ctl_master_play_vol, + &snd_dt019x_ctl_pcm_play_switch, + &snd_sb16_ctl_pcm_play_vol, + &snd_sb16_ctl_synth_capture_route, + &snd_dt019x_ctl_synth_play_switch, + &snd_sb16_ctl_synth_play_vol, + &snd_sb16_ctl_cd_capture_route, + &snd_sb16_ctl_cd_play_switch, + &snd_sb16_ctl_cd_play_vol, + &snd_sb16_ctl_line_capture_route, + &snd_sb16_ctl_line_play_switch, + &snd_sb16_ctl_line_play_vol, + &snd_sb16_ctl_mic_capture_route, + &snd_als4000_ctl_mic_20db_boost, + &snd_sb16_ctl_auto_mic_gain, + &snd_sb16_ctl_mic_play_switch, + &snd_sb16_ctl_mic_play_vol, + &snd_sb16_ctl_pc_speaker_vol, + &snd_sb16_ctl_capture_vol, + &snd_sb16_ctl_play_vol, + &snd_als4000_ctl_mono_output_switch, + &snd_als4000_ctl_mono_input_switch, + &snd_als4000_ctl_mixer_out_to_in, + &snd_als4000_ctl_3d_output_switch, + &snd_als4000_ctl_3d_output_ratio, + &snd_als4000_ctl_3d_delay, + &snd_als4000_ctl_3d_poweroff_switch, +#if NOT_AVAILABLE + &snd_als4000_ctl_fmdac, + &snd_als4000_ctl_qsound, +#endif +}; + +static unsigned char snd_als4000_init_values[][2] = { + { SB_DSP4_MASTER_DEV + 0, 0 }, + { SB_DSP4_MASTER_DEV + 1, 0 }, + { SB_DSP4_PCM_DEV + 0, 0 }, + { SB_DSP4_PCM_DEV + 1, 0 }, + { SB_DSP4_SYNTH_DEV + 0, 0 }, + { SB_DSP4_SYNTH_DEV + 1, 0 }, + { SB_DSP4_SPEAKER_DEV, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DSP4_INPUT_LEFT, 0 }, + { SB_DSP4_INPUT_RIGHT, 0 }, + { SB_DT019X_OUTPUT_SW2, 0 }, + { SB_ALS4000_MIC_IN_GAIN, 0 }, +}; + + +/* + */ +static int snd_sbmixer_init(sb_t *chip, + struct sbmix_elem **controls, + int controls_count, + unsigned char map[][2], + int map_count, + char *name) +{ + unsigned long flags; + snd_card_t *card = chip->card; + int idx, err; + + /* mixer reset */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, 0x00, 0x00); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + /* mute and zero volume channels */ + for (idx = 0; idx < map_count; idx++) { + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, map[idx][0], map[idx][1]); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + } + + for (idx = 0; idx < controls_count; idx++) { + if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0) + return err; + } + snd_component_add(card, name); + strcpy(card->mixername, name); + return 0; +} + +int snd_sbmixer_new(sb_t *chip) +{ + snd_card_t * card; + int err; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + switch (chip->hardware) { + case SB_HW_10: + return 0; /* no mixer chip on SB1.x */ + case SB_HW_20: + case SB_HW_201: + if ((err = snd_sbmixer_init(chip, + snd_sb20_controls, + ARRAY_SIZE(snd_sb20_controls), + snd_sb20_init_values, + ARRAY_SIZE(snd_sb20_init_values), + "CTL1335")) < 0) + return err; + break; + case SB_HW_PRO: + if ((err = snd_sbmixer_init(chip, + snd_sbpro_controls, + ARRAY_SIZE(snd_sbpro_controls), + snd_sbpro_init_values, + ARRAY_SIZE(snd_sbpro_init_values), + "CTL1345")) < 0) + return err; + break; + case SB_HW_16: + case SB_HW_ALS100: + if ((err = snd_sbmixer_init(chip, + snd_sb16_controls, + ARRAY_SIZE(snd_sb16_controls), + snd_sb16_init_values, + ARRAY_SIZE(snd_sb16_init_values), + "CTL1745")) < 0) + return err; + break; + case SB_HW_ALS4000: + if ((err = snd_sbmixer_init(chip, + snd_als4000_controls, + ARRAY_SIZE(snd_als4000_controls), + snd_als4000_init_values, + ARRAY_SIZE(snd_als4000_init_values), + "ALS4000")) < 0) + return err; + break; + case SB_HW_DT019X: + if ((err = snd_sbmixer_init(chip, + snd_dt019x_controls, + ARRAY_SIZE(snd_dt019x_controls), + snd_dt019x_init_values, + ARRAY_SIZE(snd_dt019x_init_values), + "DT019X")) < 0) + break; + default: + strcpy(card->mixername, "???"); + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/isa/sb/sbawe.c linux/sound/isa/sb/sbawe.c --- linux-2.4.21-rc1.orig/sound/isa/sb/sbawe.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sb/sbawe.c 2001-12-18 07:09:51.000000000 -0700 @@ -0,0 +1,2 @@ +#define SNDRV_SBAWE +#include "sb16.c" diff -urN linux-2.4.21-rc1.orig/sound/isa/sgalaxy.c linux/sound/isa/sgalaxy.c --- linux-2.4.21-rc1.orig/sound/isa/sgalaxy.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/sgalaxy.c 2003-02-19 02:33:44.000000000 -0700 @@ -0,0 +1,353 @@ +/* + * Driver for Aztech Sound Galaxy cards + * Copyright (c) by Christopher Butler +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Christopher Butler "); +MODULE_DESCRIPTION("Aztech Sound Galaxy"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Aztech Systems,Sound Galaxy}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ +static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); +MODULE_PARM_SYNTAX(sbport, SNDRV_ENABLED ",allows:{{0x220},{0x240}},dialog:list"); +MODULE_PARM(wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); +MODULE_PARM_SYNTAX(wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); +MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{7},{9},{10},{11}},dialog:list"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA8_DESC); + +#define SGALAXY_AUXC_LEFT 18 +#define SGALAXY_AUXC_RIGHT 19 + +static snd_card_t *snd_sgalaxy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +/* + + */ + +#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) + +/* from lowlevel/sb/sb.c - to avoid having to allocate a sb_t for the */ +/* short time we actually need it.. */ + +static int snd_sgalaxy_sbdsp_reset(unsigned long port) +{ + int i; + + outb(1, SBP1(port, RESET)); + udelay(10); + outb(0, SBP1(port, RESET)); + udelay(30); + for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); + if (inb(SBP1(port, READ)) != 0xaa) { + snd_printd("sb_reset: failed at 0x%lx!!!\n", port); + return -ENODEV; + } + return 0; +} + +static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char val) +{ + int i; + + for (i = 10000; i; i--) + if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { + outb(val, SBP1(port, COMMAND)); + return 1; + } + + return 0; +} + +static void snd_sgalaxy_dummy_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) +{ + static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, + 0x10, 0x18, 0x20, -1, -1, -1, -1}; + static int dma_bits[] = {1, 2, 0, 3}; + int tmp, tmp1; + + if ((tmp = inb(port + 3)) == 0xff) + { + snd_printdd("I/O address dead (0x%lx)\n", port); + return 0; + } +#if 0 + snd_printdd("WSS signature = 0x%x\n", tmp); +#endif + + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) { + snd_printdd("No WSS signature detected on port 0x%lx\n", + port + 3); + return 0; + } + +#if 0 + snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n"); +#endif + + /* initialize IRQ for WSS codec */ + tmp = interrupt_bits[irq % 16]; + if (tmp < 0) + return -EINVAL; + + if (request_irq(irq, snd_sgalaxy_dummy_interrupt, SA_INTERRUPT, "sgalaxy", NULL)) + return -EIO; + + outb(tmp | 0x40, port); + tmp1 = dma_bits[dma % 4]; + outb(tmp | tmp1, port); + + free_irq(irq, NULL); + + return 0; +} + +static int __init snd_sgalaxy_detect(int dev, int irq, int dma) +{ +#if 0 + snd_printdd("sgalaxy - switching to WSS mode\n"); +#endif + + /* switch to WSS mode */ + snd_sgalaxy_sbdsp_reset(sbport[dev]); + + snd_sgalaxy_sbdsp_command(sbport[dev], 9); + snd_sgalaxy_sbdsp_command(sbport[dev], 0); + + udelay(400); + return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); +} + +static struct ad1848_mix_elem snd_sgalaxy_controls[] = { +AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) +}; + +static int __init snd_sgalaxy_mixer(ad1848_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + unsigned int idx; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 to LINE */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Line Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Line Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUX1 to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "FM Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* build AUX2 input */ + for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { + if ((err = snd_ad1848_add_ctl_elem(chip, &snd_sgalaxy_controls[idx])) < 0) + return err; + } + return 0; +} + +static int __init snd_sgalaxy_probe(int dev) +{ + static int possible_irqs[] = {7, 9, 10, 11, -1}; + static int possible_dmas[] = {1, 3, 0, -1}; + int err, xirq, xdma1; + snd_card_t *card; + ad1848_t *chip; + + if (sbport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify SB port\n"); + return -EINVAL; + } + if (wssport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify WSS port\n"); + return -EINVAL; + } + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + xirq = irq[dev]; + if (xirq == SNDRV_AUTO_IRQ) { + if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + xdma1 = dma1[dev]; + if (xdma1 == SNDRV_AUTO_DMA) { + if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA\n"); + return -EBUSY; + } + } + + if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_create(card, wssport[dev] + 4, + xirq, xdma1, + AD1848_HW_DETECT, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) { + snd_printdd("sgalaxy - error creating new ad1848 PCM device\n"); + snd_card_free(card); + return err; + } + if ((err = snd_ad1848_mixer(chip)) < 0) { + snd_printdd("sgalaxy - error creating new ad1848 mixer\n"); + snd_card_free(card); + return err; + } + if (snd_sgalaxy_mixer(chip) < 0) { + snd_printdd("sgalaxy - the mixer rewrite failed\n"); + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Sound Galaxy"); + strcpy(card->shortname, "Sound Galaxy"); + sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", + wssport[dev], xirq, xdma1); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sgalaxy_cards[dev] = card; + return 0; +} + +static int __init alsa_card_sgalaxy_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { + if (snd_sgalaxy_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Sound Galaxy soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_sgalaxy_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_sgalaxy_cards[idx]); +} + +module_init(alsa_card_sgalaxy_init) +module_exit(alsa_card_sgalaxy_exit) + +#ifndef MODULE + +/* format is: snd-sgalaxy=enable,index,id, + sbport,wssport, + irq,dma1 */ + +static int __init alsa_card_sgalaxy_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&sbport[nr_dev]) == 2 && + get_option(&str,(int *)&wssport[nr_dev]) == 2 && + get_option(&str,(int *)&irq[nr_dev]) == 2 && + get_option(&str,(int *)&dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sgalaxy=", alsa_card_sgalaxy_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/wavefront/Makefile linux/sound/isa/wavefront/Makefile --- linux-2.4.21-rc1.orig/sound/isa/wavefront/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/wavefront/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _wavefront.o + +list-multi := snd-wavefront.o + +snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o + +include $(TOPDIR)/Rules.make + +snd-wavefront.o: $(snd-wavefront-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-objs) diff -urN linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront.c linux/sound/isa/wavefront/wavefront.c --- linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/wavefront/wavefront.c 2002-11-19 10:21:59.000000000 -0700 @@ -0,0 +1,799 @@ +/* + * ALSA card-level driver for Turtle Beach Wavefront cards + * (Maui,Tropez,Tropez+) + * + * Copyright (c) 1997-1999 by Paul Barton-Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include + +#define chip_t cs4231_t + +MODULE_AUTHOR("Paul Barton-Davis "); +MODULE_DESCRIPTION("Turtle Beach Wavefront"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Turtle Beach,Maui/Tropez/Tropez+}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static long cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static long cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static long ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */ +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for WaveFront soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for WaveFront soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable WaveFront soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards."); +MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(cs4232_pcm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(cs4232_pcm_port, SNDRV_PORT12_DESC); +MODULE_PARM(cs4232_pcm_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(cs4232_pcm_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); +MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); +MODULE_PARM(cs4232_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface."); +MODULE_PARM_SYNTAX(cs4232_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(cs4232_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface."); +MODULE_PARM_SYNTAX(cs4232_mpu_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(ics2115_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115."); +MODULE_PARM_SYNTAX(ics2115_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(ics2115_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(ics2115_port, "Port # for ICS2115."); +MODULE_PARM_SYNTAX(ics2115_port, SNDRV_PORT12_DESC); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port #."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(use_cs4232_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)"); +MODULE_PARM_SYNTAX(use_cs4232_midi, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +static snd_card_t *snd_wavefront_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_wavefront_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_wavefront_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_wavefront_pnpids[] __devinitdata = { + { + ISAPNP_CARD_ID('C','S','C',0x7532), /* Tropez */ + .devs = { ISAPNP_DEVICE_ID('C','S','C',0x0000), /* WSS */ + ISAPNP_DEVICE_ID('C','S','C',0x0010), /* CTRL */ + ISAPNP_DEVICE_ID('P','n','P',0xb006), /* MPU */ + ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */ + }, + { + ISAPNP_CARD_ID('C','S','C',0x7632), /* Tropez+ */ + .devs = { ISAPNP_DEVICE_ID('C','S','C',0x0000), /* WSS */ + ISAPNP_DEVICE_ID('C','S','C',0x0010), /* CTRL */ + ISAPNP_DEVICE_ID('P','n','P',0xb006), /* MPU */ + ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */ + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_wavefront_pnpids); + +static int __init +snd_wavefront_isapnp (int dev, snd_wavefront_card_t *acard) +{ + const struct isapnp_card_id *id = snd_wavefront_isapnp_id[dev]; + struct isapnp_card *card = snd_wavefront_isapnp_cards[dev]; + struct isapnp_dev *pdev; + int tmp; + + /* Check for each logical device. */ + + /* CS4232 chip (aka "windows sound system") is logical device 0 */ + + acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->wss->active) { + acard->wss = NULL; + return -EBUSY; + } + + /* there is a game port at logical device 1, but we ignore it completely */ + + /* the control interface is logical device 2, but we ignore it + completely. in fact, nobody even seems to know what it + does. + */ + + /* Only configure the CS4232 MIDI interface if its been + specifically requested. It is logical device 3. + */ + + if (use_cs4232_midi[dev]) { + acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->mpu->active) { + acard->wss = acard->synth = acard->mpu = NULL; + return -EBUSY; + } + } + + /* The ICS2115 synth is logical device 4 */ + + acard->synth = isapnp_find_dev(card, id->devs[3].vendor, id->devs[3].function, NULL); + if (acard->synth->active) { + acard->wss = acard->synth = NULL; + return -EBUSY; + } + + /* PCM/FM initialization */ + + pdev = acard->wss; + + if ((tmp = pdev->prepare (pdev)) < 0) { + if (tmp == -EBUSY) { + snd_printk ("ISA PnP configuration appears to have " + "been done. Restart the isapnp module.\n"); + return 0; + } + snd_printk ("isapnp WSS preparation failed\n"); + return -EAGAIN; + } + + /* An interesting note from the Tropez+ FAQ: + + Q. [Ports] Why is the base address of the WSS I/O ports off by 4? + + A. WSS I/O requires a block of 8 I/O addresses ("ports"). Of these, the first + 4 are used to identify and configure the board. With the advent of PnP, + these first 4 addresses have become obsolete, and software applications + only use the last 4 addresses to control the codec chip. Therefore, the + base address setting "skips past" the 4 unused addresses. + + */ + + if (cs4232_pcm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], cs4232_pcm_port[dev], 4); + if (fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], fm_port[dev], 4); + if (dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], dma1[dev], 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], dma2[dev], 1); + if (cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], cs4232_pcm_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk ("isapnp WSS activation failed\n"); + return -EBUSY; + } + + cs4232_pcm_port[dev] = pdev->resource[0].start; + fm_port[dev] = pdev->resource[1].start; + dma1[dev] = pdev->dma_resource[0].start; + dma2[dev] = pdev->dma_resource[1].start; + cs4232_pcm_irq[dev] = pdev->irq_resource[0].start; + + /* Synth initialization */ + + pdev = acard->synth; + + if ((tmp = pdev->prepare(pdev))<0) { + if (tmp == -EBUSY) { + snd_printk ("ISA PnP configuration appears to have " + "been done. Restart the isapnp module.\n"); + } + acard->wss->deactivate(acard->wss); + snd_printk ("ICS2115 synth preparation failed\n"); + return -EAGAIN; + } + if (ics2115_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], ics2115_port[dev], 16); + } + + if (ics2115_port[dev] != SNDRV_AUTO_IRQ) { + isapnp_resource_change(&pdev->irq_resource[0], ics2115_irq[dev], 1); + } + + if (pdev->activate(pdev)<0) { + snd_printk("isapnp activation for ICS2115 failed\n"); + acard->wss->deactivate(acard->wss); + return -EBUSY; + } + + ics2115_port[dev] = pdev->resource[0].start; + ics2115_irq[dev] = pdev->irq_resource[0].start; + + /* CS4232 MPU initialization. Configure this only if + explicitly requested, since its physically inaccessible and + consumes another IRQ. + */ + + if (use_cs4232_midi[dev]) { + + pdev = acard->mpu; + + if (pdev->prepare(pdev)<0) { + acard->wss->deactivate(acard->wss); + if (acard->synth) + acard->synth->deactivate(acard->synth); + snd_printk ("CS4232 MPU preparation failed\n"); + return -EAGAIN; + } + + if (cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], cs4232_mpu_port[dev], 2); + if (cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->resource[0], cs4232_mpu_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("isapnp CS4232 MPU activation failed\n"); + cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; + } else { + cs4232_mpu_port[dev] = pdev->resource[0].start; + cs4232_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + snd_printk ("CS4232 MPU: port=0x%lx, irq=%i\n", + cs4232_mpu_port[dev], + cs4232_mpu_irq[dev]); + } + + snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n", + cs4232_pcm_port[dev], + fm_port[dev], + dma1[dev], + dma2[dev], + cs4232_pcm_irq[dev], + ics2115_port[dev], + ics2115_irq[dev]); + + return 0; +} + +static void +snd_wavefront_deactivate (snd_wavefront_card_t *acard) +{ + snd_printk ("deactivating PnP devices\n"); + if (acard->wss) { + acard->wss->deactivate(acard->wss); + acard->wss = NULL; + } + if (acard->ctrl) { + acard->ctrl->deactivate(acard->ctrl); + acard->ctrl = NULL; + } + if (acard->mpu) { + acard->mpu->deactivate(acard->mpu); + acard->mpu = NULL; + } + if (acard->synth) { + acard->synth->deactivate(acard->synth); + acard->synth = NULL; + } +} + +#endif /* __ISAPNP__ */ + +static void snd_wavefront_ics2115_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + snd_wavefront_card_t *acard; + + acard = (snd_wavefront_card_t *) dev_id; + + if (acard == NULL) + return; + + if (acard->wavefront.interrupts_are_midi) { + snd_wavefront_midi_interrupt (acard); + } else { + snd_wavefront_internal_interrupt (acard); + } +} + +snd_hwdep_t * __init +snd_wavefront_new_synth (snd_card_t *card, + int hw_dev, + snd_wavefront_card_t *acard) +{ + snd_hwdep_t *wavefront_synth; + + if (snd_wavefront_detect (acard) < 0) { + return NULL; + } + + if (snd_wavefront_start (&acard->wavefront) < 0) { + return NULL; + } + + if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) + return NULL; + strcpy (wavefront_synth->name, + "WaveFront (ICS2115) wavetable synthesizer"); + wavefront_synth->ops.open = snd_wavefront_synth_open; + wavefront_synth->ops.release = snd_wavefront_synth_release; + wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl; + + return wavefront_synth; +} + +snd_hwdep_t * __init +snd_wavefront_new_fx (snd_card_t *card, + int hw_dev, + snd_wavefront_card_t *acard, + unsigned long port) + +{ + snd_hwdep_t *fx_processor; + + if (snd_wavefront_fx_start (&acard->wavefront)) { + snd_printk ("cannot initialize YSS225 FX processor"); + return NULL; + } + + if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0) + return NULL; + sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port); + fx_processor->ops.open = snd_wavefront_fx_open; + fx_processor->ops.release = snd_wavefront_fx_release; + fx_processor->ops.ioctl = snd_wavefront_fx_ioctl; + + return fx_processor; +} + +static snd_wavefront_mpu_id internal_id = internal_mpu; +static snd_wavefront_mpu_id external_id = external_mpu; + +snd_rawmidi_t * __init +snd_wavefront_new_midi (snd_card_t *card, + int midi_dev, + snd_wavefront_card_t *acard, + unsigned long port, + snd_wavefront_mpu_id mpu) + +{ + snd_rawmidi_t *rmidi; + static int first = 1; + + if (first) { + first = 0; + acard->wavefront.midi.base = port; + if (snd_wavefront_midi_start (acard)) { + snd_printk ("cannot initialize MIDI interface\n"); + return NULL; + } + } + + if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0) + return NULL; + + if (mpu == internal_mpu) { + strcpy(rmidi->name, "WaveFront MIDI (Internal)"); + rmidi->private_data = &internal_id; + } else { + strcpy(rmidi->name, "WaveFront MIDI (External)"); + rmidi->private_data = &external_id; + } + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + return rmidi; +} + +static void +snd_wavefront_free(snd_card_t *card) +{ + snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_wavefront_deactivate(acard); +#endif + if (acard->wavefront.res_base != NULL) { + release_resource(acard->wavefront.res_base); + kfree_nocheck(acard->wavefront.res_base); + } + if (acard->wavefront.irq > 0) + free_irq(acard->wavefront.irq, (void *)acard); + } +} + +static int __init +snd_wavefront_probe (int dev) +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + cs4231_t *chip; + snd_hwdep_t *wavefront_synth; + snd_rawmidi_t *ics2115_internal_rmidi = NULL; + snd_rawmidi_t *ics2115_external_rmidi = NULL; + snd_hwdep_t *fx_processor; + int hw_dev = 0, midi_dev = 0, err; + + if (cs4232_mpu_port[dev] < 0) + cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; + if (fm_port[dev] < 0) + fm_port[dev] = SNDRV_AUTO_PORT; + if (ics2115_port[dev] < 0) + ics2115_port[dev] = SNDRV_AUTO_PORT; + +#ifdef __ISAPNP__ + if (!isapnp[dev]) { +#endif + if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify CS4232 port\n"); + return -EINVAL; + } + if (ics2115_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify ICS2115 port\n"); + return -ENODEV; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new (index[dev], + id[dev], + THIS_MODULE, + sizeof(snd_wavefront_card_t)); + + if (card == NULL) { + return -ENOMEM; + } + acard = (snd_wavefront_card_t *)card->private_data; + acard->wavefront.irq = -1; + spin_lock_init(&acard->wavefront.irq_lock); + init_waitqueue_head(&acard->wavefront.interrupt_sleeper); + spin_lock_init(&acard->wavefront.midi.open); + spin_lock_init(&acard->wavefront.midi.virtual); + card->private_free = snd_wavefront_free; + +#ifdef __ISAPNP__ + if (isapnp[dev] && snd_wavefront_isapnp (dev, acard) < 0) { + if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk ("isapnp detection failed\n"); + snd_card_free (card); + return -ENODEV; + } + } +#endif /* __ISAPNP__ */ + + /* --------- PCM --------------- */ + + if ((err = snd_cs4231_create (card, + cs4232_pcm_port[dev], + -1, + cs4232_pcm_irq[dev], + dma1[dev], + dma2[dev], + CS4231_HW_DETECT, 0, &chip)) < 0) { + snd_card_free(card); + snd_printk ("can't allocate CS4231 device\n"); + return err; + } + + if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + /* ---------- OPL3 synth --------- */ + + if (fm_port[dev] != SNDRV_AUTO_PORT) { + opl3_t *opl3; + + if ((err = snd_opl3_create(card, + fm_port[dev], + fm_port[dev] + 2, + OPL3_HW_OPL3_CS, + 0, &opl3)) < 0) { + snd_printk ("can't allocate or detect OPL3 synth\n"); + snd_card_free(card); + return err; + } + + if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + hw_dev++; + } + + /* ------- ICS2115 Wavetable synth ------- */ + + if ((acard->wavefront.res_base = request_region(ics2115_port[dev], 16, "ICS2115")) == NULL) { + snd_printk("unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", ics2115_port[dev], ics2115_port[dev] + 16 - 1); + snd_card_free(card); + return -EBUSY; + } + if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt, SA_INTERRUPT, "ICS2115", (void *)acard)) { + snd_printk("unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]); + snd_card_free(card); + return -EBUSY; + } + + acard->wavefront.irq = ics2115_irq[dev]; + acard->wavefront.base = ics2115_port[dev]; + + if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) { + snd_printk ("can't create WaveFront synth device\n"); + snd_card_free(card); + return -ENOMEM; + } + + strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); + wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; + hw_dev++; + + /* --------- Mixer ------------ */ + + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_printk ("can't allocate mixer device\n"); + snd_card_free(card); + return err; + } + + /* -------- CS4232 MPU-401 interface -------- */ + + if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { + if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, + cs4232_mpu_port[dev], 0, + cs4232_mpu_irq[dev], + SA_INTERRUPT, + NULL)) < 0) { + snd_printk ("can't allocate CS4232 MPU-401 device\n"); + snd_card_free(card); + return err; + } + midi_dev++; + } + + /* ------ ICS2115 internal MIDI ------------ */ + + if (ics2115_port[dev] >= 0 && ics2115_port[dev] != SNDRV_AUTO_PORT) { + ics2115_internal_rmidi = + snd_wavefront_new_midi (card, + midi_dev, + acard, + ics2115_port[dev], + internal_mpu); + if (ics2115_internal_rmidi == NULL) { + snd_printk ("can't setup ICS2115 internal MIDI device\n"); + snd_card_free(card); + return -ENOMEM; + } + midi_dev++; + } + + /* ------ ICS2115 external MIDI ------------ */ + + if (ics2115_port[dev] >= 0 && ics2115_port[dev] != SNDRV_AUTO_PORT) { + ics2115_external_rmidi = + snd_wavefront_new_midi (card, + midi_dev, + acard, + ics2115_port[dev], + external_mpu); + if (ics2115_external_rmidi == NULL) { + snd_printk ("can't setup ICS2115 external MIDI device\n"); + snd_card_free(card); + return -ENOMEM; + } + midi_dev++; + } + + /* FX processor for Tropez+ */ + + if (acard->wavefront.has_fx) { + fx_processor = snd_wavefront_new_fx (card, + hw_dev, + acard, + ics2115_port[dev]); + if (fx_processor == NULL) { + snd_printk ("can't setup FX device\n"); + snd_card_free(card); + return -ENOMEM; + } + + hw_dev++; + + strcpy(card->driver, "Tropez+"); + strcpy(card->shortname, "Turtle Beach Tropez+"); + } else { + /* Need a way to distinguish between Maui and Tropez */ + strcpy(card->driver, "WaveFront"); + strcpy(card->shortname, "Turtle Beach WaveFront"); + } + + /* ----- Register the card --------- */ + + /* Not safe to include "Turtle Beach" in longname, due to + length restrictions + */ + + sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d", + card->driver, + chip->port, + cs4232_pcm_irq[dev], + dma1[dev]); + + if (dma2[dev] >= 0 && dma2[dev] < 8) + sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); + + if (cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { + sprintf (card->longname + strlen (card->longname), + " MPU-401 0x%lx irq %d", + cs4232_mpu_port[dev], + cs4232_mpu_irq[dev]); + } + + sprintf (card->longname + strlen (card->longname), + " SYNTH 0x%lx irq %d", + ics2115_port[dev], + ics2115_irq[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_wavefront_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ + +static int __init snd_wavefront_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || !isapnp[dev]) + continue; + snd_wavefront_isapnp_cards[dev] = card; + snd_wavefront_isapnp_id[dev] = id; + res = snd_wavefront_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_wavefront_init(void) +{ + int cards = 0; + int dev; + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) + continue; +#ifdef __ISAPNP__ + if (isapnp[dev]) + continue; +#endif + if (snd_wavefront_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_wavefront_pnpids, snd_wavefront_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + printk (KERN_ERR "No WaveFront cards found or devices busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_wavefront_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_wavefront_cards[idx]); +} + +module_init(alsa_card_wavefront_init) +module_exit(alsa_card_wavefront_exit) + +#ifndef MODULE + +/* format is: snd-wavefront=enable,index,id,isapnp, + cs4232_pcm_port,cs4232_pcm_irq, + cs4232_mpu_port,cs4232_mpu_irq, + ics2115_port,ics2115_irq, + fm_port, + dma1,dma2, + use_cs4232_midi */ + +static int __init alsa_card_wavefront_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&isapnp[nr_dev]) == 2 && + get_option(&str,(int *)&cs4232_pcm_port[nr_dev]) == 2 && + get_option(&str,&cs4232_pcm_irq[nr_dev]) == 2 && + get_option(&str,(int *)&cs4232_mpu_port[nr_dev]) == 2 && + get_option(&str,&cs4232_mpu_irq[nr_dev]) == 2 && + get_option(&str,(int *)&ics2115_port[nr_dev]) == 2 && + get_option(&str,&ics2115_irq[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,&dma1[nr_dev]) == 2 && + get_option(&str,&dma2[nr_dev]) == 2 && + get_option(&str,&use_cs4232_midi[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-wavefront=", alsa_card_wavefront_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront_fx.c linux/sound/isa/wavefront/wavefront_fx.c --- linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront_fx.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/wavefront/wavefront_fx.c 2003-01-31 08:20:00.000000000 -0700 @@ -0,0 +1,1014 @@ +/* + * Copyright (c) 1998-2002 by Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 +wavefront_fx_idle (snd_wavefront_t *dev) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (dev->fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + snd_printk ("FX device never idle.\n"); + return 0; + } + + return (1); +} + +static void +wavefront_fx_mute (snd_wavefront_t *dev, int onoff) + +{ + if (!wavefront_fx_idle(dev)) { + return; + } + + outb (onoff ? 0x02 : 0x00, dev->fx_op); +} + +static int +wavefront_fx_memset (snd_wavefront_t *dev, + int page, + int addr, + int cnt, + unsigned short *data) +{ + if (page < 0 || page > 7) { + snd_printk ("FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + snd_printk ("FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (page, dev->fx_dsp_page); + outb (addr, dev->fx_dsp_addr); + outb ((data[0] >> 8), dev->fx_dsp_msb); + outb ((data[0] & 0xff), dev->fx_dsp_lsb); + + snd_printk ("FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (page, dev->fx_dsp_page); + outb (addr, dev->fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), dev->fx_dsp_msb); + outb ((data[i] & 0xff), dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) { + break; + } + } + + if (i != cnt) { + snd_printk ("FX memset " + "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", + page, addr, (unsigned long) data, cnt); + return -(EIO); + } + } + + return 0; +} + +int +snd_wavefront_fx_detect (snd_wavefront_t *dev) + +{ + /* This is a crude check, but its the best one I have for now. + Certainly on the Maui and the Tropez, wavefront_fx_idle() will + report "never idle", which suggests that this test should + work OK. + */ + + if (inb (dev->fx_status) & 0x80) { + snd_printk ("Hmm, probably a Maui or Tropez.\n"); + return -1; + } + + return 0; +} + +int +snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file) + +{ + if (!try_module_get(hw->card->module)) + return -EFAULT; + file->private_data = hw; + return 0; +} + +int +snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file) + +{ + module_put(hw->card->module); + return 0; +} + +int +snd_wavefront_fx_ioctl (snd_hwdep_t *sdev, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + snd_wavefront_t *dev; + wavefront_fx_info r; + unsigned short page_data[256]; + unsigned short *pd; + + snd_assert(sdev->card != NULL, return -ENODEV); + + card = sdev->card; + + snd_assert(card->private_data != NULL, return -ENODEV); + + acard = card->private_data; + dev = &acard->wavefront; + + if (copy_from_user (&r, (unsigned char *) arg, sizeof (wavefront_fx_info))) + return -EFAULT; + + switch (r.request) { + case WFFX_MUTE: + wavefront_fx_mute (dev, r.data[0]); + return -EIO; + + case WFFX_MEMSET: + if (r.data[2] <= 0) { + snd_printk ("cannot write " + "<= 0 bytes to FX\n"); + return -EIO; + } else if (r.data[2] == 1) { + pd = (unsigned short *) &r.data[3]; + } else { + if (r.data[2] > (long)sizeof (page_data)) { + snd_printk ("cannot write " + "> 255 bytes to FX\n"); + return -EIO; + } + if (copy_from_user (page_data, + (unsigned char *) r.data[3], + r.data[2])) + return -EFAULT; + pd = page_data; + } + + wavefront_fx_memset (dev, + r.data[0], /* page */ + r.data[1], /* addr */ + r.data[2], /* cnt */ + pd); + break; + + default: + snd_printk ("FX: ioctl %d not yet supported\n", + r.request); + return -ENOTTY; + } + return 0; +} + +/* 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. Its really pretty wierd. + + 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. +*/ + + +int __init +snd_wavefront_fx_start (snd_wavefront_t *dev) + +{ + unsigned int i, 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 (!wavefront_fx_idle (dev)) { + return (-1); + } + + outb (i, dev->fx_mod_addr); + outb (0x0, dev->fx_mod_data); + } + } + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x02, dev->fx_op); /* mute on */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x44, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x42, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x43, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x7c, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x7e, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x46, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x49, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x47, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x4a, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->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 (!wavefront_fx_idle (dev)) { + return (-1); + } + + outb (i, dev->fx_mod_addr); + outb (0x0, dev->fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x00, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], dev->fx_dsp_msb); + outb (page_zero[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x01, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], dev->fx_dsp_msb); + outb (page_one[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x02, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x03, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x04, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x06, dev->fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], dev->fx_dsp_addr); + outb (page_six[i+1], dev->fx_dsp_msb); + outb (page_six[i+2], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x07, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], dev->fx_dsp_msb); + outb (page_seven[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) 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, dev->fx_mod_addr); + outb (i, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x02, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, dev->fx_mod_addr); + outb (0xff, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x1e, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, dev->fx_mod_addr); + outb (0xff, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x2e, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x3f, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x4e, dev->fx_mod_addr); + outb (0x0e, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x4f, dev->fx_mod_addr); + outb (0x0e, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x6c, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6d, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6e, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6f, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, dev->fx_mod_addr); + outb (0xc0, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0xde, dev->fx_mod_addr); + outb (0x10, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xdf, dev->fx_mod_addr); + outb (0x10, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev->fx_mod_addr); + outb (i, dev->fx_mod_data); + outb (0x02, dev->fx_mod_addr); + outb (0x01, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x02, dev->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], dev->fx_dsp_page); + outb (coefficients[i+1], dev->fx_dsp_addr); + outb (coefficients[i+2], dev->fx_dsp_msb); + outb (coefficients[i+3], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x1e, dev->fx_mod_addr); + outb (0x14, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xde, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xdf, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + + /* some more coefficients */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x06, dev->fx_dsp_page); + outb (0x78, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x40, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x03, dev->fx_dsp_addr); + outb (0x0f, dev->fx_dsp_msb); + outb (0xff, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x0b, dev->fx_dsp_addr); + outb (0x0f, dev->fx_dsp_msb); + outb (0xff, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x02, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x0a, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x46, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x49, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->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, dev->fx_lcr); + outb (0x00, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], dev->fx_dsp_msb); + outb (page_zero_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x01, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], dev->fx_dsp_msb); + outb (page_one_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + if (!wavefront_fx_idle (dev)) return (-1); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x02, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x03, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x04, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x06, dev->fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x07, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], dev->fx_dsp_msb); + outb (page_seven_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], dev->fx_mod_addr); + outb (mod_v2[i+1], dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], dev->fx_dsp_page); + outb (coefficients2[i+1], dev->fx_dsp_addr); + outb (coefficients2[i+2], dev->fx_dsp_msb); + outb (coefficients2[i+3], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, dev->fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, dev->fx_dsp_addr); + outb (coefficients3[i], dev->fx_dsp_msb); + outb (coefficients3[i+1], dev->fx_dsp_lsb); + } + + outb (0x00, dev->fx_op); /* mute off */ + if (!wavefront_fx_idle (dev)) return (-1); + + return (0); +} + +/* wierd stuff, derived from port I/O tracing with dosemu */ + +static unsigned char page_zero[] __initdata = { +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 +}; + +static unsigned char page_one[] __initdata = { +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 +}; + +static unsigned char page_two[] __initdata = { +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 +}; + +static unsigned char page_three[] __initdata = { +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 +}; + +static unsigned char page_four[] __initdata = { +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 +}; + +static unsigned char page_six[] __initdata = { +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 +}; + +static unsigned char page_seven[] __initdata = { +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 +}; + +static unsigned char page_zero_v2[] __initdata = { +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 +}; + +static unsigned char page_one_v2[] __initdata = { +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 +}; + +static unsigned char page_two_v2[] __initdata = { +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 +}; +static unsigned char page_three_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +static unsigned char page_four_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_seven_v2[] __initdata = { +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 +}; + +static unsigned char mod_v2[] __initdata = { +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 +}; +static unsigned char coefficients[] __initdata = { +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 +}; +static unsigned char coefficients2[] __initdata = { +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 +}; +static unsigned char coefficients3[] __initdata = { +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 -urN linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront_midi.c linux/sound/isa/wavefront/wavefront_midi.c --- linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront_midi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/wavefront/wavefront_midi.c 2002-11-23 03:41:53.000000000 -0700 @@ -0,0 +1,574 @@ +/* + * Copyright (C) by Paul Barton-Davis 1998-1999 + * + * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this + * software for more info. + */ + +/* 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 and 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 + * busses to be used completely independently, giving 32 channels of + * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI + * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1, + * where `n' is the card number. Note that the device numbers may be + * something other than 0 and 1 if the CS4232 UART/MPU-401 interface + * is enabled. + * + * 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 lowlevel/isa/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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static inline int +wf_mpu_status (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_status_port); +} + +static inline int +input_avail (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & INPUT_AVAIL); +} + +static inline int +output_ready (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & OUTPUT_READY); +} + +static inline int +read_data (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_data_port); +} + +static inline void +write_data (snd_wavefront_midi_t *midi, unsigned char byte) + +{ + outb (byte, midi->mpu_data_port); +} + +static snd_wavefront_midi_t * +get_wavefront_midi (snd_rawmidi_substream_t *substream) + +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + + if (substream == NULL || substream->rmidi == NULL) + return NULL; + + card = substream->rmidi->card; + + if (card == NULL) + return NULL; + + if (card->private_data == NULL) + return NULL; + + acard = card->private_data; + + return &acard->wavefront.midi; +} + +static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card) +{ + snd_wavefront_midi_t *midi = &card->wavefront.midi; + snd_wavefront_mpu_id mpu; + unsigned long flags; + unsigned char midi_byte; + int max = 256, mask = 1; + int timeout; + + /* Its not OK to try to change the status of "virtuality" of + the MIDI interface while we're outputting stuff. See + snd_wavefront_midi_{enable,disable}_virtual () for the + other half of this. + + The first loop attempts to flush any data from the + current output device, and then the second + emits the switch byte (if necessary), and starts + outputting data for the output device currently in use. + */ + + if (midi->substream_output[midi->output_mpu] == NULL) { + goto __second; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + if (output_ready (midi)) { + if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } + + __second: + + if (midi->substream_output[!midi->output_mpu] == NULL) { + return; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (!midi->isvirtual) + mask = 0; + mpu = midi->output_mpu ^ mask; + mask = 0; /* don't invert the value from now */ + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + if (snd_rawmidi_transmit_empty(midi->substream_output[mpu])) + goto __timer; + if (output_ready (midi)) { + if (mpu != midi->output_mpu) { + write_data(midi, mpu == internal_mpu ? + WF_INTERNAL_SWITCH : + WF_EXTERNAL_SWITCH); + midi->output_mpu = mpu; + } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + __timer: + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } +} + +static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_INPUT; + midi->substream_input[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_OUTPUT; + midi->substream_output[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_INPUT; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; + spin_unlock_irqrestore (&midi->open, flags); + return 0; +} + +static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) { + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER; + } else { + midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); +} + +static void snd_wavefront_midi_output_timer(unsigned long data) +{ + snd_wavefront_card_t *card = (snd_wavefront_card_t *)data; + snd_wavefront_midi_t *midi = &card->wavefront.midi; + unsigned long flags; + + spin_lock_irqsave (&midi->virtual, flags); + midi->timer.expires = 1 + jiffies; + add_timer(&midi->timer); + spin_unlock_irqrestore (&midi->virtual, flags); + snd_wavefront_midi_output_write(card); +} + +static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) { + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { + if (!midi->istimer) { + init_timer(&midi->timer); + midi->timer.function = snd_wavefront_midi_output_timer; + midi->timer.data = (unsigned long) substream->rmidi->card->private_data; + midi->timer.expires = 1 + jiffies; + add_timer(&midi->timer); + } + midi->istimer++; + midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER; + } + } else { + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); + + if (up) + snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data); +} + +void +snd_wavefront_midi_interrupt (snd_wavefront_card_t *card) + +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + static snd_rawmidi_substream_t *substream = NULL; + static int mpu = external_mpu; + int max = 128; + unsigned char byte; + + midi = &card->wavefront.midi; + + if (!input_avail (midi)) { /* not for us */ + snd_wavefront_midi_output_write(card); + return; + } + + while (--max) { + spin_lock_irqsave (&midi->virtual, flags); + + if (input_avail (midi)) { + byte = read_data (midi); + + if (midi->isvirtual) { + if (byte == WF_EXTERNAL_SWITCH) { + substream = midi->substream_input[external_mpu]; + mpu = external_mpu; + } else if (byte == WF_INTERNAL_SWITCH) { + substream = midi->substream_output[internal_mpu]; + mpu = internal_mpu; + } /* else just leave it as it is */ + } else { + substream = midi->substream_input[internal_mpu]; + mpu = internal_mpu; + } + + if (substream == NULL) { + spin_unlock_irqrestore (&midi->virtual, flags); + continue; + } + + if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) { + spin_unlock_irqrestore (&midi->virtual, flags); + snd_rawmidi_receive(substream, &byte, 1); + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + break; + } + } + + snd_wavefront_midi_output_write(card); +} + +void +snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + card->wavefront.midi.isvirtual = 1; + card->wavefront.midi.output_mpu = internal_mpu; + card->wavefront.midi.input_mpu = internal_mpu; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +void +snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + // snd_wavefront_midi_input_close (card->ics2115_external_rmidi); + // snd_wavefront_midi_output_close (card->ics2115_external_rmidi); + card->wavefront.midi.isvirtual = 0; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +int __init +snd_wavefront_midi_start (snd_wavefront_card_t *card) + +{ + int ok, i; + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev; + snd_wavefront_midi_t *midi; + + dev = &card->wavefront; + midi = &dev->midi; + + /* The ICS2115 MPU-401 interface doesn't do anything + until its set into UART mode. + */ + + /* XXX fix me - no hard timing loops allowed! */ + + for (i = 0; i < 30000 && !output_ready (midi); i++); + + if (!output_ready (midi)) { + snd_printk ("MIDI interface not ready for command\n"); + return -1; + } + + /* Any interrupts received from now on + are owned by the MIDI side of things. + */ + + dev->interrupts_are_midi = 1; + + outb (UART_MODE_ON, midi->mpu_command_port); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail (midi)) { + if (read_data (midi) == MPU_ACK) { + ok = 1; + break; + } + } + } + + if (!ok) { + snd_printk ("cannot set UART mode for MIDI interface"); + dev->interrupts_are_midi = 0; + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) { + snd_printk ("can't enable MIDI-IN-2-synth routing.\n"); + /* XXX error ? */ + } + + /* 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 (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { + snd_printk ("virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + snd_wavefront_midi_enable_virtual (card); + + if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) { + snd_printk ("cannot enable virtual MIDI mode.\n"); + snd_wavefront_midi_disable_virtual (card); + } + return 0; +} + +snd_rawmidi_ops_t snd_wavefront_midi_output = +{ + .open = snd_wavefront_midi_output_open, + .close = snd_wavefront_midi_output_close, + .trigger = snd_wavefront_midi_output_trigger, +}; + +snd_rawmidi_ops_t snd_wavefront_midi_input = +{ + .open = snd_wavefront_midi_input_open, + .close = snd_wavefront_midi_input_close, + .trigger = snd_wavefront_midi_input_trigger, +}; + diff -urN linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront_synth.c linux/sound/isa/wavefront/wavefront_synth.c --- linux-2.4.21-rc1.orig/sound/isa/wavefront/wavefront_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/isa/wavefront/wavefront_synth.c 2003-01-31 08:20:00.000000000 -0700 @@ -0,0 +1,2204 @@ +/* Copyright (C) by Paul Barton-Davis 1998-1999 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * 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. + */ + +/* + * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth + * (Maui, Tropez, Tropez Plus) + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int wf_raw = 0; /* we normally check for "raw state" to firmware + loading. if non-zero, 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 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 *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +int wait_usecs = 150; /* This magic number seems to give pretty optimal + throughput based on my limited experimentation. + If you want to play around with it and find a better + value, be my guest. Remember, the idea is to + get a number that causes us to just busy wait + for as many WaveFront commands as possible, without + coming up with a number so large that we hog the + whole CPU. + + Specifically, with this number, out of about 134,000 + status waits, only about 250 result in a sleep. + */ + +int sleep_interval = 100; /* HZ/sleep_interval seconds per sleep */ +int sleep_tries = 50; /* number of times we'll try to sleep */ + +int reset_time = 2; /* hundreths of a second we wait after a HW + reset for the expected interrupt. + */ + +int ramcheck_time = 20; /* time in seconds to wait while ROM code + checks on-board RAM. + */ + +int osrun_time = 10; /* time in seconds we wait for the OS to + start running. + */ +MODULE_PARM(wf_raw,"i"); +MODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help"); +MODULE_PARM(debug_default,"i"); +MODULE_PARM_DESC(debug_default, "debug parameters for card initialization"); +MODULE_PARM(wait_usecs,"i"); +MODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs"); +MODULE_PARM(sleep_interval,"i"); +MODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply"); +MODULE_PARM(sleep_tries,"i"); +MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait"); +MODULE_PARM(ospath,"s"); +MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware"); +MODULE_PARM(reset_time,"i"); +MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect"); +MODULE_PARM(ramcheck_time,"i"); +MODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test"); +MODULE_PARM(osrun_time,"i"); +MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS"); + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +#ifdef WF_DEBUG + +#if defined(NEW_MACRO_VARARGS) || __GNUC__ >= 3 +#define DPRINT(cond, ...) \ + if ((dev->debug & (cond)) == (cond)) { \ + snd_printk (__VA_ARGS__); \ + } +#else +#define DPRINT(cond, args...) \ + if ((dev->debug & (cond)) == (cond)) { \ + snd_printk (##args); \ + } +#endif +#else +#define DPRINT(cond, args...) +#endif /* WF_DEBUG */ + +#define LOGNAME "WaveFront: " + +/* bitmasks for WaveFront status port value */ + +#define STAT_RINTR_ENABLED 0x01 +#define STAT_CAN_READ 0x02 +#define STAT_INTR_READ 0x04 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_CAN_WRITE 0x20 +#define STAT_INTR_WRITE 0x40 + +static int wavefront_delete_sample (snd_wavefront_t *, int sampnum); +static int wavefront_find_free_sample (snd_wavefront_t *); + +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 snd_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 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +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 inline int +wavefront_status (snd_wavefront_t *dev) + +{ + return inb (dev->status_port); +} + +static int +wavefront_sleep (int limit) + +{ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(limit); + + return signal_pending(current); +} + +static int +wavefront_wait (snd_wavefront_t *dev, int mask) + +{ + int i; + + /* Spin for a short period of time, because >99% of all + requests to the WaveFront can be serviced inline like this. + */ + + for (i = 0; i < wait_usecs; i += 5) { + if (wavefront_status (dev) & mask) { + return 1; + } + udelay(5); + } + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status (dev) & mask) { + return 1; + } + + if (wavefront_sleep (HZ/sleep_interval)) { + return (0); + } + } + + return (0); +} + +static int +wavefront_read (snd_wavefront_t *dev) + +{ + if (wavefront_wait (dev, STAT_CAN_READ)) + return inb (dev->data_port); + + DPRINT (WF_DEBUG_DATA, "read timeout.\n"); + + return -1; +} + +static int +wavefront_write (snd_wavefront_t *dev, unsigned char data) + +{ + if (wavefront_wait (dev, STAT_CAN_WRITE)) { + outb (data, dev->data_port); + return 0; + } + + DPRINT (WF_DEBUG_DATA, "write timeout.\n"); + + return -1; +} + +int +snd_wavefront_cmd (snd_wavefront_t *dev, + int cmd, unsigned char *rbuf, unsigned char *wbuf) + +{ + int ack; + unsigned int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + snd_printk ("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 long) rbuf; + rbuf = 0; + } + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + + if (wavefront_write (dev, cmd)) { + DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + } + + if (wfcmd->write_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (wavefront_write (dev, wbuf[i])) { + DPRINT (WF_DEBUG_IO, "bad write for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + + if (wfcmd->read_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_IO, "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 (dev)) == -1) { + DPRINT (WF_DEBUG_IO, "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 { + + DPRINT (WF_DEBUG_IO, "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; + } + + DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read (dev)) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + DPRINT (WF_DEBUG_IO, "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 (dev)) == -1) { + DPRINT (WF_DEBUG_DATA, + "cannot read err " + "for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + + DPRINT (WF_DEBUG_IO, "0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + + return -err; + } + } + + DPRINT (WF_DEBUG_DATA, "ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } else { + + DPRINT (WF_DEBUG_CMD, "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 wierd. 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-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 +wierd casting and worrying about bit-level offsets. + +**********************************************************************/ + +static unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + unsigned 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) + +{ + unsigned 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 (snd_wavefront_t *dev, int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + dev->sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) { + snd_printk ("cannot request sample count.\n"); + return -1; + } + + sc_real = sc_alias = sc_multi = dev->samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + snd_printk("cannot identify sample " + "type of slot %d\n", i); + dev->sample_status[i] = WF_ST_EMPTY; + continue; + } + + dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + dev->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: + snd_printk ("unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + dev->samples_used++; + } + } + + snd_printk ("%d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - dev->samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (snd_wavefront_t *dev) + +{ + 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 = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + dev->patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + dev->sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + dev->patch_status[i] = 0; + } else { + snd_printk ("upload patch " + "error 0x%x\n", x); + dev->patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (dev->patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (dev->patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (snd_wavefront_t *dev) + +{ + 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 = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + dev->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) { + dev->patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + dev->prog_status[i] = 0; + } else { + snd_printk ("upload program " + "error 0x%x\n", x); + dev->prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (dev->prog_status[i]) { + cnt++; + } + } + + snd_printk ("%d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", + header->number); + + dev->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 (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, 0, buf)) { + snd_printk ("download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", + header->number); + + dev->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) { + dev->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 (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, 0, buf)) { + snd_printk ("download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (snd_wavefront_t *dev) + +{ + char rbuf[8]; + + if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + snd_printk ("can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (snd_wavefront_t *dev, + wavefront_patch_info *header, + u16 *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. + */ + + u16 sample_short; + u32 length; + u16 *data_end = 0; + unsigned int i; + const unsigned 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; + + DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%lx\n", + header->size ? "" : "header ", + header->number, header->subkey, + header->size, + (unsigned long) header->dataptr); + + if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { + int x; + + if ((x = wavefront_find_free_sample (dev)) < 0) { + return -ENOMEM; + } + snd_printk ("unspecified sample => %d\n", x); + header->number = x; + } + + 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 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (dev->rom_samples_rdonly) { + if (dev->sample_status[header->number] & WF_SLOT_ROM) { + snd_printk ("sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (dev, header->number); + } + + if (header->size) { + dev->freemem = wavefront_freemem (dev); + + if (dev->freemem < (int)header->size) { + snd_printk ("insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { + snd_printk ("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; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "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 (*((u32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly wierd. What kind of wierdo decided that in + a system dominated by 16 and 32 bit integers, they would use + a just 12 bits ? + */ + + 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 (snd_wavefront_cmd (dev, + header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + snd_printk ("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 (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, 0, 0)) { + snd_printk ("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) { /* GUS ? */ + + 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, dev->block_port); + } else { + outw (sample_short, dev->last_block_port); + } + } + + /* Get "DMA page acknowledge", even though its really + nothing to do with DMA at all. + */ + + if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) { + if (dma_ack == -1) { + snd_printk ("upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + snd_printk ("upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + dev->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 (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + DPRINT (WF_DEBUG_LOAD_PATCH, "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 (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + snd_printk ("download alias failed.\n"); + return -(EIO); + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (snd_wavefront_t *dev, 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; + + DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", + header->number, + header->hdr.ms.NumberOfSamples, + num_samples); + + for (i = 0; i < num_samples; i++) { + DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "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 (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) (long) ((num_samples*2)+3), + msample_hdr)) { + snd_printk ("download of multisample failed.\n"); + return -(EIO); + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (snd_wavefront_t *dev, + 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 (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + snd_printk ("upload multisample failed.\n"); + return -(EIO); + } + + DPRINT (WF_DEBUG_DATA, "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]; + int val; + + if ((val = wavefront_read (dev)) == -1) { + snd_printk ("upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + d[0] = val; + + if ((val = wavefront_read (dev)) == -1) { + snd_printk ("upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + d[1] = val; + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + + return (0); +} + + +static int +wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "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 (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + snd_printk ("download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (snd_wavefront_t *dev) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(dev->sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + snd_printk ("no free sample slots!\n"); + return -1; +} + +#if 0 +static int +wavefront_find_free_patch (snd_wavefront_t *dev) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(dev->patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + snd_printk ("no free patch slots!\n"); + return -1; +} +#endif + +static int +wavefront_load_patch (snd_wavefront_t *dev, const char *addr) + +{ + wavefront_patch_info header; + + if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - + sizeof(wavefront_any))) { + snd_printk ("bad address for load patch.\n"); + return -(EFAULT); + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "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 */ + + if (copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample))) + return -EFAULT; + + return wavefront_send_sample (dev, &header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + if (copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample))) + return -EFAULT; + + return wavefront_send_multisample (dev, &header); + + + case WF_ST_ALIAS: + + if (copy_from_user ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias))) + return -EFAULT; + + return wavefront_send_alias (dev, &header); + + case WF_ST_DRUM: + if (copy_from_user ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum))) + return -EFAULT; + + return wavefront_send_drum (dev, &header); + + case WF_ST_PATCH: + if (copy_from_user ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch))) + return -EFAULT; + + return wavefront_send_patch (dev, &header); + + case WF_ST_PROGRAM: + if (copy_from_user ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program))) + return -EFAULT; + + return wavefront_send_program (dev, &header); + + default: + snd_printk ("unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: hardware-dependent interface +***********************************************************************/ + +static void +process_sample_hdr (u8 *buf) + +{ + wavefront_sample s; + u8 *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. + */ + + *((u32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &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 (snd_wavefront_card_t *acard, + wavefront_control *wc) + +{ + snd_wavefront_t *dev = &acard->wavefront; + unsigned char patchnumbuf[2]; + int i; + + DPRINT (WF_DEBUG_CMD, "synth control with " + "cmd 0x%x\n", wc->cmd); + + /* Pre-handling of or for various commands */ + + switch (wc->cmd) { + + case WFC_DISABLE_INTERRUPTS: + snd_printk ("interrupts disabled.\n"); + outb (0x80|0x20, dev->control_port); + dev->interrupts_are_midi = 1; + return 0; + + case WFC_ENABLE_INTERRUPTS: + snd_printk ("interrupts enabled.\n"); + outb (0x80|0x40|0x20, dev->control_port); + dev->interrupts_are_midi = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc->rbuf[0] = dev->interrupts_are_midi; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + dev->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) { + snd_printk ("invalid slot ID %d\n", + i); + wc->status = EINVAL; + return -EINVAL; + } + wc->rbuf[0] = dev->sample_status[i]; + wc->status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + dev->debug = wc->wbuf[0]; + snd_printk ("debug = 0x%x\n", dev->debug); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((u32 *) wc->wbuf), patchnumbuf, 2); + memcpy (wc->wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + /* multisamples have to be handled differently, and + cannot be dealt with properly by snd_wavefront_cmd() alone. + */ + wc->status = wavefront_fetch_multisample + (dev, (wavefront_patch_info *) wc->rbuf); + return 0; + + case WFC_UPLOAD_SAMPLE_ALIAS: + snd_printk ("support for sample alias upload " + "being considered.\n"); + wc->status = EINVAL; + return -EINVAL; + } + + wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf); + + /* Post-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: + dev->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_SAMPLE_ALIAS: + snd_printk ("support for " + "sample aliases still " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + snd_wavefront_midi_disable_virtual (acard); + break; + + case WFC_VMIDI_ON: + snd_wavefront_midi_enable_virtual (acard); + break; + } + } + + return 0; +} + +int +snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file) + +{ + if (!try_module_get(hw->card->module)) + return -EFAULT; + file->private_data = hw; + return 0; +} + +int +snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file) + +{ + module_put(hw->card->module); + return 0; +} + +int +snd_wavefront_synth_ioctl (snd_hwdep_t *hw, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + snd_card_t *card; + snd_wavefront_t *dev; + snd_wavefront_card_t *acard; + wavefront_control wc; + + card = (snd_card_t *) hw->card; + + snd_assert(card != NULL, return -ENODEV); + + snd_assert(card->private_data != NULL, return -ENODEV); + + acard = card->private_data; + dev = &acard->wavefront; + + switch (cmd) { + case WFCTL_LOAD_SPP: + if (wavefront_load_patch (dev, (char *) arg) != 0) { + return -EIO; + } + break; + + case WFCTL_WFCMD: + if (copy_from_user (&wc, (void *) arg, sizeof (wc))) + return -EFAULT; + if (wavefront_synth_control (acard, &wc) < 0) { + return -EIO; + } + if (copy_to_user ((void *) arg, &wc, sizeof (wc))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + + +/***********************************************************************/ +/* WaveFront: interface for card-level wavefront module */ +/***********************************************************************/ + +void +snd_wavefront_internal_interrupt (snd_wavefront_card_t *card) +{ + snd_wavefront_t *dev = &card->wavefront; + + /* + Some comments on interrupts. I attempted a version of this + driver that used interrupts throughout the code instead of + doing busy and/or sleep-waiting. Alas, it appears that once + the Motorola firmware is downloaded, the card *never* + generates an RX interrupt. These are successfully generated + during firmware loading, and after that wavefront_status() + reports that an interrupt is pending on the card from time + to time, but it never seems to be delivered to this + driver. Note also that wavefront_status() continues to + report that RX interrupts are enabled, suggesting that I + didn't goof up and disable them by mistake. + + Thus, I stepped back to a prior version of + wavefront_wait(), the only place where this really + matters. Its sad, but I've looked through the code to check + on things, and I really feel certain that the Motorola + firmware prevents RX-ready interrupts. + */ + + if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { + return; + } + + spin_lock(&dev->irq_lock); + dev->irq_ok = 1; + dev->irq_cnt++; + spin_unlock(&dev->irq_lock); + wake_up(&dev->interrupt_sleeper); +} + +/* 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 +*/ + +int __init +snd_wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + snd_printk ("invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +static void __init +wavefront_should_cause_interrupt (snd_wavefront_t *dev, + int val, int port, int timeout) + +{ + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + spin_lock_irq(&dev->irq_lock); + add_wait_queue(&dev->interrupt_sleeper, &wait); + dev->irq_ok = 0; + outb (val,port); + spin_unlock_irq(&dev->irq_lock); + while (1) { + if ((timeout = schedule_timeout(timeout)) == 0) + return; + if (dev->irq_ok) + return; + } +} + +static int __init +wavefront_reset_to_cleanliness (snd_wavefront_t *dev) + +{ + int bits; + int hwv[2]; + + /* IRQ already checked */ + + bits = snd_wavefront_interrupt_bits (dev->irq); + + /* try reset of port */ + + outb (0x0, dev->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 + + 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, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + outb (0x80 | 0x40 | bits, dev->data_port); + + /* 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 + + Take us out of reset, mute output, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. + */ + + wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1, + dev->control_port, + (reset_time*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!dev->irq_ok) { + snd_printk ("intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + XXX Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, + dev->data_port, ramcheck_time*HZ); + + if (!dev->irq_ok) { + snd_printk ("post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!wavefront_wait (dev, STAT_CAN_READ)) { + snd_printk ("no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read (dev)) == -1) { + snd_printk ("board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read (dev)) == -1) { + snd_printk ("on-board RAM test failed " + "(bad error code).\n"); + } else { + snd_printk ("on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read (dev)) == -1) { + snd_printk ("incorrect h/w response.\n"); + goto gone_bad; + } + + snd_printk ("hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + return (1); +} + +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include + +static int errno; + +static int __init +wavefront_download_firmware (snd_wavefront_t *dev, 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) { + snd_printk ("Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + snd_printk ("firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + snd_printk ("firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (wavefront_write (dev, WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (wavefront_write (dev, section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (dev, STAT_CAN_READ)) { + + if ((c = inb (dev->data_port)) != WF_ACK) { + + snd_printk ("download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } + + } else { + snd_printk ("time out for firmware ACK.\n"); + goto failure; + } + + } + + close (fd); + set_fs (fs); + return 0; + + failure: + close (fd); + set_fs (fs); + snd_printk ("firmware download failed!!!\n"); + return 1; +} + + +static int __init +wavefront_do_reset (snd_wavefront_t *dev) + +{ + char voices[1]; + + if (wavefront_reset_to_cleanliness (dev)) { + snd_printk ("hw reset failed.\n"); + goto gone_bad; + } + + if (dev->israw) { + if (wavefront_download_firmware (dev, ospath)) { + goto gone_bad; + } + + dev->israw = 0; + + /* Wait for the OS to get running. The protocol for + this is non-obvious, and was determined by + using port-IO tracing in DOSemu and some + experimentation here. + + Rather than using timed waits, use interrupts creatively. + */ + + wavefront_should_cause_interrupt (dev, WFC_NOOP, + dev->data_port, + (osrun_time*HZ)); + + if (!dev->irq_ok) { + snd_printk ("no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (dev, WFC_NOOP, + dev->data_port, (10*HZ)); + + if (!dev->irq_ok) { + snd_printk ("no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + in effect. + */ + + outb (0x80|0x40, dev->control_port); + } + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((dev->freemem = wavefront_freemem (dev)) < 0) { + goto gone_bad; + } + + snd_printk ("available DRAM %dk\n", dev->freemem / 1024); + + if (wavefront_write (dev, 0xf0) || + wavefront_write (dev, 1) || + (wavefront_read (dev) < 0)) { + dev->debug = 0; + snd_printk ("MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, 0, voices)) { + snd_printk ("cannot set number of voices to 32.\n"); + goto gone_bad; + } + + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, dev->control_port); + dev->interrupts_are_midi = 0; + return 1; +} + +int __init +snd_wavefront_start (snd_wavefront_t *dev) + +{ + int samples_are_from_rom; + + /* IMPORTANT: assumes that snd_wavefront_detect() and/or + wavefront_reset_to_cleanliness() has already been called + */ + + if (dev->israw) { + samples_are_from_rom = 1; + } else { + /* XXX is this always true ? */ + samples_are_from_rom = 0; + } + + if (dev->israw || fx_raw) { + if (wavefront_do_reset (dev)) { + return -1; + } + } + /* Check for FX device, present only on Tropez+ */ + + dev->has_fx = (snd_wavefront_fx_detect (dev) == 0); + + if (dev->has_fx && fx_raw) { + snd_wavefront_fx_start (dev); + } + + wavefront_get_sample_status (dev, samples_are_from_rom); + wavefront_get_program_status (dev); + wavefront_get_patch_status (dev); + + /* Start normal operation: unreset, master interrupt enabled, no mute + */ + + outb (0x80|0x40|0x20, dev->control_port); + + return (0); +} + +int __init +snd_wavefront_detect (snd_wavefront_card_t *card) + +{ + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev = &card->wavefront; + + /* returns zero if a WaveFront card is successfully detected. + negative otherwise. + */ + + dev->israw = 0; + dev->has_fx = 0; + dev->debug = debug_default; + dev->interrupts_are_midi = 0; + dev->irq_cnt = 0; + dev->rom_samples_rdonly = 1; + + if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + dev->fw_version[0] = rbuf[0]; + dev->fw_version[1] = rbuf[1]; + + snd_printk ("firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + dev->hw_version[0] = rbuf[0]; + dev->hw_version[1] = rbuf[1]; + } else { + snd_printk ("not raw, but no " + "hardware version!\n"); + return -1; + } + + if (!wf_raw) { + return 0; + } else { + snd_printk ("reloading firmware as you requested.\n"); + dev->israw = 1; + } + + } else { + + dev->israw = 1; + snd_printk ("no response to firmware probe, assume raw.\n"); + + } + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/last.c linux/sound/last.c --- linux-2.4.21-rc1.orig/sound/last.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/last.c 2002-06-18 02:13:33.000000000 -0600 @@ -0,0 +1,42 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MAIN_OBJECT_FILE +#include +#include +#include + +static int __init alsa_sound_last_init(void) +{ + int idx, ok = 0; + + printk(KERN_INFO "ALSA device list:\n"); + for (idx = 0; idx < SNDRV_CARDS; idx++) + if (snd_cards[idx] != NULL) { + printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname); + ok++; + } + if (ok == 0) + printk(KERN_INFO " No soundcards found.\n"); + return 0; +} + +__initcall(alsa_sound_last_init); diff -urN linux-2.4.21-rc1.orig/sound/pci/Config.in linux/sound/pci/Config.in --- linux-2.4.21-rc1.orig/sound/pci/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/Config.in 2003-04-29 05:16:36.000000000 -0600 @@ -0,0 +1,44 @@ +# ALSA PCI drivers + +mainmenu_option next_comment +comment 'PCI devices' + +dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND +dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND +dep_mbool ' Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)' CONFIG_SND_CS46XX_NEW_DSP $CONFIG_SND_CS46XX $CONFIG_EXPERIMENTAL +dep_tristate 'Cirrus Logic (Sound Fusion) CS4281' CONFIG_SND_CS4281 $CONFIG_SND +dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND +dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND +dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND +dep_tristate 'RME Digi32, 32/8, 32 PRO' CONFIG_SND_RME32 $CONFIG_SND +dep_tristate 'RME Digi96, 96/8, 96/8 PRO' CONFIG_SND_RME96 $CONFIG_SND +dep_tristate 'RME Digi9652 (Hammerfall)' CONFIG_SND_RME9652 $CONFIG_SND +dep_tristate 'RME Hammerfall DSP Audio' CONFIG_SND_HDSP $CONFIG_SND +dep_tristate 'Trident 4D-Wave DX/NX; SiS 7018' CONFIG_SND_TRIDENT $CONFIG_SND +dep_tristate 'Yamaha YMF724/740/744/754' CONFIG_SND_YMFPCI $CONFIG_SND +dep_tristate 'Avance Logic ALS4000' CONFIG_SND_ALS4000 $CONFIG_SND +dep_tristate 'C-Media 8738, 8338' CONFIG_SND_CMIPCI $CONFIG_SND +dep_tristate '(Creative) Ensoniq AudioPCI 1370' CONFIG_SND_ENS1370 $CONFIG_SND +dep_tristate '(Creative) Ensoniq AudioPCI 1371/1373' CONFIG_SND_ENS1371 $CONFIG_SND +dep_tristate 'ESS ES1938/1946 (Solo-1)' CONFIG_SND_ES1938 $CONFIG_SND +dep_tristate 'ESS ES1968/1978 (Maestro-1/2/2E)' CONFIG_SND_ES1968 $CONFIG_SND +dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND +dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND +dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND +dep_tristate 'ICE/VT1724 (Envy24HT)' CONFIG_SND_ICE1724 $CONFIG_SND +dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND +dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND +dep_tristate 'VIA 82C686A/B, 8233 South Bridge' CONFIG_SND_VIA82XX $CONFIG_SND + +# define gameport if necessary +if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then + if [ "$CONFIG_SND_CS4281" = "y" \ + -o "$CONFIG_SND_ES1938" = "y" \ + -o "$CONFIG_SND_CS46XX" = "y" \ + -o "$CONFIG_SND_SONICVIBES" = "y" \ + -o "$CONFIG_SND_TRIDENT" = "y" ]; then + define_tristate CONFIG_INPUT_GAMEPORT y + fi +fi + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/pci/Makefile linux/sound/pci/Makefile --- linux-2.4.21-rc1.orig/sound/pci/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,89 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := pci.o + +subdir-y := ac97 ali5451 cs46xx emu10k1 korg1212 nm256 rme9652 trident ymfpci ice1712 +subdir-m := $(subdir-y) + +list-multi := snd-als4000.o snd-cmipci.o snd-cs4281.o snd-ens1370.o \ + snd-ens1371.o snd-es1938.o snd-es1968.o snd-fm801.o \ + snd-intel8x0.o snd-maestro3.o snd-rme32.o \ + snd-rme96.o snd-sonicvibes.o snd-via82xx.o + +snd-als4000-objs := als4000.o +snd-cmipci-objs := cmipci.o +snd-cs4281-objs := cs4281.o +snd-ens1370-objs := ens1370.o +snd-ens1371-objs := ens1371.o +snd-es1938-objs := es1938.o +snd-es1968-objs := es1968.o +snd-fm801-objs := fm801.o +snd-intel8x0-objs := intel8x0.o +snd-maestro3-objs := maestro3.o +snd-rme32-objs := rme32.o +snd-rme96-objs := rme96.o +snd-sonicvibes-objs := sonicvibes.o +snd-via82xx-objs := via82xx.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS4000) += snd-als4000.o +obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o +obj-$(CONFIG_SND_CS4281) += snd-cs4281.o +obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o +obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o +obj-$(CONFIG_SND_ES1938) += snd-es1938.o +obj-$(CONFIG_SND_ES1968) += snd-es1968.o +obj-$(CONFIG_SND_FM801) += snd-fm801.o +obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o +obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o +obj-$(CONFIG_SND_RME32) += snd-rme32.o +obj-$(CONFIG_SND_RME96) += snd-rme96.o +obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o +obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o + +include $(TOPDIR)/Rules.make + +snd-als4000.o: $(snd-als4000-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-als4000-objs) + +snd-cmipci.o: $(snd-cmipci-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cmipci-objs) + +snd-cs4281.o: $(snd-cs4281-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4281-objs) + +snd-ens1370.o: $(snd-ens1370-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1370-objs) + +snd-ens1371.o: $(snd-ens1371-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1371-objs) + +snd-es1938.o: $(snd-es1938-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1938-objs) + +snd-es1968.o: $(snd-es1968-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1968-objs) + +snd-fm801.o: $(snd-fm801-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-fm801-objs) + +snd-intel8x0.o: $(snd-intel8x0-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-intel8x0-objs) + +snd-maestro3.o: $(snd-maestro3-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-maestro3-objs) + +snd-rme32.o: $(snd-rme32-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme32-objs) + +snd-rme96.o: $(snd-rme96-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme96-objs) + +snd-sonicvibes.o: $(snd-sonicvibes-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sonicvibes-objs) + +snd-via82xx.o: $(snd-via82xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-via82xx-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/ac97/Makefile linux/sound/pci/ac97/Makefile --- linux-2.4.21-rc1.orig/sound/pci/ac97/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ac97/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,40 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ac97.o + +list-multi := snd-ac97-codec.o snd-ak4531-codec.o + +export-objs := ac97_codec.o ak4531_codec.o + +snd-ac97-codec-objs := ac97_codec.o ac97_patch.o +snd-ak4531-codec-objs := ak4531_codec.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o +obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o +obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o +obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o +obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o +obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o +obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o +obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o +obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o +obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o +obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o +obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o +obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o +obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o +obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o + +obj-m := $(sort $(obj-m)) + +include $(TOPDIR)/Rules.make + +snd-ac97-codec.o: $(snd-ac97-codec-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ac97-codec-objs) + +snd-ak4531-codec.o: $(snd-ak4531-codec-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ak4531-codec-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_codec.c linux/sound/pci/ac97/ac97_codec.c --- linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_codec.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ac97/ac97_codec.c 2003-02-28 12:22:18.000000000 -0700 @@ -0,0 +1,2812 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.2 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ac97_id.h" +#include "ac97_patch.h" + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Universal interface for Audio Codec '97"); +MODULE_LICENSE("GPL"); + +static int enable_loopback; + +MODULE_PARM(enable_loopback, "i"); +MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); +MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); + +#define chip_t ac97_t + +/* + + */ + +static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97, const char *prefix); + +typedef struct { + unsigned int id; + unsigned int mask; + const char *name; + int (*patch)(ac97_t *ac97); + int (*mpatch)(ac97_t *ac97); +} ac97_codec_id_t; + +static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = { +{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL }, +{ 0x41445300, 0xffffff00, "Analog Devices", NULL, NULL }, +{ 0x414c4300, 0xffffff00, "Realtek", NULL, NULL }, +{ 0x414c4700, 0xffffff00, "Avance Logic", NULL, NULL }, +{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL }, +{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL, NULL }, +{ 0x43585400, 0xffffff00, "Conexant", NULL, NULL }, +{ 0x44543000, 0xffffff00, "Diamond Technology", NULL, NULL }, +{ 0x454d4300, 0xffffff00, "eMicro", NULL, NULL }, +{ 0x45838300, 0xffffff00, "ESS Technology", NULL, NULL }, +{ 0x48525300, 0xffffff00, "Intersil", NULL, NULL }, +{ 0x49434500, 0xffffff00, "ICEnsemble", NULL, NULL }, +{ 0x49544500, 0xffffff00, "ITE Tech.Inc", NULL, NULL }, +{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL }, +{ 0x50534300, 0xffffff00, "Philips", NULL, NULL }, +{ 0x53494c00, 0xffffff00, "Silicon Laboratory", NULL, NULL }, +{ 0x54524100, 0xffffff00, "TriTech", NULL, NULL }, +{ 0x54584e00, 0xffffff00, "Texas Instruments", NULL, NULL }, +{ 0x56494100, 0xffffff00, "VIA Technologies", NULL, NULL }, +{ 0x57454300, 0xffffff00, "Winbond", NULL, NULL }, +{ 0x574d4c00, 0xffffff00, "Wolfson", NULL, NULL }, +{ 0x594d4800, 0xffffff00, "Yamaha", NULL, NULL }, +{ 0x83847600, 0xffffff00, "SigmaTel", NULL, NULL }, +{ 0, 0, NULL, NULL, NULL } +}; + +static const ac97_codec_id_t snd_ac97_codec_ids[] = { +{ 0x014b0502, 0xffffffff, "NM256AV", NULL, NULL }, // FIXME: which real one? +{ 0x414b4d00, 0xffffffff, "AK4540", NULL, NULL }, +{ 0x414b4d01, 0xffffffff, "AK4542", NULL, NULL }, +{ 0x414b4d02, 0xffffffff, "AK4543", NULL, NULL }, +{ 0x414b4d06, 0xffffffff, "AK4544A", NULL, NULL }, +{ 0x414b4d07, 0xffffffff, "AK4545", NULL, NULL }, +{ 0x41445303, 0xffffffff, "AD1819", patch_ad1819, NULL }, +{ 0x41445340, 0xffffffff, "AD1881", patch_ad1881, NULL }, +{ 0x41445348, 0xffffffff, "AD1881A", patch_ad1881, NULL }, +{ 0x41445360, 0xffffffff, "AD1885", patch_ad1885, NULL }, +{ 0x41445361, 0xffffffff, "AD1886", patch_ad1886, NULL }, +{ 0x41445362, 0xffffffff, "AD1887", patch_ad1881, NULL }, +{ 0x41445363, 0xffffffff, "AD1886A", patch_ad1881, NULL }, +{ 0x41445370, 0xffffffff, "AD1980", patch_ad1980, NULL }, +{ 0x41445372, 0xffffffff, "AD1981A", patch_ad1881, NULL }, +{ 0x414c4300, 0xfffffff0, "RL5306", NULL, NULL }, +{ 0x414c4310, 0xfffffff0, "RL5382", NULL, NULL }, +{ 0x414c4320, 0xfffffff0, "RL5383", NULL, NULL }, +{ 0x414c4710, 0xfffffff0, "ALC200/200P", NULL, NULL }, +{ 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL }, +{ 0x414c4730, 0xffffffff, "ALC101", NULL, NULL }, +{ 0x414c4740, 0xfffffff0, "ALC202", NULL, NULL }, +{ 0x414c4750, 0xfffffff0, "ALC250", NULL, NULL }, +{ 0x434d4941, 0xffffffff, "CMI9738", NULL, NULL }, +{ 0x434d4961, 0xffffffff, "CMI9739", NULL, NULL }, +{ 0x43525900, 0xfffffff8, "CS4297", NULL, NULL }, +{ 0x43525910, 0xfffffff8, "CS4297A", patch_cirrus_spdif, NULL }, +{ 0x43525920, 0xfffffff8, "CS4294/4298", NULL, NULL }, +{ 0x43525928, 0xfffffff8, "CS4294", NULL, NULL }, +{ 0x43525930, 0xfffffff8, "CS4299", patch_cirrus_cs4299, NULL }, +{ 0x43525948, 0xfffffff8, "CS4201", NULL, NULL }, +{ 0x43525958, 0xfffffff8, "CS4205", patch_cirrus_spdif, NULL }, +{ 0x43525960, 0xfffffff8, "CS4291", NULL, NULL }, +{ 0x43585421, 0xffffffff, "HSD11246", NULL, NULL }, // SmartMC II +{ 0x43585428, 0xfffffff8, "Cx20468", patch_conexant, NULL }, // SmartAMC fixme: the mask might be different +{ 0x44543031, 0xfffffff0, "DT0398", NULL, NULL }, +{ 0x454d4328, 0xffffffff, "28028", NULL, NULL }, // same as TR28028? +{ 0x45838308, 0xffffffff, "ESS1988", NULL, NULL }, +{ 0x48525300, 0xffffff00, "HMP9701", NULL, NULL }, +{ 0x49434501, 0xffffffff, "ICE1230", NULL, NULL }, +{ 0x49434511, 0xffffffff, "ICE1232", NULL, NULL }, // alias VIA VT1611A? +{ 0x49434551, 0xffffffff, "VT1616", NULL, NULL }, +{ 0x49544520, 0xffffffff, "IT2226E", NULL, NULL }, +{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48", NULL, NULL }, // only guess --jk +{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL }, +{ 0x50534304, 0xffffffff, "UCB1400", NULL, NULL }, +{ 0x53494c20, 0xffffffe0, "Si3036/8", NULL, NULL }, +{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL }, +{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL }, +{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028, NULL }, // added by xin jin [07/09/99] +{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] +{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL }, +{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF +{ 0x57454301, 0xffffffff, "W83971D", NULL, NULL }, +{ 0x574d4c00, 0xffffffff, "WM9701A", patch_wolfson00,NULL }, +{ 0x574d4c03, 0xffffffff, "WM9703/9707", patch_wolfson03,NULL }, +{ 0x574d4c04, 0xffffffff, "WM9704/quad", patch_wolfson04,NULL }, +{ 0x574d4c05, 0xffffffff, "WM9705", NULL, NULL }, // patch? +{ 0x594d4800, 0xffffffff, "YMF743", NULL, NULL }, +{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL }, +{ 0x594d4803, 0xffffffff, "YMF753", patch_yamaha_ymf753, NULL }, +{ 0x83847600, 0xffffffff, "STAC9700/83/84", NULL, NULL }, +{ 0x83847604, 0xffffffff, "STAC9701/3/4/5", NULL, NULL }, +{ 0x83847605, 0xffffffff, "STAC9704", NULL, NULL }, +{ 0x83847608, 0xffffffff, "STAC9708/11", patch_sigmatel_stac9708, NULL }, +{ 0x83847609, 0xffffffff, "STAC9721/23", patch_sigmatel_stac9721, NULL }, +{ 0x83847644, 0xffffffff, "STAC9744", patch_sigmatel_stac9744, NULL }, +{ 0x83847650, 0xffffffff, "STAC9750/51", NULL, NULL }, // patch? +{ 0x83847656, 0xffffffff, "STAC9756/57", patch_sigmatel_stac9756, NULL }, +{ 0x83847666, 0xffffffff, "STAC9766/67", NULL, NULL }, // patch? +{ 0, 0, NULL, NULL, NULL } +}; + +static const char *snd_ac97_stereo_enhancements[] = +{ + /* 0 */ "No 3D Stereo Enhancement", + /* 1 */ "Analog Devices Phat Stereo", + /* 2 */ "Creative Stereo Enhancement", + /* 3 */ "National Semi 3D Stereo Enhancement", + /* 4 */ "YAMAHA Ymersion", + /* 5 */ "BBE 3D Stereo Enhancement", + /* 6 */ "Crystal Semi 3D Stereo Enhancement", + /* 7 */ "Qsound QXpander", + /* 8 */ "Spatializer 3D Stereo Enhancement", + /* 9 */ "SRS 3D Stereo Enhancement", + /* 10 */ "Platform Tech 3D Stereo Enhancement", + /* 11 */ "AKM 3D Audio", + /* 12 */ "Aureal Stereo Enhancement", + /* 13 */ "Aztech 3D Enhancement", + /* 14 */ "Binaura 3D Audio Enhancement", + /* 15 */ "ESS Technology Stereo Enhancement", + /* 16 */ "Harman International VMAx", + /* 17 */ "Nvidea 3D Stereo Enhancement", + /* 18 */ "Philips Incredible Sound", + /* 19 */ "Texas Instruments 3D Stereo Enhancement", + /* 20 */ "VLSI Technology 3D Stereo Enhancement", + /* 21 */ "TriTech 3D Stereo Enhancement", + /* 22 */ "Realtek 3D Stereo Enhancement", + /* 23 */ "Samsung 3D Stereo Enhancement", + /* 24 */ "Wolfson Microelectronics 3D Enhancement", + /* 25 */ "Delta Integration 3D Enhancement", + /* 26 */ "SigmaTel 3D Enhancement", + /* 27 */ "Reserved 27", + /* 28 */ "Rockwell 3D Stereo Enhancement", + /* 29 */ "Reserved 29", + /* 30 */ "Reserved 30", + /* 31 */ "Reserved 31" +}; + +/* + * I/O routines + */ + +static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg) +{ + if (ac97->limited_regs && ! test_bit(reg, ac97->reg_accessed)) + return 0; + + /* filter some registers for buggy codecs */ + switch (ac97->id) { + case AC97_ID_AK4540: + case AC97_ID_AK4542: + if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c) + return 1; + return 0; + case AC97_ID_AD1819: /* AD1819 */ + case AC97_ID_AD1881: /* AD1881 */ + case AC97_ID_AD1881A: /* AD1881A */ + if (reg >= 0x3a && reg <= 0x6e) /* 0x59 */ + return 0; + return 1; + case AC97_ID_AD1885: /* AD1885 */ + case AC97_ID_AD1886: /* AD1886 */ + case AC97_ID_AD1886A: /* AD1886A - !!verify!! --jk */ + case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */ + if (reg == 0x5a) + return 1; + if (reg >= 0x3c && reg <= 0x6e) /* 0x59 */ + return 0; + return 1; + case AC97_ID_STAC9700: + case AC97_ID_STAC9704: + case AC97_ID_STAC9705: + case AC97_ID_STAC9708: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + if (reg <= 0x3a || reg >= 0x5a) + return 1; + return 0; + } + return 1; +} + +/** + * snd_ac97_write - write a value on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Writes a value on the given register. This will invoke the write + * callback directly after the register check. + * This function doesn't change the register cache unlike + * #snd_ca97_write_cache(), so use this only when you don't want to + * reflect the change to the suspend/resume state. + */ +void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return; + ac97->write(ac97, reg, value); +} + +/** + * snd_ac97_read - read a value from the given register + * + * @ac97: the ac97 instance + * @reg: the register to read + * + * Reads a value from the given register. This will invoke the read + * callback directly after the register check. + * + * Returns the read value. + */ +unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return 0; + return ac97->read(ac97, reg); +} + +/** + * snd_ac97_write_cache - write a value on the given register and update the cache + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Writes a value on the given register and updates the register + * cache. The cached values are used for the cached-read and the + * suspend/resume. + */ +void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return; + spin_lock(&ac97->reg_lock); + ac97->regs[reg] = value; + spin_unlock(&ac97->reg_lock); + ac97->write(ac97, reg, value); + set_bit(reg, ac97->reg_accessed); +} + +static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned short value) +{ +#if 0 + if (!snd_ac97_valid_reg(ac97, reg)) + return; + //spin_lock(&ac97->reg_lock); + ac97->write(ac97, reg, value); + ac97->regs[reg] = ac97->read(ac97, reg); + if (value != ac97->regs[reg]) + snd_printk("AC97 reg=%02x val=%04x real=%04x\n", reg, value, ac97->regs[reg]); + //spin_unlock(&ac97->reg_lock); +#endif + snd_ac97_write_cache(ac97, reg, value); +} + +/** + * snd_ac97_update - update the value on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Compares the value with the register cache and updates the value + * only when the value is changed. + * + * Returns 1 if the value is changed, 0 if no change, or a negative + * code on failure. + */ +int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + int change; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; + spin_lock(&ac97->reg_lock); + change = ac97->regs[reg] != value; + if (change) { + ac97->regs[reg] = value; + spin_unlock(&ac97->reg_lock); + ac97->write(ac97, reg, value); + } else + spin_unlock(&ac97->reg_lock); + return change; +} + +/** + * snd_ac97_update_bits - update the bits on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @mask: the bit-mask to change + * @value: the value to set + * + * Updates the masked-bits on the given register only when the value + * is changed. + * + * Returns 1 if the bits are changed, 0 if no change, or a negative + * code on failure. + */ +int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; + spin_lock(&ac97->reg_lock); + old = ac97->regs[reg]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + ac97->regs[reg] = new; + spin_unlock(&ac97->reg_lock); + ac97->write(ac97, reg, new); + } else + spin_unlock(&ac97->reg_lock); + return change; +} + +static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + down(&ac97->spec.ad18xx.mutex); + spin_lock(&ac97->reg_lock); + old = ac97->spec.ad18xx.pcmreg[codec]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + ac97->spec.ad18xx.pcmreg[codec] = new; + spin_unlock(&ac97->reg_lock); + /* select single codec */ + ac97->write(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); + /* update PCM bits */ + ac97->write(ac97, AC97_PCM, new); + /* select all codecs */ + ac97->write(ac97, AC97_AD_SERIAL_CFG, 0x7000); + } else + spin_unlock(&ac97->reg_lock); + up(&ac97->spec.ad18xx.mutex); + return change; +} + +/* + * + */ + +static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "CD", "Video", "Aux", "Line", + "Mix", "Mix Mono", "Phone" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_REC_SEL]; + ucontrol->value.enumerated.item[0] = (val >> 8) & 7; + ucontrol->value.enumerated.item[1] = (val >> 0) & 7; + return 0; +} + +static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 7 || + ucontrol->value.enumerated.item[1] > 7) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] << 8) | + (ucontrol->value.enumerated.item[1] << 0); + return snd_ac97_update(ac97, AC97_REC_SEL, val); +} + +#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \ + .get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \ + .private_value = reg | (shift << 8) | (invert << 24) } + +static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts1[2] = { "pre 3D", "post 3D" }; + static char *texts2[2] = { "Mix", "Mic" }; + static char *texts3[2] = { "Mic1", "Mic2" }; + char **texts = NULL; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + + switch (reg) { + case AC97_GENERAL_PURPOSE: + switch (shift) { + case 15: texts = texts1; break; + case 9: texts = texts2; break; + case 8: texts = texts3; break; + } + } + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + val = (ac97->regs[reg] >> shift) & 1; + if (invert) + val ^= 1; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + val = !!ucontrol->value.enumerated.item[0]; + if (invert) + val = !val; + return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift); +} + +#define AC97_SINGLE(xname, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_single, \ + .get = snd_ac97_get_single, .put = snd_ac97_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_ac97_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ac97_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + return snd_ac97_update_bits(ac97, reg, mask << shift, val << shift); +} + +#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_double, \ + .get = snd_ac97_get_double, .put = snd_ac97_put_double, \ + .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_ac97_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock(&ac97->reg_lock); + ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (ac97->regs[reg] >> shift_right) & mask; + spin_unlock(&ac97->reg_lock); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ac97_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + return snd_ac97_update_bits(ac97, reg, + (mask << shift_left) | (mask << shift_right), + (val1 << shift_left) | (val2 << shift_right)); +} + +static const snd_kcontrol_new_t snd_ac97_controls_master[2] = { +AC97_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), +AC97_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_headphone[2] = { +AC97_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), +AC97_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = { +AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_tone[2] = { +AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1), +AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_pc_beep[2] = { +AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_phone[2] = { +AC97_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1), +AC97_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_mic[3] = { +AC97_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1), +AC97_SINGLE("Mic Playback Volume", AC97_MIC, 0, 15, 1), +AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_line[2] = { +AC97_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1), +AC97_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_cd[2] = { +AC97_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1), +AC97_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_video[2] = { +AC97_SINGLE("Video Playback Switch", AC97_VIDEO, 15, 1, 1), +AC97_DOUBLE("Video Playback Volume", AC97_VIDEO, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_aux[2] = { +AC97_SINGLE("Aux Playback Switch", AC97_AUX, 15, 1, 1), +AC97_DOUBLE("Aux Playback Volume", AC97_AUX, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_pcm[2] = { +AC97_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), +AC97_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_capture[3] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_ac97_info_mux, + .get = snd_ac97_get_mux, + .put = snd_ac97_put_mux, +}, +AC97_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1), +AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_mic_capture[2] = { +AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1), +AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0) +}; + +typedef enum { + AC97_GENERAL_PCM_OUT = 0, + AC97_GENERAL_STEREO_ENHANCEMENT, + AC97_GENERAL_3D, + AC97_GENERAL_LOUDNESS, + AC97_GENERAL_MONO, + AC97_GENERAL_MIC, + AC97_GENERAL_LOOPBACK +} ac97_general_index_t; + +static const snd_kcontrol_new_t snd_ac97_controls_general[7] = { +AC97_ENUM_DOUBLE("PCM Out Path & Mute", AC97_GENERAL_PURPOSE, 15, 0), +AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0), +AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), +AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), +AC97_ENUM_DOUBLE("Mono Output Select", AC97_GENERAL_PURPOSE, 9, 0), +AC97_ENUM_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0), +AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_3d[2] = { +AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0), +AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_center[2] = { +AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1), +AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_lfe[2] = { +AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1), +AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = { +AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), +AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), +}; + +static const snd_kcontrol_new_t snd_ac97_sigmatel_surround[2] = { +AC97_SINGLE("Sigmatel Surround Playback Switch", AC97_HEADPHONE, 15, 1, 1), +AC97_DOUBLE("Sigmatel Surround Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_sigmatel_controls[] = { +AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0), +AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_control_eapd = +AC97_SINGLE("External Amplifier Power Down", AC97_POWERDOWN, 15, 1, 0); + +static const snd_kcontrol_new_t snd_ac97_controls_vt1616[] = { +AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0), +AC97_SINGLE("Alternate Level to Surround Out", 0x5a, 15, 1, 0), +AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0), +AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0), +}; + +static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ac97_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_EMPHASIS_5015 | + IEC958_AES0_CON_NOT_COPYRIGHT; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | + IEC958_AES1_CON_ORIGINAL; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + return 0; +} + +static int snd_ac97_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + /* FIXME: AC'97 spec doesn't say which bits are used for what */ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS_5015; + return 0; +} + +static int snd_ac97_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + spin_lock(&ac97->reg_lock); + ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff; + ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff; + spin_unlock(&ac97->reg_lock); + return 0; +} + +static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned int new = 0; + unsigned short val = 0; + int change; + + spin_lock(&ac97->reg_lock); + new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); + if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) { + new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015); + switch (new & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break; + case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break; + case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break; + default: val |= 1<<12; break; + } + if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) + val |= 1<<3; + } else { + new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT); + new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8); + new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24); + if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) + val |= 1<<3; + if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT)) + val |= 1<<2; + val |= ((new >> 8) & 0xff) << 4; // category + original + switch ((new >> 24) & 0xff) { + case IEC958_AES3_CON_FS_44100: val |= 0<<12; break; + case IEC958_AES3_CON_FS_48000: val |= 2<<12; break; + case IEC958_AES3_CON_FS_32000: val |= 3<<12; break; + default: val |= 1<<12; break; + } + } + + change = ac97->spdif_status != new; + ac97->spdif_status = new; + spin_unlock(&ac97->reg_lock); + + if (ac97->flags & AC97_CS_SPDIF) { + int x = (val >> 12) & 0x03; + switch (x) { + case 0: x = 1; break; // 44.1 + case 2: x = 0; break; // 48.0 + default: x = 0; break; // illegal. + } + change |= snd_ac97_update_bits(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12))); + } else if (ac97->flags & AC97_CX_SPDIF) { + int v; + v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT; + v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM; + change |= snd_ac97_update_bits(ac97, AC97_CXR_AUDIO_MISC, + AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT, + v); + } else { + change |= snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val); + } + + return change; +} + +static int snd_ac97_put_spsa(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + // int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short value, old, new; + + value = (ucontrol->value.integer.value[0] & mask); + + mask <<= shift; + value <<= shift; + spin_lock(&ac97->reg_lock); + old = ac97->regs[reg]; + new = (old & ~mask) | value; + spin_unlock(&ac97->reg_lock); + + if (old != new) { + int change; + unsigned short extst = ac97->regs[AC97_EXTENDED_STATUS]; + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */ + change = snd_ac97_update_bits(ac97, reg, mask, value); + if (extst & AC97_EA_SPDIF) + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */ + return change; + } + return 0; +} + +static const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_ac97_spdif_mask_info, + .get = snd_ac97_spdif_cmask_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_ac97_spdif_mask_info, + .get = snd_ac97_spdif_pmask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_ac97_spdif_mask_info, + .get = snd_ac97_spdif_default_get, + .put = snd_ac97_spdif_default_put, + }, + + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0), + // AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",AC97_EXTENDED_STATUS, 4, 3, 0) + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", + .info = snd_ac97_info_single, + .get = snd_ac97_get_single, + .put = snd_ac97_put_spsa, + .private_value = AC97_EXTENDED_STATUS | (4 << 8) | (3 << 16) | (0 << 24), + }, +}; + +static const snd_kcontrol_new_t snd_ac97_cirrus_controls_spdif[2] = { + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0), + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_conexant_controls_spdif[2] = { + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0), +}; + +#define AD18XX_PCM_BITS(xname, codec, shift, mask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_bits, \ + .get = snd_ac97_ad18xx_pcm_get_bits, .put = snd_ac97_ad18xx_pcm_put_bits, \ + .private_value = codec | (shift << 8) | (mask << 16) } + +static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_ad18xx_pcm_get_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + + ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> shift) & mask); + return 0; +} + +static int snd_ac97_ad18xx_pcm_put_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + unsigned short val; + + val = mask - (ucontrol->value.integer.value[0] & mask); + return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, mask << shift, val << shift); +} + +#define AD18XX_PCM_VOLUME(xname, codec) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_volume, \ + .get = snd_ac97_ad18xx_pcm_get_volume, .put = snd_ac97_ad18xx_pcm_put_volume, \ + .private_value = codec } + +static int snd_ac97_ad18xx_pcm_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_ac97_ad18xx_pcm_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + + spin_lock(&ac97->reg_lock); + ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31); + ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31); + spin_unlock(&ac97->reg_lock); + return 0; +} + +static int snd_ac97_ad18xx_pcm_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + unsigned short val1, val2; + + val1 = 31 - (ucontrol->value.integer.value[0] & 31); + val2 = 31 - (ucontrol->value.integer.value[1] & 31); + return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2); +} + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_pcm[2] = { +AD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 1), +AD18XX_PCM_VOLUME("PCM Playback Volume", 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_surround[2] = { +AD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 1), +AD18XX_PCM_VOLUME("Surround Playback Volume", 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_center[2] = { +AD18XX_PCM_BITS("Center Playback Switch", 2, 15, 1), +AD18XX_PCM_BITS("Center Playback Volume", 2, 8, 31) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_lfe[1] = { +AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 31) +}; + +static int snd_ac97_ad1980_spdif_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[2] = { "AC-Link", "A/D Converter" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_ad1980_spdif_source_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_AD_SERIAL_CFG]; + ucontrol->value.enumerated.item[0] = (val >> 2) & 1; + return 0; +} + +static int snd_ac97_ad1980_spdif_source_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0] << 2; + return snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x0004, val); +} + +static const snd_kcontrol_new_t snd_ac97_ad1980_spdif_source = + { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + info: snd_ac97_ad1980_spdif_source_info, + get: snd_ac97_ad1980_spdif_source_get, + put: snd_ac97_ad1980_spdif_source_put, + }; + + +/* + * ALC650 + */ +static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { + AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), + AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0), + AC97_SINGLE("Center/LFE Down Mix", AC97_ALC650_MULTICH, 2, 1, 0), + AC97_SINGLE("Exchange Center/LFE", AC97_ALC650_MULTICH, 3, 1, 0), + /* 4: Analog Input To Surround */ + /* 5: Analog Input To Center/LFE */ + /* 6: Independent Master Volume Right */ + /* 7: Independent Master Volume Left */ + /* 8: reserved */ + AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), + AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0), + AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0), +#if 0 /* always set in patch_alc650 */ + AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0), + AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0), + AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1), + AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1), + AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), + AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), +#endif +}; + +static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = { + AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0), + AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0), + AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), +}; + +/* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ + +/* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */ +static int snd_ac97_ymf753_info_speaker(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Standard", "Small", "Smaller" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_ymf753_get_speaker(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_YMF753_3D_MODE_SEL]; + val = (val >> 10) & 3; + if (val > 0) /* 0 = invalid */ + val--; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_ac97_ymf753_put_speaker(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] + 1) << 10; + return snd_ac97_update(ac97, AC97_YMF753_3D_MODE_SEL, val); +} + +static const snd_kcontrol_new_t snd_ac97_ymf753_controls_speaker = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "3D Control - Speaker", + .info = snd_ac97_ymf753_info_speaker, + .get = snd_ac97_ymf753_get_speaker, + .put = snd_ac97_ymf753_put_speaker, +}; + +/* It is possible to indicate to the Yamaha YMF753 the source to direct to the S/PDIF output. */ +static int snd_ac97_ymf753_spdif_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[2] = { "AC-Link", "A/D Converter" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_ymf753_spdif_source_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_YMF753_DIT_CTRL2]; + ucontrol->value.enumerated.item[0] = (val >> 1) & 1; + return 0; +} + +static int snd_ac97_ymf753_spdif_source_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0] << 1; + return snd_ac97_update_bits(ac97, AC97_YMF753_DIT_CTRL2, 0x0002, val); +} + +/* The AC'97 spec states that the S/PDIF signal is to be output at pin 48. + The YMF753 will output the S/PDIF signal to pin 43, 47 (EAPD), or 48. + By default, no output pin is selected, and the S/PDIF signal is not output. + There is also a bit to mute S/PDIF output in a vendor-specific register. */ +static int snd_ac97_ymf753_spdif_output_pin_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_ymf753_spdif_output_pin_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_YMF753_DIT_CTRL2]; + ucontrol->value.enumerated.item[0] = (val & 0x0008) ? 2 : (val & 0x0020) ? 1 : 0; + return 0; +} + +static int snd_ac97_ymf753_spdif_output_pin_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] == 2) ? 0x0008 : + (ucontrol->value.enumerated.item[0] == 1) ? 0x0020 : 0; + return snd_ac97_update_bits(ac97, AC97_YMF753_DIT_CTRL2, 0x0028, val); + /* The following can be used to direct S/PDIF output to pin 47 (EAPD). + snd_ac97_write_cache(ac97, 0x62, snd_ac97_read(ac97, 0x62) | 0x0008); */ +} + +static const snd_kcontrol_new_t snd_ac97_ymf753_controls_spdif[3] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + .info = snd_ac97_ymf753_spdif_source_info, + .get = snd_ac97_ymf753_spdif_source_get, + .put = snd_ac97_ymf753_spdif_source_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Output Pin", + .info = snd_ac97_ymf753_spdif_output_pin_info, + .get = snd_ac97_ymf753_spdif_output_pin_get, + .put = snd_ac97_ymf753_spdif_output_pin_put, + }, + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",NONE,NONE) "Mute", AC97_YMF753_DIT_CTRL2, 2, 1, 1) +}; + +/* + * + */ + +static int snd_ac97_free(ac97_t *ac97) +{ + if (ac97) { + if (ac97->private_free) + ac97->private_free(ac97); + snd_magic_kfree(ac97); + } + return 0; +} + +static int snd_ac97_dev_free(snd_device_t *device) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, device->device_data, return -ENXIO); + return snd_ac97_free(ac97); +} + +static int snd_ac97_try_volume_mix(ac97_t * ac97, int reg) +{ + unsigned short val, mask = 0x8000; + + if (! snd_ac97_valid_reg(ac97, reg)) + return 0; + + switch (reg) { + case AC97_MASTER_TONE: + return ac97->caps & 0x04 ? 1 : 0; + case AC97_HEADPHONE: + return ac97->caps & 0x10 ? 1 : 0; + case AC97_REC_GAIN_MIC: + return ac97->caps & 0x01 ? 1 : 0; + case AC97_3D_CONTROL: + if (ac97->caps & 0x7c00) { + val = snd_ac97_read(ac97, reg); + /* if nonzero - fixed and we can't set it */ + return val == 0; + } + return 0; + case AC97_CENTER_LFE_MASTER: /* center */ + if ((ac97->ext_id & 0x40) == 0) + return 0; + break; + case AC97_CENTER_LFE_MASTER+1: /* lfe */ + if ((ac97->ext_id & 0x100) == 0) + return 0; + reg = AC97_CENTER_LFE_MASTER; + mask = 0x0080; + break; + case AC97_SURROUND_MASTER: + if ((ac97->ext_id & 0x80) == 0) + return 0; + break; + } + + if (ac97->limited_regs && test_bit(reg, ac97->reg_accessed)) + return 1; /* allow without check */ + + val = snd_ac97_read(ac97, reg); + if (!(val & mask)) { + /* nothing seems to be here - mute flag is not set */ + /* try another test */ + snd_ac97_write_cache_test(ac97, reg, val | mask); + val = snd_ac97_read(ac97, reg); + if (!(val & mask)) + return 0; /* nothing here */ + } + return 1; /* success, useable */ +} + +static int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit) +{ + unsigned short mask, val, orig, res; + + mask = 1 << bit; + orig = snd_ac97_read(ac97, reg); + val = orig ^ mask; + snd_ac97_write(ac97, reg, val); + res = snd_ac97_read(ac97, reg); + snd_ac97_write_cache(ac97, reg, orig); + return res == val; +} + +static void snd_ac97_change_volume_params1(ac97_t * ac97, int reg, unsigned char *max) +{ + unsigned short val, val1; + + *max = 63; + val = 0x8000 | 0x0020; + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 31; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8000); +} + +static void snd_ac97_change_volume_params2(ac97_t * ac97, int reg, int shift, unsigned char *max) +{ + unsigned short val, val1; + + *max = 63; + val = 0x8080 | (0x20 << shift); + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 31; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8080); +} + +static void snd_ac97_change_volume_params3(ac97_t * ac97, int reg, unsigned char *max) +{ + unsigned short val, val1; + + *max = 31; + val = 0x8000 | 0x0010; + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 15; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8000); +} + +static inline int printable(unsigned int x) +{ + x &= 0xff; + if (x < ' ' || x >= 0x71) { + if (x <= 0x89) + return x - 0x71 + 'A'; + return '?'; + } + return x; +} + +static snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t * ac97) +{ + snd_kcontrol_new_t template; + memcpy(&template, _template, sizeof(template)); + snd_runtime_check(!template.index, return NULL); + template.index = ac97->num; + return snd_ctl_new1(&template, ac97); +} + +static int snd_ac97_mixer_build(snd_card_t * card, ac97_t * ac97) +{ + snd_kcontrol_t *kctl; + const snd_kcontrol_new_t *knew; + int err; + unsigned int idx; + unsigned char max; + + /* build master controls */ + /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_MASTER, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000 | max | (max << 8)); + } + + ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; + + /* build center controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max); + } + + /* build LFE controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8); + } + + /* build surround controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_surround[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_surround[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_SURROUND_MASTER, 0, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x8080 | max | (max << 8)); + } + + /* build headphone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE) || ac97->id == AC97_ID_STAC9708) { + knew = ac97->id == AC97_ID_STAC9708 ? snd_ac97_sigmatel_surround : snd_ac97_controls_headphone; + if ((err = snd_ctl_add(card, snd_ac97_cnew(knew, ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(knew + 1, ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_HEADPHONE, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_HEADPHONE, 0x8000 | max | (max << 8)); + } + + /* build master mono controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master_mono[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master_mono[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_MASTER_MONO, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_MASTER_MONO, 0x8000 | max); + } + + /* build master tone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) { + for (idx = 0; idx < 2; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0) + return err; + if (ac97->id == AC97_ID_YMF753) { + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= 7 << 16; + } + } + snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f); + } + + /* build PC Speaker controls */ + if ((ac97->flags & AC97_HAS_PC_BEEP) || + snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e); + } + + /* build Phone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_phone[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_phone[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params3(ac97, AC97_PHONE, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_PHONE, 0x8000 | max); + } + + /* build MIC controls */ + snd_ac97_change_volume_params3(ac97, AC97_MIC, &max); + for (idx = 0; idx < 3; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic[idx], ac97))) < 0) + return err; + if (idx == 1) { // volume + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + } + } + snd_ac97_write_cache(ac97, AC97_MIC, 0x8000 | max); + + /* build Line controls */ + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_line[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_LINE, 0x9f1f); + + /* build CD controls */ + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_cd[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_CD, 0x9f1f); + + /* build Video controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_video[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_VIDEO, 0x9f1f); + } + + /* build Aux controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_aux[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_AUX, 0x9f1f); + } + + /* build PCM controls */ + if (ac97->flags & AC97_AD_MULTI) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[0] = 0x9f1f; + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[1] = 0x9f1f; + } + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[0], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[2] = 0x9f1f; + } + } else { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pcm[idx], ac97))) < 0) + return err; + } + snd_ac97_write_cache(ac97, AC97_PCM, 0x9f1f); + + /* build Capture controls */ + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_capture[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); + + /* build MIC Capture controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); + } + + /* build PCM out path & mute control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0) + return err; + } + + /* build Simulated Stereo Enhancement control */ + if (ac97->caps & 0x0008) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) + return err; + } + + /* build 3D Stereo Enhancement control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0) + return err; + } + + /* build Loudness control */ + if (ac97->caps & 0x0020) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) + return err; + } + + /* build Mono output select control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0) + return err; + } + + /* build Mic select control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0) + return err; + } + + /* build ADC/DAC loopback control */ + if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0) + return err; + } + + snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x0000); + + /* build 3D controls */ + switch (ac97->id) { + case AC97_ID_STAC9708: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + kctl->private_value = AC97_3D_CONTROL | (3 << 16); + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); + kctl->private_value = AC97_3D_CONTROL | (2 << 8) | (3 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + break; + case AC97_ID_STAC9700: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + kctl->private_value = AC97_3D_CONTROL | (3 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + break; + case AC97_ID_YMF753: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control - Wide"); + kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_ymf753_controls_speaker, ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_YMF753_3D_MODE_SEL, 0x0c00); + break; + default: + if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) { + unsigned short val; + val = 0x0707; + snd_ac97_write(ac97, AC97_3D_CONTROL, val); + val = snd_ac97_read(ac97, AC97_3D_CONTROL); + val = val == 0x0606; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + if (val) + kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0) + return err; + if (val) + kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + } + } + + /* build S/PDIF controls */ + if (ac97->ext_id & AC97_EI_SPDIF) { + if (ac97->flags & AC97_CS_SPDIF) { + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_cirrus_controls_spdif[0], ac97))) < 0) + return err; + switch (ac97->id & AC97_ID_CS_MASK) { + case AC97_ID_CS4205: + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_cirrus_controls_spdif[1], ac97))) < 0) + return err; + break; + } + /* set default PCM S/PDIF params */ + /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ + snd_ac97_write_cache(ac97, AC97_CSR_SPDIF, 0x0a20); + } else if (ac97->flags & AC97_CX_SPDIF) { + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_conexant_controls_spdif[0], ac97))) < 0) + return err; + /* set default PCM S/PDIF params */ + /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ + snd_ac97_write_cache(ac97, AC97_CXR_AUDIO_MISC, + snd_ac97_read(ac97, AC97_CXR_AUDIO_MISC) & ~(AC97_CXR_SPDIFEN|AC97_CXR_COPYRGT|AC97_CXR_SPDIF_MASK)); + + } else { + for (idx = 0; idx < 5; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) + return err; + if (ac97->id == AC97_ID_YMF753) { + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_ymf753_controls_spdif[idx], ac97))) < 0) + return err; + } else if (ac97->id == AC97_ID_AD1980) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_ad1980_spdif_source, ac97))) < 0) + return err; + } + /* set default PCM S/PDIF params */ + /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ + snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); + } + + ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + } + + /* build chip specific controls */ + switch (ac97->id) { + case AC97_ID_STAC9700: + case AC97_ID_STAC9708: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + snd_ac97_write_cache_test(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003); + if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[0], ac97))) < 0) + return err; + if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[1], ac97))) < 0) + return err; + break; + case AC97_ID_ALC650: + for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_alc650); idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_alc650[idx], ac97))) < 0) + return err; + if (ac97->ext_id & AC97_EI_SPDIF) { + for (idx = 0; idx < ARRAY_SIZE(snd_ac97_spdif_controls_alc650); idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_spdif_controls_alc650[idx], ac97))) < 0) + return err; + } + break; + case AC97_ID_VT1616: + if (snd_ac97_try_bit(ac97, 0x5a, 9)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_vt1616[0], ac97))) < 0) + return err; + for (idx = 1; idx < ARRAY_SIZE(snd_ac97_controls_vt1616); idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_vt1616[idx], ac97))) < 0) + return err; + break; + default: + /* nothing */ + break; + } + + if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0) + return err; + } + + return 0; +} + +static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97) +{ + /* TODO */ + return 0; +} + +static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate) +{ + unsigned short val; + unsigned int tmp; + + tmp = ((unsigned int)rate * ac97->clock) / 48000; + snd_ac97_write_cache_test(ac97, reg, tmp & 0xffff); + val = snd_ac97_read(ac97, reg); + return val == (tmp & 0xffff); +} + +static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result) +{ + unsigned int result = 0; + + /* test a non-standard rate */ + if (snd_ac97_test_rate(ac97, reg, 11000)) + result |= SNDRV_PCM_RATE_CONTINUOUS; + /* let's try to obtain standard rates */ + if (snd_ac97_test_rate(ac97, reg, 8000)) + result |= SNDRV_PCM_RATE_8000; + if (snd_ac97_test_rate(ac97, reg, 11025)) + result |= SNDRV_PCM_RATE_11025; + if (snd_ac97_test_rate(ac97, reg, 16000)) + result |= SNDRV_PCM_RATE_16000; + if (snd_ac97_test_rate(ac97, reg, 22050)) + result |= SNDRV_PCM_RATE_22050; + if (snd_ac97_test_rate(ac97, reg, 32000)) + result |= SNDRV_PCM_RATE_32000; + if (snd_ac97_test_rate(ac97, reg, 44100)) + result |= SNDRV_PCM_RATE_44100; + if (snd_ac97_test_rate(ac97, reg, 48000)) + result |= SNDRV_PCM_RATE_48000; + *r_result = result; +} + +static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem) +{ + const ac97_codec_id_t *pid; + + sprintf(name, "0x%x %c%c%c", id, + printable(id >> 24), + printable(id >> 16), + printable(id >> 8)); + for (pid = snd_ac97_codec_id_vendors; pid->id; pid++) + if (pid->id == (id & pid->mask)) { + strcpy(name, pid->name); + if (ac97) { + if (!modem && pid->patch) + pid->patch(ac97); + else if (modem && pid->mpatch) + pid->mpatch(ac97); + } + goto __vendor_ok; + } + return; + + __vendor_ok: + for (pid = snd_ac97_codec_ids; pid->id; pid++) + if (pid->id == (id & pid->mask)) { + strcat(name, " "); + strcat(name, pid->name); + if (pid->mask != 0xffffffff) + sprintf(name + strlen(name), " rev %d", id & ~pid->mask); + if (ac97) { + if (!modem && pid->patch) + pid->patch(ac97); + else if (modem && pid->mpatch) + pid->mpatch(ac97); + } + return; + } + sprintf(name + strlen(name), " id %x", id & 0xff); +} + + +/* wait for a while until registers are accessible after RESET + * return 0 if ok, negative not ready + */ +static int ac97_reset_wait(ac97_t *ac97, int timeout, int with_modem) +{ + signed long end_time; + end_time = jiffies + timeout; + do { + unsigned short ext_mid; + + /* use preliminary reads to settle the communication */ + snd_ac97_read(ac97, AC97_RESET); + snd_ac97_read(ac97, AC97_VENDOR_ID1); + snd_ac97_read(ac97, AC97_VENDOR_ID2); + /* modem? */ + if (with_modem) { + ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (ext_mid != 0xffff && (ext_mid & 1) != 0) + return 0; + } + /* because the PCM or MASTER volume registers can be modified, + * the REC_GAIN register is used for tests + */ + /* test if we can write to the record gain volume register */ + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); + if (snd_ac97_read(ac97, AC97_REC_GAIN) == 0x8a05) + return 0; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } while (time_after_eq(end_time, jiffies)); + return -ENODEV; +} + +/** + * snd_ac97_mixer - create an AC97 codec component + * @card: the card instance + * @_ac97: the template of ac97, including index, callbacks and + * the private data. + * @rac97: the pointer to store the new ac97 instance. + * + * Creates an AC97 codec component. An ac97_t instance is newly + * allocated and initialized from the template (_ac97). The codec + * is then initialized by the standard procedure. + * + * The template must include the valid callbacks (at least read and + * write), the codec number (num) and address (addr), and the private + * data (private_data). The other callbacks, wait and reset, are not + * mandatory. + * + * The clock is set to 48000. If another clock is needed, reset + * ac97->clock manually afterwards. + * + * The ac97 instance is registered as a low-level device, so you don't + * have to release it manually. + * + * The MCs (Modem Codecs only) are only detected but valid. The PCM driver + * have to check for MCs using the !ac97_is_audio() function. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) +{ + int err; + ac97_t *ac97; + char name[64]; + signed long end_time; + static snd_device_ops_t ops = { + .dev_free = snd_ac97_dev_free, + }; + + snd_assert(rac97 != NULL, return -EINVAL); + *rac97 = NULL; + snd_assert(card != NULL && _ac97 != NULL, return -EINVAL); + ac97 = snd_magic_kcalloc(ac97_t, 0, GFP_KERNEL); + if (ac97 == NULL) + return -ENOMEM; + *ac97 = *_ac97; + ac97->card = card; + spin_lock_init(&ac97->reg_lock); + + if (ac97->reset) { + ac97->reset(ac97); + goto __access_ok; + } + + snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ + if (ac97->wait) + ac97->wait(ac97); + else { + udelay(50); + if (ac97_reset_wait(ac97, HZ/2, 0) < 0 && + ac97_reset_wait(ac97, HZ/2, 1) < 0) { + snd_printk("AC'97 %d:%d does not respond - RESET\n", ac97->num, ac97->addr); + snd_ac97_free(ac97); + return -ENXIO; + } + } + __access_ok: + ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) { + snd_printk("AC'97 %d:%d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->addr, ac97->id); + snd_ac97_free(ac97); + return -EIO; + } + + /* test for AC'97 */ + /* test if we can write to the record gain volume register */ + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06); + if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a06) { + ac97->scaps |= AC97_SCAP_AUDIO; + ac97->caps = snd_ac97_read(ac97, AC97_RESET); + ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->ext_id == 0xffff) /* invalid combination */ + ac97->ext_id = 0; + } + + /* test for MC'97 */ + ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (ac97->ext_mid == 0xffff) /* invalid combination */ + ac97->ext_mid = 0; + if (ac97->ext_mid & 1) + ac97->scaps |= AC97_SCAP_MODEM; + + if (ac97->reset) // FIXME: always skipping? + goto __ready_ok; + + /* FIXME: add powerdown control */ + if (ac97_is_audio(ac97)) { + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); + snd_ac97_write_cache_test(ac97, AC97_RESET, 0); /* reset to defaults */ + udelay(100); + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); + snd_ac97_write_cache_test(ac97, AC97_GENERAL_PURPOSE, 0); + end_time = jiffies + (HZ / 10); + do { + if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) + goto __ready_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + } while (time_after_eq(end_time, jiffies)); + snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr); + } + + __ready_ok: + if (ac97->clock == 0) + ac97->clock = 48000; /* standard value */ + if (ac97_is_audio(ac97)) + ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT; + else + ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT; + if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ + snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); + if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */ + snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]); + } else { + ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000; + ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000; + } + if (ac97->ext_id & AC97_EI_SPDIF) { + /* codec specific code (patch) should override these values */ + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_32000; + } + if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */ + snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]); + } else { + ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000; + } + if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */ + snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]); + ac97->scaps |= AC97_SCAP_SURROUND_DAC; + } + if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */ + snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]); + ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; + } + /* additional initializations */ + if (ac97->init) + ac97->init(ac97); + snd_ac97_get_name(ac97, ac97->id, name, 0); + snd_ac97_get_name(NULL, ac97->id, name, 0); // ac97->id might be changed in the special setup code + if (ac97_is_audio(ac97)) { + if (card->mixername[0] == '\0') { + strcpy(card->mixername, name); + } else { + if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { + strcat(card->mixername, ","); + strcat(card->mixername, name); + } + } + if ((err = snd_component_add(card, "AC97a")) < 0) { + snd_ac97_free(ac97); + return err; + } + } + if (ac97_is_audio(ac97) && snd_ac97_mixer_build(card, ac97) < 0) { + snd_ac97_free(ac97); + return -ENOMEM; + } + snd_ac97_proc_init(card, ac97, "ac97"); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { + snd_ac97_free(ac97); + return err; + } + *rac97 = ac97; + return 0; +} + +/* wait for a while until registers are accessible after RESET + * return 0 if ok, negative not ready + */ +static int ac97_modem_reset_wait(ac97_t *ac97, int timeout) +{ + signed long end_time; + end_time = jiffies + timeout; + do { + unsigned short ext_mid; + + /* use preliminary reads to settle the communication */ + snd_ac97_read(ac97, AC97_EXTENDED_MID); + snd_ac97_read(ac97, AC97_VENDOR_ID1); + snd_ac97_read(ac97, AC97_VENDOR_ID2); + ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (ext_mid != 0xffff && (ext_mid & 1) != 0) + return 0; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } while (time_after_eq(end_time, jiffies)); + return -ENODEV; +} + +/** + * snd_ac97_modem - create an MC97 codec component + * @card: the card instance + * @_ac97: the template of ac97, including index, callbacks and + * the private data. + * @rac97: the pointer to store the new ac97 instance. + * + * Creates an MC97 codec component. An ac97_t instance is newly + * allocated and initialized from the template (_ac97). The codec + * is then initialized by the standard procedure. + * + * The template must include the valid callbacks (at least read and + * write), the codec number (num) and address (addr), and the private + * data (private_data). The other callbacks, wait and reset, are not + * mandatory. + * + * The clock is set to 48000. If another clock is needed, reset + * ac97->clock manually afterwards. + * + * The ac97 instance is registered as a low-level device, so you don't + * have to release it manually. + * + * The ACs (Audio Codecs only) are only detected but valid. The PCM driver + * have to check for ACs using the !ac97_is_modem() function. + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_ac97_modem(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) +{ + int err; + ac97_t *ac97; + char name[64]; + signed long end_time; + unsigned short tmp; + static snd_device_ops_t ops = { + .dev_free = snd_ac97_dev_free, + }; + + snd_assert(rac97 != NULL, return -EINVAL); + *rac97 = NULL; + snd_assert(card != NULL && _ac97 != NULL, return -EINVAL); + ac97 = snd_magic_kcalloc(ac97_t, 0, GFP_KERNEL); + if (ac97 == NULL) + return -ENOMEM; + *ac97 = *_ac97; + ac97->card = card; + spin_lock_init(&ac97->reg_lock); + + if (ac97->reset) { + ac97->reset(ac97); + goto __access_ok; + } + + snd_ac97_write(ac97, AC97_EXTENDED_MID, 0); /* reset to defaults */ + if (ac97->wait) + ac97->wait(ac97); + else { + udelay(50); + if (ac97_modem_reset_wait(ac97, HZ/2) < 0) { + snd_printk("MC'97 %d:%d does not respond - MODEM RESET\n", ac97->num, ac97->addr); + snd_ac97_free(ac97); + return -ENXIO; + } + } + __access_ok: + ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) { + snd_printk("MC'97 %d:%d access is not valid [0x%x], removing modem controls.\n", ac97->num, ac97->addr, ac97->id); + snd_ac97_free(ac97); + return -EIO; + } + + /* test for MC'97 */ + ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (ac97->ext_mid == 0xffff) /* invalid combination */ + ac97->ext_mid = 0; + if (ac97->ext_mid & 1) + ac97->scaps |= AC97_SCAP_MODEM; + + /* non-destructive test for AC'97 */ + tmp = snd_ac97_read(ac97, AC97_RESET); + if (tmp == 0 || tmp == 0xffff) { + tmp = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (tmp == 0 || tmp == 0xffff) { + tmp = snd_ac97_read(ac97, AC97_REC_GAIN); + if (tmp == 0 || tmp == 0xffff) + tmp = snd_ac97_read(ac97, AC97_POWERDOWN); + } + } + if ((tmp != 0 && tmp != 0xffff) || !(ac97->scaps & AC97_SCAP_MODEM)) + ac97->scaps |= AC97_SCAP_AUDIO; + + if (ac97->reset) // FIXME: always skipping? + goto __ready_ok; + + /* FIXME: add powerdown control */ + if (ac97->scaps & AC97_SCAP_MODEM) { + /* nothing should be in powerdown mode */ + /* note: it's important to set the rate at first */ + tmp = AC97_MEA_GPIO; + if (ac97->ext_mid & AC97_MEI_LINE1) { + snd_ac97_write_cache_test(ac97, AC97_LINE1_RATE, 12000); + tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1; + } + if (ac97->ext_mid & AC97_MEI_LINE2) { + snd_ac97_write_cache_test(ac97, AC97_LINE2_RATE, 12000); + tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2; + } + if (ac97->ext_mid & AC97_MEI_HANDSET) { + snd_ac97_write_cache_test(ac97, AC97_HANDSET_RATE, 12000); + tmp |= AC97_MEA_HADC | AC97_MEA_HDAC; + } + snd_ac97_write_cache_test(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8)); + udelay(100); + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8)); + end_time = jiffies + (HZ / 10); + do { + if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) + goto __ready_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + } while (time_after_eq(end_time, jiffies)); + snd_printk("MC'97 %d:%d converters and GPIO not ready (0x%x)\n", ac97->num, ac97->addr, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS)); + } + + __ready_ok: + /* additional initializations */ + /* FIXME: ADD MODEM INITALIZATION */ + if (ac97_is_modem(ac97)) + ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT; + else + ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT; + + if (ac97->init) + ac97->init(ac97); + snd_ac97_get_name(ac97, ac97->id, name, 1); + snd_ac97_get_name(NULL, ac97->id, name, 1); // ac97->id might be changed in the special setup code + if (ac97_is_modem(ac97)) { + if (card->mixername[0] == '\0') { + strcpy(card->mixername, name); + } else { + if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { + strcat(card->mixername, ","); + strcat(card->mixername, name); + } + } + if ((err = snd_component_add(card, "AC97m")) < 0) { + snd_ac97_free(ac97); + return err; + } + } + if (ac97_is_modem(ac97) && snd_ac97_modem_build(card, ac97) < 0) { + snd_ac97_free(ac97); + return -ENOMEM; + } + snd_ac97_proc_init(card, ac97, "mc97"); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { + snd_ac97_free(ac97); + return err; + } + *rac97 = ac97; + return 0; +} + +/* + * proc interface + */ + +static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) +{ + char name[64]; + unsigned int id; + unsigned short val, tmp, ext, mext; + static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" }; + static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" }; + static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" }; + + id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + snd_ac97_get_name(NULL, id, name, 0); + snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); + if ((ac97->scaps & AC97_SCAP_AUDIO) == 0) + goto __modem; + + // val = snd_ac97_read(ac97, AC97_RESET); + val = ac97->caps; + snd_iprintf(buffer, "Capabilities :%s%s%s%s%s%s\n", + val & AC97_BC_DEDICATED_MIC ? " -dedicated MIC PCM IN channel-" : "", + val & AC97_BC_RESERVED1 ? " -reserved1-" : "", + val & AC97_BC_BASS_TREBLE ? " -bass & treble-" : "", + val & AC97_BC_SIM_STEREO ? " -simulated stereo-" : "", + val & AC97_BC_HEADPHONE ? " -headphone out-" : "", + val & AC97_BC_LOUDNESS ? " -loudness-" : ""); + tmp = ac97->caps & AC97_BC_DAC_MASK; + snd_iprintf(buffer, "DAC resolution : %s%s%s%s\n", + tmp == AC97_BC_16BIT_DAC ? "16-bit" : "", + tmp == AC97_BC_18BIT_DAC ? "18-bit" : "", + tmp == AC97_BC_20BIT_DAC ? "20-bit" : "", + tmp == AC97_BC_DAC_MASK ? "???" : ""); + tmp = ac97->caps & AC97_BC_ADC_MASK; + snd_iprintf(buffer, "ADC resolution : %s%s%s%s\n", + tmp == AC97_BC_16BIT_ADC ? "16-bit" : "", + tmp == AC97_BC_18BIT_ADC ? "18-bit" : "", + tmp == AC97_BC_20BIT_ADC ? "20-bit" : "", + tmp == AC97_BC_ADC_MASK ? "???" : ""); + snd_iprintf(buffer, "3D enhancement : %s\n", + snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]); + snd_iprintf(buffer, "\nCurrent setup\n"); + val = snd_ac97_read(ac97, AC97_MIC); + snd_iprintf(buffer, "Mic gain : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB"); + val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE); + snd_iprintf(buffer, "POP path : %s 3D\n" + "Sim. stereo : %s\n" + "3D enhancement : %s\n" + "Loudness : %s\n" + "Mono output : %s\n" + "Mic select : %s\n" + "ADC/DAC loopback : %s\n", + val & 0x8000 ? "post" : "pre", + val & 0x4000 ? "on" : "off", + val & 0x2000 ? "on" : "off", + val & 0x1000 ? "on" : "off", + val & 0x0200 ? "Mic" : "MIX", + val & 0x0100 ? "Mic2" : "Mic1", + val & 0x0080 ? "on" : "off"); + + ext = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ext == 0) + goto __modem; + + snd_iprintf(buffer, "Extended ID : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n", + (ext & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT, + (ext & AC97_EI_REV_MASK) >> AC97_EI_REV_SHIFT, + ext & AC97_EI_AMAP ? " AMAP" : "", + ext & AC97_EI_LDAC ? " LDAC" : "", + ext & AC97_EI_SDAC ? " SDAC" : "", + ext & AC97_EI_CDAC ? " CDAC" : "", + (ext & AC97_EI_DACS_SLOT_MASK) >> AC97_EI_DACS_SLOT_SHIFT, + ext & AC97_EI_VRM ? " VRM" : "", + ext & AC97_EI_SPDIF ? " SPDIF" : "", + ext & AC97_EI_DRA ? " DRA" : "", + ext & AC97_EI_VRA ? " VRA" : ""); + val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); + snd_iprintf(buffer, "Extended status :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + val & AC97_EA_PRL ? " PRL" : "", + val & AC97_EA_PRK ? " PRK" : "", + val & AC97_EA_PRJ ? " PRJ" : "", + val & AC97_EA_PRI ? " PRI" : "", + val & AC97_EA_SPCV ? " SPCV" : "", + val & AC97_EA_MDAC ? " MADC" : "", + val & AC97_EA_LDAC ? " LDAC" : "", + val & AC97_EA_SDAC ? " SDAC" : "", + val & AC97_EA_CDAC ? " CDAC" : "", + ext & AC97_EI_SPDIF ? spdif_slots[(val & AC97_EA_SPSA_SLOT_MASK) >> AC97_EA_SPSA_SLOT_SHIFT] : "", + val & AC97_EA_VRM ? " VRM" : "", + val & AC97_EA_SPDIF ? " SPDIF" : "", + val & AC97_EA_DRA ? " DRA" : "", + val & AC97_EA_VRA ? " VRA" : ""); + if (ext & AC97_EI_VRA) { /* VRA */ + val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE); + snd_iprintf(buffer, "PCM front DAC : %iHz\n", val); + if (ext & AC97_EI_SDAC) { + val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE); + snd_iprintf(buffer, "PCM Surr DAC : %iHz\n", val); + } + if (ext & AC97_EI_LDAC) { + val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE); + snd_iprintf(buffer, "PCM LFE DAC : %iHz\n", val); + } + val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE); + snd_iprintf(buffer, "PCM ADC : %iHz\n", val); + } + if (ext & AC97_EI_VRM) { + val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE); + snd_iprintf(buffer, "PCM MIC ADC : %iHz\n", val); + } + if ((ext & AC97_EI_SPDIF) || (ac97->flags & AC97_CS_SPDIF)) { + if (ac97->flags & AC97_CS_SPDIF) + val = snd_ac97_read(ac97, AC97_CSR_SPDIF); + else + val = snd_ac97_read(ac97, AC97_SPDIF); + + snd_iprintf(buffer, "SPDIF Control :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n", + val & AC97_SC_PRO ? " PRO" : " Consumer", + val & AC97_SC_NAUDIO ? " Non-audio" : " PCM", + val & AC97_SC_COPY ? " Copyright" : "", + val & AC97_SC_PRE ? " Preemph50/15" : "", + (val & AC97_SC_CC_MASK) >> AC97_SC_CC_SHIFT, + (val & AC97_SC_L) >> 11, + (ac97->flags & AC97_CS_SPDIF) ? + spdif_rates_cs4205[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT] : + spdif_rates[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT], + (ac97->flags & AC97_CS_SPDIF) ? + (val & AC97_SC_DRS ? " Validity" : "") : + (val & AC97_SC_DRS ? " DRS" : ""), + (ac97->flags & AC97_CS_SPDIF) ? + (val & AC97_SC_V ? " Enabled" : "") : + (val & AC97_SC_V ? " Validity" : "")); + } + + __modem: + mext = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (mext == 0) + return; + + snd_iprintf(buffer, "Extended modem ID: codec=%i%s%s%s%s%s\n", + (mext & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT, + mext & AC97_MEI_CID2 ? " CID2" : "", + mext & AC97_MEI_CID1 ? " CID1" : "", + mext & AC97_MEI_HANDSET ? " HSET" : "", + mext & AC97_MEI_LINE2 ? " LIN2" : "", + mext & AC97_MEI_LINE1 ? " LIN1" : ""); + val = snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS); + snd_iprintf(buffer, "Modem status :%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + val & AC97_MEA_GPIO ? " GPIO" : "", + val & AC97_MEA_MREF ? " MREF" : "", + val & AC97_MEA_ADC1 ? " ADC1" : "", + val & AC97_MEA_DAC1 ? " DAC1" : "", + val & AC97_MEA_ADC2 ? " ADC2" : "", + val & AC97_MEA_DAC2 ? " DAC2" : "", + val & AC97_MEA_HADC ? " HADC" : "", + val & AC97_MEA_HDAC ? " HDAC" : "", + val & AC97_MEA_PRA ? " PRA(GPIO)" : "", + val & AC97_MEA_PRB ? " PRB(res)" : "", + val & AC97_MEA_PRC ? " PRC(ADC1)" : "", + val & AC97_MEA_PRD ? " PRD(DAC1)" : "", + val & AC97_MEA_PRE ? " PRE(ADC2)" : "", + val & AC97_MEA_PRF ? " PRF(DAC2)" : "", + val & AC97_MEA_PRG ? " PRG(HADC)" : "", + val & AC97_MEA_PRH ? " PRH(HDAC)" : ""); + if (mext & AC97_MEI_LINE1) { + val = snd_ac97_read(ac97, AC97_LINE1_RATE); + snd_iprintf(buffer, "Line1 rate : %iHz\n", val); + } + if (mext & AC97_MEI_LINE2) { + val = snd_ac97_read(ac97, AC97_LINE2_RATE); + snd_iprintf(buffer, "Line2 rate : %iHz\n", val); + } + if (mext & AC97_MEI_HANDSET) { + val = snd_ac97_read(ac97, AC97_HANDSET_RATE); + snd_iprintf(buffer, "Headset rate : %iHz\n", val); + } +} + +static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + + if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 + int idx; + down(&ac97->spec.ad18xx.mutex); + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) { + /* select single codec */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]); + snd_ac97_proc_read_main(ac97, buffer, idx); + snd_iprintf(buffer, "\n\n"); + } + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + up(&ac97->spec.ad18xx.mutex); + + snd_iprintf(buffer, "\nAD18XX configuration\n"); + snd_iprintf(buffer, "Unchained : 0x%04x,0x%04x,0x%04x\n", + ac97->spec.ad18xx.unchained[0], + ac97->spec.ad18xx.unchained[1], + ac97->spec.ad18xx.unchained[2]); + snd_iprintf(buffer, "Chained : 0x%04x,0x%04x,0x%04x\n", + ac97->spec.ad18xx.chained[0], + ac97->spec.ad18xx.chained[1], + ac97->spec.ad18xx.chained[2]); + } else { + snd_ac97_proc_read_main(ac97, buffer, 0); + } +} + +static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) +{ + int reg, val; + + for (reg = 0; reg < 0x80; reg += 2) { + val = snd_ac97_read(ac97, reg); + snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val); + } +} + +static void snd_ac97_proc_regs_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + + if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 + + int idx; + down(&ac97->spec.ad18xx.mutex); + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) { + /* select single codec */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]); + snd_ac97_proc_regs_read_main(ac97, buffer, idx); + } + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + up(&ac97->spec.ad18xx.mutex); + } else { + snd_ac97_proc_regs_read_main(ac97, buffer, 0); + } +} + +static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97, const char *prefix) +{ + snd_info_entry_t *entry; + char name[32]; + + if (ac97->num) + sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num); + else + sprintf(name, "%s#%d", prefix, ac97->addr); + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read); + if (ac97->num) + sprintf(name, "%s#%d-%dregs", prefix, ac97->addr, ac97->num); + else + sprintf(name, "%s#%dregs", prefix, ac97->addr); + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); +} + +/* + * PCM support + */ + +static int set_spdif_rate(ac97_t *ac97, unsigned short rate) +{ + unsigned short old, bits, reg; + + if (! (ac97->ext_id & AC97_EI_SPDIF)) + return -ENODEV; + + if (ac97->flags & AC97_CS_SPDIF) { + switch (rate) { + case 48000: bits = 0; break; + case 44100: bits = 1 << AC97_SC_SPSR_SHIFT; break; + default: /* invalid - disable output */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); + return -EINVAL; + } + reg = AC97_CSR_SPDIF; + } else { + switch (rate) { + case 44100: bits = AC97_SC_SPSR_44K; break; + case 48000: bits = AC97_SC_SPSR_48K; break; + case 32000: bits = AC97_SC_SPSR_32K; break; + default: /* invalid - disable output */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); + return -EINVAL; + } + reg = AC97_SPDIF; + } + + spin_lock(&ac97->reg_lock); + old = ac97->regs[reg] & ~AC97_SC_SPSR_MASK; + spin_unlock(&ac97->reg_lock); + if (old != bits) { + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); + snd_ac97_update_bits(ac97, reg, AC97_SC_SPSR_MASK, bits); + } + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); + return 0; +} + +/** + * snd_ac97_set_rate - change the rate of the given input/output. + * @ac97: the ac97 instance + * @reg: the register to change + * @rate: the sample rate to set + * + * Changes the rate of the given input/output on the codec. + * If the codec doesn't support VAR, the rate must be 48000 (except + * for SPDIF). + * + * The valid registers are AC97_PMC_MIC_ADC_RATE, + * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE and AC97_SPDIF. + * The SPDIF register is a pseudo-register to change the rate of SPDIF + * (only if supported). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) +{ + unsigned short mask; + unsigned int tmp; + + switch (reg) { + case AC97_PCM_MIC_ADC_RATE: + mask = 0x0000; + if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_PCM_FRONT_DAC_RATE: + mask = 0x0200; + if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0) /* VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_PCM_LR_ADC_RATE: + mask = 0x0100; + if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0) /* VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_SPDIF: + return set_spdif_rate(ac97, rate); + default: + return -EINVAL; + } + tmp = ((unsigned int)rate * ac97->clock) / 48000; + if (tmp > 65535) + return -EINVAL; + snd_ac97_update(ac97, reg, tmp & 0xffff); + snd_ac97_read(ac97, reg); + return 0; +} + + +#ifdef CONFIG_PM +/** + * snd_ac97_suspend - General suspend function for AC97 codec + * @ac97: the ac97 instance + * + * Suspends the codec, power down the chip. + */ +void snd_ac97_suspend(ac97_t *ac97) +{ + unsigned short power = (ac97->regs[AC97_POWERDOWN] ^ 0x8000) & ~0x8000; /* invert EAPD */ + + power |= 0x4000; /* Headphone amplifier powerdown */ + power |= 0x0300; /* ADC & DAC powerdown */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); + udelay(100); + power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); + udelay(100); + power |= 0x3800; /* AC-link powerdown, internal Clk disable */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); +} + +/** + * snd_ac97_resume - General resume function for AC97 codec + * @ac97: the ac97 instance + * + * Do the standard resume procedure, power up and restoring the + * old register values. + */ +void snd_ac97_resume(ac97_t *ac97) +{ + int i; + + if (ac97->reset) { + ac97->reset(ac97); + goto __reset_ready; + } + + snd_ac97_write(ac97, AC97_POWERDOWN, 0); + snd_ac97_write(ac97, AC97_RESET, 0); + udelay(100); + snd_ac97_write(ac97, AC97_POWERDOWN, 0); + snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); + + snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); + snd_ac97_write(ac97, AC97_MASTER, 0x8000); + for (i = 0; i < 10; i++) { + if (snd_ac97_read(ac97, AC97_MASTER) == 0x8000) + break; + mdelay(1); + } +__reset_ready: + + if (ac97->init) + ac97->init(ac97); + + /* restore ac97 status */ + for (i = 2; i < 0x7c ; i += 2) { + if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID) + continue; + /* restore only accessible registers + * some chip (e.g. nm256) may hang up when unsupported registers + * are accessed..! + */ + if (test_bit(i, ac97->reg_accessed)) + snd_ac97_write(ac97, i, ac97->regs[i]); + } +} +#endif + + +/* + */ +static int remove_ctl(ac97_t *ac97, const char *name) +{ + snd_ctl_elem_id_t id; + memset(&id, 0, sizeof(id)); + strcpy(id.name, name); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_remove_id(ac97->card, &id); +} + +static int rename_ctl(ac97_t *ac97, const char *src, const char *dst) +{ + snd_ctl_elem_id_t sid, did; + memset(&sid, 0, sizeof(sid)); + strcpy(sid.name, src); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + memset(&did, 0, sizeof(did)); + strcpy(did.name, dst); + did.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_rename_id(ac97->card, &sid, &did); +} + +static int swap_headphone(ac97_t *ac97, int remove_master) +{ + /* FIXME: error checks.. */ + if (remove_master) { + remove_ctl(ac97, "Master Playback Switch"); + remove_ctl(ac97, "Master Playback Volume"); + } else { + rename_ctl(ac97, "Master Playback Switch", "Line-Out Playback Switch"); + rename_ctl(ac97, "Master Playback Volume", "Line-Out Playback Volume"); + } + rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch"); + rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume"); + return 0; +} + + +/** + * snd_ac97_tune_hardware - tune up the hardware + * @ac97: the ac97 instance + * @pci: pci device + * @quirk: quirk list + * + * Do some workaround for each pci device, such as renaming of the + * headphone (true line-out) control as "Master". + * The quirk-list must be terminated with a zero-filled entry. + * + * Returns zero if successful, or a negative error code on failure. + */ + +int snd_ac97_tune_hardware(ac97_t *ac97, struct pci_dev *pci, struct ac97_quirk *quirk) +{ + unsigned short vendor, device; + + snd_assert(quirk, return -EINVAL); + + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &device); + + for (; quirk->vendor; quirk++) { + if (quirk->vendor == vendor && quirk->device == device) { + snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, vendor, device); + switch (quirk->type) { + case AC97_TUNE_HP_ONLY: + return swap_headphone(ac97, 1); + case AC97_TUNE_SWAP_HP: + return swap_headphone(ac97, 0); + } + snd_printk(KERN_ERR "invalid quirk type %d for %s\n", quirk->type, quirk->name); + return -EINVAL; + } + } + return 0; +} + + +/* + * Exported symbols + */ + +EXPORT_SYMBOL(snd_ac97_write); +EXPORT_SYMBOL(snd_ac97_read); +EXPORT_SYMBOL(snd_ac97_write_cache); +EXPORT_SYMBOL(snd_ac97_update); +EXPORT_SYMBOL(snd_ac97_update_bits); +EXPORT_SYMBOL(snd_ac97_mixer); +EXPORT_SYMBOL(snd_ac97_modem); +EXPORT_SYMBOL(snd_ac97_set_rate); +EXPORT_SYMBOL(snd_ac97_tune_hardware); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_ac97_resume); +#endif + +/* + * INIT part + */ + +static int __init alsa_ac97_init(void) +{ + return 0; +} + +static void __exit alsa_ac97_exit(void) +{ +} + +module_init(alsa_ac97_init) +module_exit(alsa_ac97_exit) diff -urN linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_id.h linux/sound/pci/ac97/ac97_id.h --- linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_id.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ac97/ac97_id.h 2003-02-18 09:42:15.000000000 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.2 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define AC97_ID_AK4540 0x414b4d00 +#define AC97_ID_AK4542 0x414b4d01 +#define AC97_ID_AD1819 0x41445303 +#define AC97_ID_AD1881 0x41445340 +#define AC97_ID_AD1881A 0x41445348 +#define AC97_ID_AD1885 0x41445360 +#define AC97_ID_AD1886 0x41445361 +#define AC97_ID_AD1887 0x41445362 +#define AC97_ID_AD1886A 0x41445363 +#define AC97_ID_AD1980 0x41445370 +#define AC97_ID_TR28028 0x54524108 +#define AC97_ID_STAC9700 0x83847600 +#define AC97_ID_STAC9704 0x83847604 +#define AC97_ID_STAC9705 0x83847605 +#define AC97_ID_STAC9708 0x83847608 +#define AC97_ID_STAC9721 0x83847609 +#define AC97_ID_STAC9744 0x83847644 +#define AC97_ID_STAC9756 0x83847656 +#define AC97_ID_CS4297A 0x43525910 +#define AC97_ID_CS4299 0x43525930 +#define AC97_ID_CS4201 0x43525948 +#define AC97_ID_CS4205 0x43525958 +#define AC97_ID_CS_MASK 0xfffffff8 /* bit 0-2: rev */ +#define AC97_ID_ALC650 0x414c4720 +#define AC97_ID_YMF753 0x594d4803 +#define AC97_ID_VT1616 0x49434551 diff -urN linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_patch.c linux/sound/pci/ac97/ac97_patch.c --- linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_patch.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ac97/ac97_patch.c 2003-02-25 09:56:25.000000000 -0700 @@ -0,0 +1,375 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.2 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ac97_patch.h" + +/* + * Chip specific initialization + */ + +int patch_yamaha_ymf753(ac97_t * ac97) +{ + /* Patch for Yamaha YMF753, Copyright (c) by David Shust, dshust@shustring.com. + This chip has nonstandard and extended behaviour with regard to its S/PDIF output. + The AC'97 spec states that the S/PDIF signal is to be output at pin 48. + The YMF753 will ouput the S/PDIF signal to pin 43, 47 (EAPD), or 48. + By default, no output pin is selected, and the S/PDIF signal is not output. + There is also a bit to mute S/PDIF output in a vendor-specific register. + */ + ac97->caps |= AC97_BC_BASS_TREBLE; + ac97->caps |= 0x04 << 10; /* Yamaha 3D enhancement */ + return 0; +} + +int patch_wolfson00(ac97_t * ac97) +{ + /* This sequence is suspect because it was designed for + the WM9704, and is known to fail when applied to the + WM9707. If you're having trouble initializing a + WM9700, this is the place to start looking. + Randolph Bentson */ + + // WM9701A + snd_ac97_write_cache(ac97, 0x72, 0x0808); + snd_ac97_write_cache(ac97, 0x74, 0x0808); + + // patch for DVD noise + snd_ac97_write_cache(ac97, 0x5a, 0x0200); + + // init vol + snd_ac97_write_cache(ac97, 0x70, 0x0808); + + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + +int patch_wolfson03(ac97_t * ac97) +{ + /* This is known to work for the ViewSonic ViewPad 1000 + Randolph Bentson */ + + // WM9703/9707 + snd_ac97_write_cache(ac97, 0x72, 0x0808); + snd_ac97_write_cache(ac97, 0x20, 0x8000); + return 0; +} + +int patch_wolfson04(ac97_t * ac97) +{ + // WM9704 + snd_ac97_write_cache(ac97, 0x72, 0x0808); + snd_ac97_write_cache(ac97, 0x74, 0x0808); + + // patch for DVD noise + snd_ac97_write_cache(ac97, 0x5a, 0x0200); + + // init vol + snd_ac97_write_cache(ac97, 0x70, 0x0808); + + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + +int patch_tritech_tr28028(ac97_t * ac97) +{ + snd_ac97_write_cache(ac97, 0x26, 0x0300); + snd_ac97_write_cache(ac97, 0x26, 0x0000); + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000); + return 0; +} + +int patch_sigmatel_stac9708(ac97_t * ac97) +{ + unsigned int codec72, codec6c; + + codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000; + codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG); + + if ((codec72==0) && (codec6c==0)) { + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007); + } else if ((codec72==0x8000) && (codec6c==0)) { + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008); + } else if ((codec72==0x8000) && (codec6c==0x0080)) { + /* nothing */ + } + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +int patch_sigmatel_stac9721(ac97_t * ac97) +{ + if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) { + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + } + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +int patch_sigmatel_stac9744(ac97_t * ac97) +{ + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +int patch_sigmatel_stac9756(ac97_t * ac97) +{ + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +int patch_cirrus_spdif(ac97_t * ac97) +{ + /* Basically, the cs4201/cs4205/cs4297a has non-standard sp/dif registers. + WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC? *sigh* + - sp/dif EA ID is not set, but sp/dif is always present. + - enable/disable is spdif register bit 15. + - sp/dif control register is 0x68. differs from AC97: + - valid is bit 14 (vs 15) + - no DRS + - only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48) + - sp/dif ssource select is in 0x5e bits 0,1. + */ + + ac97->flags |= AC97_CS_SPDIF; + ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000; + ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ + snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080); + return 0; +} + +int patch_cirrus_cs4299(ac97_t * ac97) +{ + /* force the detection of PC Beep */ + ac97->flags |= AC97_HAS_PC_BEEP; + + return patch_cirrus_spdif(ac97); +} + +int patch_conexant(ac97_t * ac97) +{ + ac97->flags |= AC97_CX_SPDIF; + ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ + return 0; +} + +int patch_ad1819(ac97_t * ac97) +{ + // patch for Analog Devices + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); /* select all codecs */ + return 0; +} + +static unsigned short patch_ad1881_unchained(ac97_t * ac97, int idx, unsigned short mask) +{ + unsigned short val; + + // test for unchained codec + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, mask); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); /* ID0C, ID1C, SDIE = off */ + val = snd_ac97_read(ac97, AC97_VENDOR_ID2); + if ((val & 0xff40) != 0x5340) + return 0; + ac97->spec.ad18xx.unchained[idx] = mask; + ac97->spec.ad18xx.id[idx] = val; + return mask; +} + +static int patch_ad1881_chained1(ac97_t * ac97, int idx, unsigned short codec_bits) +{ + static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 }; + unsigned short val; + + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, cfg_bits[idx]); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004); // SDIE + val = snd_ac97_read(ac97, AC97_VENDOR_ID2); + if ((val & 0xff40) != 0x5340) + return 0; + if (codec_bits) + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits); + ac97->spec.ad18xx.chained[idx] = cfg_bits[idx]; + ac97->spec.ad18xx.id[idx] = val; + return 1; +} + +static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, int cidx2) +{ + // already detected? + if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1]) + cidx1 = -1; + if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2]) + cidx2 = -1; + if (cidx1 < 0 && cidx2 < 0) + return; + // test for chained codecs + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[unchained_idx]); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002); // ID1C + if (cidx1 >= 0) { + if (patch_ad1881_chained1(ac97, cidx1, 0x0006)) // SDIE | ID1C + patch_ad1881_chained1(ac97, cidx2, 0); + else if (patch_ad1881_chained1(ac97, cidx2, 0x0006)) // SDIE | ID1C + patch_ad1881_chained1(ac97, cidx1, 0); + } else if (cidx2 >= 0) { + patch_ad1881_chained1(ac97, cidx2, 0); + } +} + +int patch_ad1881(ac97_t * ac97) +{ + static const char cfg_idxs[3][2] = { + {2, 1}, + {0, 2}, + {0, 1} + }; + + // patch for Analog Devices + unsigned short codecs[3]; + int idx, num; + + init_MUTEX(&ac97->spec.ad18xx.mutex); + + codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12)); + codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14)); + codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13)); + + snd_runtime_check(codecs[0] | codecs[1] | codecs[2], goto __end); + + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.unchained[idx]) + patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]); + + if (ac97->spec.ad18xx.id[1]) { + ac97->flags |= AC97_AD_MULTI; + ac97->scaps |= AC97_SCAP_SURROUND_DAC; + } + if (ac97->spec.ad18xx.id[2]) { + ac97->flags |= AC97_AD_MULTI; + ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; + } + + __end: + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + /* check if only one codec is present */ + for (idx = num = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) + num++; + if (num == 1) { + /* ok, deselect all ID bits */ + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); + } + /* required for AD1886/AD1885 combination */ + ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->spec.ad18xx.id[0]) { + ac97->id &= 0xffff0000; + ac97->id |= ac97->spec.ad18xx.id[0]; + } + return 0; +} + +int patch_ad1885(ac97_t * ac97) +{ + unsigned short jack; + + patch_ad1881(ac97); + /* This is required to deal with the Intel D815EEAL2 */ + /* i.e. Line out is actually headphone out from codec */ + + /* turn off jack sense bits D8 & D9 */ + jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF); + snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300); + return 0; +} + +int patch_ad1886(ac97_t * ac97) +{ + patch_ad1881(ac97); + /* Presario700 workaround */ + /* for Jack Sense/SPDIF Register misetting causing */ + snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010); + return 0; +} + +int patch_ad1980(ac97_t * ac97) +{ + unsigned short misc; + + patch_ad1881(ac97); + /* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */ + /* it seems that most vendors connect line-out connector to headphone out of AC'97 */ + misc = snd_ac97_read(ac97, AC97_AD_MISC); + snd_ac97_write_cache(ac97, AC97_AD_MISC, misc | 0x0420); + return 0; +} + +int patch_alc650(ac97_t * ac97) +{ + unsigned short val; + + /* check spdif */ + val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); + if (val & AC97_EA_SPCV) { + /* enable spdif in */ + snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, + snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x03); + } else + ac97->ext_id &= ~AC97_EI_SPDIF; /* disable extended-id */ + + val = snd_ac97_read(ac97, AC97_ALC650_MULTICH); + val &= ~0xc000; /* slot: 3,4,7,8,6,9 */ + snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val); + + /* full DAC volume */ + snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); + snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808); + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_patch.h linux/sound/pci/ac97/ac97_patch.h --- linux-2.4.21-rc1.orig/sound/pci/ac97/ac97_patch.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ac97/ac97_patch.h 2002-11-29 03:15:43.000000000 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.2 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +int patch_yamaha_ymf753(ac97_t * ac97); +int patch_wolfson00(ac97_t * ac97); +int patch_wolfson03(ac97_t * ac97); +int patch_wolfson04(ac97_t * ac97); +int patch_tritech_tr28028(ac97_t * ac97); +int patch_sigmatel_stac9708(ac97_t * ac97); +int patch_sigmatel_stac9721(ac97_t * ac97); +int patch_sigmatel_stac9744(ac97_t * ac97); +int patch_sigmatel_stac9756(ac97_t * ac97); +int patch_cirrus_cs4299(ac97_t * ac97); +int patch_cirrus_spdif(ac97_t * ac97); +int patch_conexant(ac97_t * ac97); +int patch_ad1819(ac97_t * ac97); +int patch_ad1881(ac97_t * ac97); +int patch_ad1885(ac97_t * ac97); +int patch_ad1886(ac97_t * ac97); +int patch_ad1980(ac97_t * ac97); +int patch_alc650(ac97_t * ac97); diff -urN linux-2.4.21-rc1.orig/sound/pci/ac97/ak4531_codec.c linux/sound/pci/ac97/ak4531_codec.c --- linux-2.4.21-rc1.orig/sound/pci/ac97/ak4531_codec.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ac97/ak4531_codec.c 2003-01-31 08:20:43.000000000 -0700 @@ -0,0 +1,447 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal routines for AK4531 codec + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Universal routines for AK4531 codec"); +MODULE_LICENSE("GPL"); + +#define chip_t ak4531_t + +static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531); + +/* + * + */ + +#if 0 + +static void snd_ak4531_dump(ak4531_t *ak4531) +{ + int idx; + + for (idx = 0; idx < 0x19; idx++) + printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]); +} + +#endif + +/* + * + */ + +#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ak4531_info_single, \ + .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \ + .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) } + +static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ak4531_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int val; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + val = (ak4531->regs[reg] >> shift) & mask; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + if (invert) { + val = mask - val; + } + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + int val; + + val = ucontrol->value.integer.value[0] & mask; + if (invert) { + val = mask - val; + } + val <<= shift; + spin_lock_irqsave(&ak4531->reg_lock, flags); + val = (ak4531->regs[reg] & ~(mask << shift)) | val; + change = val != ak4531->regs[reg]; + ak4531->write(ak4531, reg, ak4531->regs[reg] = val); + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ak4531_info_double, \ + .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \ + .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) } + +static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ak4531_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int left, right; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + left = (ak4531->regs[left_reg] >> left_shift) & mask; + right = (ak4531->regs[right_reg] >> right_shift) & mask; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + if (invert) { + left = mask - left; + right = mask - right; + } + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + return 0; +} + +static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + int left, right; + + left = ucontrol->value.integer.value[0] & mask; + right = ucontrol->value.integer.value[1] & mask; + if (invert) { + left = mask - left; + right = mask - right; + } + left <<= left_shift; + right <<= right_shift; + spin_lock_irqsave(&ak4531->reg_lock, flags); + if (left_reg == right_reg) { + left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right; + change = left != ak4531->regs[left_reg]; + ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); + } else { + left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left; + right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right; + change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg]; + ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); + ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right); + } + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ak4531_info_input_sw, \ + .get = snd_ak4531_get_input_sw, .put = snd_ak4531_put_input_sw, \ + .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } + +static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ak4531_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1; + ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1; + ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1; + ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return 0; +} + +static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + int change; + int val1, val2; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift)); + val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; + change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2]; + ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1); + ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2); + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_CONTROLS (sizeof(snd_ak4531_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ak4531_controls[] = { + +AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1), +AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1), + +AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1), +AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1), + +AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1), +AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1), +AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0), +AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0), + +AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1), +AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1), +AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0), +AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5), + +AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1), +AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1), +AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0), +AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1), + +AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1), +AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1), +AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0), +AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3), + +AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), +AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), +AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), +AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), + +AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), +AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), +AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0), +AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0), + +AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1), +AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1), +AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0), +AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0), + +AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1), +AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1), +AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0), +AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0), + +AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0), +AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0), +AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0), + +AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0), +AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0) +}; + +static int snd_ak4531_free(ak4531_t *ak4531) +{ + if (ak4531) { + if (ak4531->private_free) + ak4531->private_free(ak4531); + snd_magic_kfree(ak4531); + } + return 0; +} + +static int snd_ak4531_dev_free(snd_device_t *device) +{ + ak4531_t *ak4531 = snd_magic_cast(ak4531_t, device->device_data, return -ENXIO); + return snd_ak4531_free(ak4531); +} + +static u8 snd_ak4531_initial_map[0x19 + 1] = { + 0x9f, /* 00: Master Volume Lch */ + 0x9f, /* 01: Master Volume Rch */ + 0x9f, /* 02: Voice Volume Lch */ + 0x9f, /* 03: Voice Volume Rch */ + 0x9f, /* 04: FM Volume Lch */ + 0x9f, /* 05: FM Volume Rch */ + 0x9f, /* 06: CD Audio Volume Lch */ + 0x9f, /* 07: CD Audio Volume Rch */ + 0x9f, /* 08: Line Volume Lch */ + 0x9f, /* 09: Line Volume Rch */ + 0x9f, /* 0a: Aux Volume Lch */ + 0x9f, /* 0b: Aux Volume Rch */ + 0x9f, /* 0c: Mono1 Volume */ + 0x9f, /* 0d: Mono2 Volume */ + 0x9f, /* 0e: Mic Volume */ + 0x87, /* 0f: Mono-out Volume */ + 0x00, /* 10: Output Mixer SW1 */ + 0x00, /* 11: Output Mixer SW2 */ + 0x00, /* 12: Lch Input Mixer SW1 */ + 0x00, /* 13: Rch Input Mixer SW1 */ + 0x00, /* 14: Lch Input Mixer SW2 */ + 0x00, /* 15: Rch Input Mixer SW2 */ + 0x00, /* 16: Reset & Power Down */ + 0x00, /* 17: Clock Select */ + 0x00, /* 18: AD Input Select */ + 0x01 /* 19: Mic Amp Setup */ +}; + +int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531) +{ + unsigned int idx; + int err; + ak4531_t * ak4531; + static snd_device_ops_t ops = { + .dev_free = snd_ak4531_dev_free, + }; + + snd_assert(rak4531 != NULL, return -EINVAL); + *rak4531 = NULL; + snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL); + ak4531 = snd_magic_kcalloc(ak4531_t, 0, GFP_KERNEL); + if (ak4531 == NULL) + return -ENOMEM; + *ak4531 = *_ak4531; + spin_lock_init(&ak4531->reg_lock); + if ((err = snd_component_add(card, "AK4531")) < 0) { + snd_ak4531_free(ak4531); + return err; + } + strcpy(card->mixername, "Asahi Kasei AK4531"); + ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ + udelay(100); + ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ + for (idx = 0; idx < 0x19; idx++) { + if (idx == AK4531_RESET || idx == AK4531_CLOCK) + continue; + ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */ + } + for (idx = 0; idx < AK4531_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) { + snd_ak4531_free(ak4531); + return err; + } + } + snd_ak4531_proc_init(card, ak4531); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ak4531, &ops)) < 0) { + snd_ak4531_free(ak4531); + return err; + } + +#if 0 + snd_ak4531_dump(ak4531); +#endif + *rak4531 = ak4531; + return 0; +} + +/* + + */ + +static void snd_ak4531_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ak4531_t *ak4531 = snd_magic_cast(ak4531_t, entry->private_data, return); + + snd_iprintf(buffer, "Asahi Kasei AK4531\n\n"); + snd_iprintf(buffer, "Recording source : %s\n" + "MIC gain : %s\n", + ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer", + ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB"); +} + +static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(card, "ak4531", &entry)) + snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); +} + +EXPORT_SYMBOL(snd_ak4531_mixer); + +/* + * INIT part + */ + +static int __init alsa_ak4531_init(void) +{ + return 0; +} + +static void __exit alsa_ak4531_exit(void) +{ +} + +module_init(alsa_ak4531_init) +module_exit(alsa_ak4531_exit) diff -urN linux-2.4.21-rc1.orig/sound/pci/ali5451/Makefile linux/sound/pci/ali5451/Makefile --- linux-2.4.21-rc1.orig/sound/pci/ali5451/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ali5451/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ali5451.o + +list-multi := snd-ali5451.o + +snd-ali5451-objs := ali5451.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o + +include $(TOPDIR)/Rules.make + +snd-ali5451.o: $(snd-ali5451-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ali5451-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/ali5451/ali5451.c linux/sound/pci/ali5451/ali5451.c --- linux-2.4.21-rc1.orig/sound/pci/ali5451/ali5451.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ali5451/ali5451.c 2003-02-25 06:35:43.000000000 -0700 @@ -0,0 +1,2314 @@ +/* + * Matt Wu + * Apr 26, 2001 + * Routines for control of ALi pci audio M5451 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Lcodecnse as published by + * the Free Software Foundation; either version 2 of the Lcodecnse, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Lcodecnse for more details. + * + * You should have received a copy of the GNU General Public Lcodecnse + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __SNDRV_OSS_COMPAT__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Matt Wu "); +MODULE_DESCRIPTION("ALI M5451"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALI,M5451,pci},{ALI,M5451}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ALI 5451 PCI Audio."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(pcm_channels, "PCM Channels"); +MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); + +/* + * Debug part definitions + */ + +//#define ALI_DEBUG + +#ifdef ALI_DEBUG +#define snd_ali_printk(format, args...) printk(format, ##args); +#else +#define snd_ali_printk(format, args...) +#endif + +/* + * Constants definition + */ + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +#ifndef PCI_DEVICE_ID_ALI_5451 +#define PCI_DEVICE_ID_ALI_5451 0x5451 +#endif + +#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_ALI<<16)|PCI_DEVICE_ID_ALI_5451) + + +#define ALI_CHANNELS 32 + +#define ALI_PCM_IN_CHANNEL 31 +#define ALI_SPDIF_IN_CHANNEL 19 +#define ALI_SPDIF_OUT_CHANNEL 15 +#define ALI_CENTER_CHANNEL 24 +#define ALI_LEF_CHANNEL 23 +#define ALI_SURR_LEFT_CHANNEL 26 +#define ALI_SURR_RIGHT_CHANNEL 25 + +#define SNDRV_ALI_VOICE_TYPE_PCM 01 +#define SNDRV_ALI_VOICE_TYPE_OTH 02 + +#define ALI_5451_V02 0x02 + +/* + * Direct Registers + */ + +#define ALI_LEGACY_DMAR0 0x00 // ADR0 +#define ALI_LEGACY_DMAR4 0x04 // CNT0 +#define ALI_LEGACY_DMAR11 0x0b // MOD +#define ALI_LEGACY_DMAR15 0x0f // MMR +#define ALI_MPUR0 0x20 +#define ALI_MPUR1 0x21 +#define ALI_MPUR2 0x22 +#define ALI_MPUR3 0x23 + +#define ALI_AC97_WRITE 0x40 +#define ALI_AC97_READ 0x44 + +#define ALI_SCTRL 0x48 +#define ALI_AC97_GPIO 0x4c +#define ALI_SPDIF_CS 0x70 +#define ALI_SPDIF_CTRL 0x74 +#define ALI_START 0x80 +#define ALI_STOP 0x84 +#define ALI_CSPF 0x90 +#define ALI_AINT 0x98 +#define ALI_GC_CIR 0xa0 + #define ENDLP_IE 0x00001000 + #define MIDLP_IE 0x00002000 +#define ALI_AINTEN 0xa4 +#define ALI_VOLUME 0xa8 +#define ALI_SBDELTA_DELTA_R 0xac +#define ALI_MISCINT 0xb0 + #define ADDRESS_IRQ 0x00000020 + #define TARGET_REACHED 0x00008000 + #define MIXER_OVERFLOW 0x00000800 + #define MIXER_UNDERFLOW 0x00000400 +#define ALI_SBBL_SBCL 0xc0 +#define ALI_SBCTRL_SBE2R_SBDD 0xc4 +#define ALI_STIMER 0xc8 +#define ALI_GLOBAL_CONTROL 0xd4 + +#define ALI_CSO_ALPHA_FMS 0xe0 +#define ALI_LBA 0xe4 +#define ALI_ESO_DELTA 0xe8 +#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0 +#define ALI_EBUF1 0xf4 +#define ALI_EBUF2 0xf8 + +#define ALI_REG(codec, x) ((codec)->port + x) + +typedef struct snd_stru_ali ali_t; +typedef struct snd_ali_stru_voice snd_ali_voice_t; +#define chip_t ali_t + +typedef struct snd_ali_channel_control { + // register data + struct REGDATA { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + } data; + + // register addresses + struct REGS { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + unsigned int ac97read; + unsigned int ac97write; + } regs; + +} snd_ali_channel_control_t; + +struct snd_ali_stru_voice { + unsigned int number; + int use: 1, + pcm: 1, + midi: 1, + mode: 1, + synth: 1; + + /* PCM data */ + ali_t *codec; + snd_pcm_substream_t *substream; + snd_ali_voice_t *extra; + + int running: 1; + + int eso; /* final ESO value for channel */ + int count; /* runtime->period_size */ + + /* --- */ + + void *private_data; + void (*private_free)(void *private_data); +}; + + +typedef struct snd_stru_alidev { + + snd_ali_voice_t voices[ALI_CHANNELS]; + + unsigned int chcnt; /* num of opened channels */ + unsigned int chmap; /* bitmap for opened channels */ + unsigned int synthcount; + +} alidev_t; + + +#ifdef CONFIG_PM +#define ALI_GLOBAL_REGS 56 +#define ALI_CHANNEL_REGS 8 +typedef struct snd_ali_image { + unsigned long regs[ALI_GLOBAL_REGS]; + unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; +} ali_image_t; +#endif + + +struct snd_stru_ali { + unsigned long irq; + unsigned long port; + unsigned char revision; + + unsigned int hw_initialized: 1; + struct resource *res_port; + + struct pci_dev *pci; + struct pci_dev *pci_m1533; + struct pci_dev *pci_m7101; + + snd_card_t *card; + snd_pcm_t *pcm; + alidev_t synth; + snd_ali_channel_control_t chregs; + + /* S/PDIF Mask */ + unsigned int spdif_mask; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + ac97_t *ac97; + unsigned short ac97_ext_id; + unsigned short ac97_ext_status; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + +#ifdef CONFIG_PM + ali_image_t *image; +#endif +}; + +static struct pci_device_id snd_ali_ids[] __devinitdata = { + {0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + {0, } +}; +MODULE_DEVICE_TABLE(pci, snd_ali_ids); + +static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int); +static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short); +static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short); + +/* + * Debug Part + */ + +#ifdef ALI_DEBUG + +static void ali_read_regs(ali_t *codec, int channel) +{ + int i,j; + unsigned int dwVal; + + printk("channel %d registers map:\n", channel); + outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR)); + + printk(" "); + for(j=0;j<8;j++) + printk("%2.2x ", j*4); + printk("\n"); + + for (i=0; i<=0xf8/4;i++) { + if(i%8 == 0) + printk("%2.2x ", (i*4/0x10)*0x10); + dwVal = inl(ALI_REG(codec,i*4)); + printk("%8.8x ", dwVal); + if ((i+1)%8 == 0) + printk("\n"); + } + printk("\n"); +} +static void ali_read_cfg(unsigned int vendor, unsigned deviceid) +{ + unsigned int dwVal; + struct pci_dev *pci_dev = NULL; + int i,j; + + + pci_dev = pci_find_device(vendor, deviceid, pci_dev); + if (pci_dev == NULL) + return ; + + printk("\nM%x PCI CFG\n", deviceid); + printk(" "); + for(j=0;j<8;j++) + printk("%d ",j); + printk("\n"); + + for(i=0;i<8;i++) { + printk("%d ",i); + for(j=0;j<8;j++) + { + pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal); + printk("%8.8x ", dwVal); + } + printk("\n"); + } + } +static void ali_read_ac97regs(ali_t *codec, int secondary) +{ + unsigned short i,j; + unsigned short wVal; + + printk("\ncodec %d registers map:\n", secondary); + + printk(" "); + for(j=0;j<8;j++) + printk("%2.2x ",j*2); + printk("\n"); + + for (i=0; i<64;i++) { + if(i%8 == 0) + printk("%2.2x ", (i/8)*0x10); + wVal = snd_ali_codec_peek(codec, secondary, i*2); + printk("%4.4x ", wVal); + if ((i+1)%8 == 0) + printk("\n"); + } + printk("\n"); +} + +#endif + +/* + * AC97 ACCESS + */ + +static inline unsigned int snd_ali_5451_peek(ali_t *codec, + unsigned int port ) +{ + return (unsigned int)inl(ALI_REG(codec, port)); +} + +static inline void snd_ali_5451_poke( ali_t *codec, + unsigned int port, + unsigned int val ) +{ + outl((unsigned int)val, ALI_REG(codec, port)); +} + +static int snd_ali_codec_ready( ali_t *codec, + unsigned int port, + int sched ) +{ + signed long end_time; + + end_time = jiffies + 10 * (HZ >> 2); + do { + if (!(snd_ali_5451_peek(codec,port) & 0x8000)) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("ali_codec_ready: codec is not ready.\n "); + return -EIO; +} + +static int snd_ali_stimer_ready(ali_t *codec, int sched) +{ + signed long end_time; + unsigned long dwChk1,dwChk2; + + dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + + end_time = jiffies + 10 * (HZ >> 2); + do { + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + if (dwChk2 != dwChk1) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("ali_stimer_read: stimer is not ready.\n"); + return -EIO; +} + +static void snd_ali_codec_poke(ali_t *codec,int secondary, + unsigned short reg, + unsigned short val) +{ + unsigned int dwVal = 0; + unsigned int port = 0; + + if (reg >= 0x80) { + snd_printk("ali_codec_poke: reg(%xh) invalid.\n", reg); + return; + } + + port = codec->chregs.regs.ac97write; + + if (snd_ali_codec_ready(codec, port, 0) < 0) + return; + if (snd_ali_stimer_ready(codec, 0) < 0) + return; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000 | (val << 16); + if (secondary) dwVal |= 0x0080; + if (codec->revision == ALI_5451_V02) dwVal |= 0x0100; + + snd_ali_5451_poke(codec,port,dwVal); + + return ; +} + +static unsigned short snd_ali_codec_peek( ali_t *codec, + int secondary, + unsigned short reg) +{ + unsigned int dwVal = 0; + unsigned int port = 0; + + if (reg >= 0x80) { + snd_printk("ali_codec_peek: reg(%xh) invalid.\n", reg); + return ~0; + } + + port = codec->chregs.regs.ac97read; + + if (snd_ali_codec_ready(codec, port, 0) < 0) + return ~0; + if (snd_ali_stimer_ready(codec, 0) < 0) + return ~0; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000; /* bit 15*/ + if (secondary) dwVal |= 0x0080; + + snd_ali_5451_poke(codec, port, dwVal); + + if (snd_ali_stimer_ready(codec, 0) < 0) + return ~0; + if (snd_ali_codec_ready(codec, port, 0) < 0) + return ~0; + + return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16; +} + +static void snd_ali_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val ) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + + snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); + snd_ali_codec_poke(codec, 0, reg, val); + return ; +} + + +static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return -ENXIO); + + snd_ali_printk("codec_read reg=%xh.\n", reg); + return (snd_ali_codec_peek(codec, 0, reg)); +} + +/* + * AC97 Reset + */ + +static int snd_ali_reset_5451(ali_t *codec) +{ + struct pci_dev *pci_dev = NULL; + unsigned short wCount, wReg; + unsigned int dwVal; + + if ((pci_dev = codec->pci_m1533) != NULL) { + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + } + + pci_dev = codec->pci; + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); + udelay(500); + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); + udelay(5000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + + /* non-fatal if you have a non PM capable codec */ + /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */ + return 0; +} + +#ifdef CODEC_RESET + +static int snd_ali_reset_codec(ali_t *codec) +{ + struct pci_dev *pci_dev = NULL; + unsigned char bVal = 0; + unsigned int dwVal; + unsigned short wCount, wReg; + + pci_dev = codec->pci_m1533; + + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal |= 0x02; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(5000); + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal &= 0xfd; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(15000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + return -1; +} + +#endif + +/* + * ALI 5451 Controller + */ + +static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel) +{ + unsigned long dwVal = 0; + + dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); + dwVal |= 1 << (channel & 0x0000001f); + outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_disable_special_channel(ali_t *codec, unsigned int channel) +{ + unsigned long dwVal = 0; + + dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); + dwVal &= ~(1 << (channel & 0x0000001f)); + outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_enable_address_interrupt(ali_t * codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc |= ENDLP_IE; + gc |= MIDLP_IE; + outl( gc, ALI_REG(codec, ALI_GC_CIR)); +} + +static void snd_ali_disable_address_interrupt(ali_t * codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc &= ~ENDLP_IE; + gc &= ~MIDLP_IE; + outl(gc, ALI_REG(codec, ALI_GC_CIR)); +} + +#if 0 // not used +static void snd_ali_enable_voice_irq(ali_t *codec, unsigned int channel) +{ + unsigned int mask; + snd_ali_channel_control_t *pchregs = &(codec->chregs); + + snd_ali_printk("enable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten)); + pchregs->data.ainten |= mask; + outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten)); +} +#endif + +static void snd_ali_disable_voice_irq(ali_t *codec, unsigned int channel) +{ + unsigned int mask; + snd_ali_channel_control_t *pchregs = &(codec->chregs); + + snd_ali_printk("disable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten)); + pchregs->data.ainten &= ~mask; + outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten)); +} + +static int snd_ali_alloc_pcm_channel(ali_t *codec, int channel) +{ + unsigned int idx = channel & 0x1f; + + if (codec->synth.chcnt >= ALI_CHANNELS){ + snd_printk("ali_alloc_pcm_channel: no free channels.\n"); + return -1; + } + + if (!(codec->synth.chmap & (1 << idx))) { + codec->synth.chmap |= 1 << idx; + codec->synth.chcnt++; + snd_ali_printk("alloc_pcm_channel no. %d.\n",idx); + return idx; + } + return -1; +} + +static int snd_ali_find_free_channel(ali_t * codec, int rec) +{ + int idx; + int result = -1; + + snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm"); + + // recording + if (rec) { + if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<11) && + ( codec->revision == ALI_5451_V02 )) + idx = ALI_SPDIF_IN_CHANNEL; + else + idx = ALI_PCM_IN_CHANNEL; + + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) { + return result; + } else { + snd_printk("ali_find_free_channel: record channel is busy now.\n"); + return -1; + } + } + + //playback... + if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) { + idx = ALI_SPDIF_OUT_CHANNEL; + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) { + return result; + } else { + snd_printk("ali_find_free_channel: S/PDIF out channel is in busy now.\n"); + } + } + + for (idx = 0; idx < ALI_CHANNELS; idx++) { + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) + return result; + } + snd_printk("ali_find_free_channel: no free channels.\n"); + return -1; +} + +static void snd_ali_free_channel_pcm(ali_t *codec, int channel) +{ + unsigned int idx = channel & 0x0000001f; + + snd_ali_printk("free_channel_pcm channel=%d\n",channel); + + if (channel < 0 || channel >= ALI_CHANNELS) + return; + + if (!(codec->synth.chmap & (1 << idx))) { + snd_printk("ali_free_channel_pcm: channel %d is not in use.\n",channel); + return; + } else { + codec->synth.chmap &= ~(1 << idx); + codec->synth.chcnt--; + } +} + +#if 0 // not used +static void snd_ali_start_voice(ali_t * codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("start_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec,codec->chregs.regs.start)); +} +#endif + +static void snd_ali_stop_voice(ali_t * codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("stop_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec, codec->chregs.regs.stop)); +} + +/* + * S/PDIF Part + */ + +static void snd_ali_delay(ali_t *codec,int interval) +{ + unsigned long begintimer,currenttimer; + + begintimer = inl(ALI_REG(codec, ALI_STIMER)); + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + + while (currenttimer < begintimer + interval) { + if(snd_ali_stimer_ready(codec, 1) < 0) + break; + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + } +} + +static void snd_ali_detect_spdif_rate(ali_t *codec) +{ + u16 wval = 0; + u16 count = 0; + u8 bval = 0, R1 = 0, R2 = 0; + + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + bval |= 0x1F; + outb(bval,ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + + while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) { + count ++; + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R1 = bval & 0x1F; + } + + if (count > 50000) { + snd_printk("ali_detect_spdif_rate: timeout!\n"); + return; + } + + count = 0; + while (count++ <= 50000) { + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R2 = bval & 0x1F; + if (R2 != R1) R1 = R2; else break; + } + + if (count > 50000) { + snd_printk("ali_detect_spdif_rate: timeout!\n"); + return; + } + + if (R2 >= 0x0b && R2 <= 0x0e) { + wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x09 << 8 | (u16)0x05; + outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x02,ALI_REG(codec,ALI_SPDIF_CS + 3)); + } else if (R2 == 0x12) { + wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x0E << 8 | (u16)0x08; + outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x03,ALI_REG(codec,ALI_SPDIF_CS + 3)); + } +} + +static unsigned int snd_ali_get_spdif_in_rate(ali_t *codec) +{ + u32 dwRate = 0; + u8 bval = 0; + + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + bval &= 0x7F; + bval |= 0x40; + outb(bval, ALI_REG(codec,ALI_SPDIF_CTRL)); + + snd_ali_detect_spdif_rate(codec); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)); + bval &= 0x0F; + + if (bval == 0) dwRate = 44100; + if (bval == 1) dwRate = 48000; + if (bval == 2) dwRate = 32000; + + return dwRate; +} + +static void snd_ali_enable_spdif_in(ali_t *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal |= 1<<11; + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + dwVal |= 0x02; + outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + +static void snd_ali_disable_spdif_in(ali_t *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal &= ~(1<<11); + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + + +static void snd_ali_set_spdif_out_rate(ali_t *codec, unsigned int rate) +{ + unsigned char bVal; + unsigned int dwRate = 0; + + if (rate == 32000) dwRate = 0x300; + if (rate == 44100) dwRate = 0; + if (rate == 48000) dwRate = 0x200; + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + bVal &= (unsigned char)(~(1<<6)); + + bVal |= 0x80; //select right + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2)); + + bVal &= (~0x80); //select left + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2)); +} + +static void snd_ali_enable_spdif_out(ali_t *codec) +{ + unsigned short wVal; + unsigned char bVal; + + struct pci_dev *pci_dev = NULL; + + pci_dev = codec->pci_m1533; + if (pci_dev == NULL) + return; + pci_read_config_byte(pci_dev, 0x61, &bVal); + bVal |= 0x40; + pci_write_config_byte(pci_dev, 0x61, bVal); + pci_read_config_byte(pci_dev, 0x7d, &bVal); + bVal |= 0x01; + pci_write_config_byte(pci_dev, 0x7d, bVal); + + pci_read_config_byte(pci_dev, 0x7e, &bVal); + bVal &= (~0x20); + bVal |= 0x10; + pci_write_config_byte(pci_dev, 0x7e, bVal); + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal | 0x20, ALI_REG(codec, ALI_SCTRL)); + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(bVal & ~(1<<6), ALI_REG(codec, ALI_SPDIF_CTRL)); + + { + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= (1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL); + } +} + +static void snd_ali_enable_spdif_chnout(ali_t *codec) +{ + unsigned short wVal = 0; + + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal &= ~(1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); +/* + wVal = inw(ALI_REG(codec, ALI_SPDIF_CS)); + if (flag & ALI_SPDIF_OUT_NON_PCM) + wVal |= 0x0002; + else + wVal &= (~0x0002); + outw(wVal, ALI_REG(codec, ALI_SPDIF_CS)); +*/ + snd_ali_enable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_chnout(ali_t *codec) +{ + unsigned short wVal = 0; + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= (1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_out(ali_t *codec) +{ + unsigned char bVal; + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal & (~0x20), ALI_REG(codec, ALI_SCTRL)); + + snd_ali_disable_spdif_chnout(codec); +} + +static void snd_ali_update_ptr(ali_t *codec,int channel) +{ + snd_ali_voice_t *pvoice = NULL; + snd_pcm_runtime_t *runtime; + snd_ali_channel_control_t *pchregs = NULL; + unsigned int old, mask; +#ifdef ALI_DEBUG + unsigned int temp, cspf; +#endif + + pchregs = &(codec->chregs); + + // check if interrupt occurred for channel + old = pchregs->data.aint; + mask = ((unsigned int) 1L) << (channel & 0x1f); + + if (!(old & mask)) + return; + + pvoice = &codec->synth.voices[channel]; + runtime = pvoice->substream->runtime; + + udelay(100); + spin_lock(&codec->reg_lock); + + if (pvoice->pcm && pvoice->substream) { + /* pcm interrupt */ +#ifdef ALI_DEBUG + outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR)); + temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask; +#endif + if (pvoice->running) { + snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",(u16)temp,cspf); + spin_unlock(&codec->reg_lock); + snd_pcm_period_elapsed(pvoice->substream); + spin_lock(&codec->reg_lock); + } else { + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + } else if (codec->synth.voices[channel].synth) { + /* synth interrupt */ + } else if (codec->synth.voices[channel].midi) { + /* midi interrupt */ + } else { + /* unknown interrupt */ + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + spin_unlock(&codec->reg_lock); + outl(mask,ALI_REG(codec,pchregs->regs.aint)); + pchregs->data.aint = old & (~mask); +} + +static void snd_ali_interrupt(ali_t * codec) +{ + int channel; + unsigned int audio_int; + snd_ali_channel_control_t *pchregs = NULL; + pchregs = &(codec->chregs); + + audio_int = inl(ALI_REG(codec, ALI_MISCINT)); + if (audio_int & ADDRESS_IRQ) { + // get interrupt status for all channels + pchregs->data.aint = inl(ALI_REG(codec,pchregs->regs.aint)); + for (channel = 0; channel < ALI_CHANNELS; channel++) { + snd_ali_update_ptr(codec, channel); + } + } + outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), + ALI_REG(codec,ALI_MISCINT)); +} + + +static void snd_ali_card_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + ali_t *codec = snd_magic_cast(ali_t, dev_id, return); + + if (codec == NULL) + return; + snd_ali_interrupt(codec); +} + + +static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec) +{ + snd_ali_voice_t *pvoice = NULL; + unsigned long flags; + int idx; + + snd_ali_printk("alloc_voice: type=%d rec=%d\n",type,rec); + + spin_lock_irqsave(&codec->voice_alloc, flags); + if (type == SNDRV_ALI_VOICE_TYPE_PCM) { + idx = snd_ali_find_free_channel(codec,rec); + if(idx < 0) { + snd_printk("ali_alloc_voice: err.\n"); + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return NULL; + } + pvoice = &(codec->synth.voices[idx]); + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->mode = rec; + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return pvoice; + } + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return NULL; +} + + +static void snd_ali_free_voice(ali_t * codec, snd_ali_voice_t *pvoice) +{ + unsigned long flags; + void (*private_free)(void *); + void *private_data; + + snd_ali_printk("free_voice: channel=%d\n",pvoice->number); + if (pvoice == NULL || !pvoice->use) + return; + snd_ali_clear_voices(codec, pvoice->number, pvoice->number); + spin_lock_irqsave(&codec->voice_alloc, flags); + private_free = pvoice->private_free; + private_data = pvoice->private_data; + pvoice->private_free = NULL; + pvoice->private_data = NULL; + if (pvoice->pcm) { + snd_ali_free_channel_pcm(codec, pvoice->number); + } + pvoice->use = pvoice->pcm = pvoice->synth = 0; + pvoice->substream = NULL; + spin_unlock_irqrestore(&codec->voice_alloc, flags); + if (private_free) + private_free(private_data); +} + + +static void snd_ali_clear_voices(ali_t * codec, + unsigned int v_min, + unsigned int v_max) +{ + unsigned int i; + + for (i = v_min; i <= v_max; i++) { + snd_ali_stop_voice(codec, i); + snd_ali_disable_voice_irq(codec, i); + } +} + +static void snd_ali_write_voice_regs(ali_t * codec, + unsigned int Channel, + unsigned int LBA, + unsigned int CSO, + unsigned int ESO, + unsigned int DELTA, + unsigned int ALPHA_FMS, + unsigned int GVSEL, + unsigned int PAN, + unsigned int VOL, + unsigned int CTRL, + unsigned int EC) +{ + unsigned int ctlcmds[4]; + + outb((unsigned char)(Channel & 0x001f),ALI_REG(codec,ALI_GC_CIR)); + + ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); + ctlcmds[1] = LBA; + ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff); + ctlcmds[3] = (GVSEL << 31) | + ((PAN & 0x0000007f) << 24) | + ((VOL & 0x000000ff) << 16) | + ((CTRL & 0x0000000f) << 12) | + (EC & 0x00000fff); + + outb(Channel, ALI_REG(codec, ALI_GC_CIR)); + + outl(ctlcmds[0], ALI_REG(codec,ALI_CSO_ALPHA_FMS)); + outl(ctlcmds[1], ALI_REG(codec,ALI_LBA)); + outl(ctlcmds[2], ALI_REG(codec,ALI_ESO_DELTA)); + outl(ctlcmds[3], ALI_REG(codec,ALI_GVSEL_PAN_VOC_CTRL_EC)); + + outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */ + outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */ +} + +static unsigned int snd_ali_convert_rate(unsigned int rate, int rec) +{ + unsigned int delta; + + if (rate < 4000) rate = 4000; + if (rate > 48000) rate = 48000; + + if (rec) { + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + } else { + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; + } + + return delta; +} + +static unsigned int snd_ali_control_mode(snd_pcm_substream_t *substream) +{ + unsigned int CTRL; + snd_pcm_runtime_t *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; // 16-bit data + if (!snd_pcm_format_unsigned(runtime->format)) + CTRL |= 0x00000002; // signed data + if (runtime->channels > 1) + CTRL |= 0x00000004; // stereo data + return CTRL; +} + +/* + * PCM part + */ + +static int snd_ali_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_ali_trigger(snd_pcm_substream_t *substream, + int cmd) + +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *s; + unsigned int what, whati, capture_flag; + snd_ali_voice_t *pvoice = NULL, *evoice = NULL; + unsigned int val; + int do_start; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + do_start = 1; break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + do_start = 0; break; + default: + return -EINVAL; + } + + what = whati = capture_flag = 0; + s = substream; + do { + if ((ali_t *) _snd_pcm_chip(s->pcm) == codec) { + pvoice = (snd_ali_voice_t *) s->runtime->private_data; + evoice = pvoice->extra; + what |= 1 << (pvoice->number & 0x1f); + if (evoice == NULL) { + whati |= 1 << (pvoice->number & 0x1f); + } else { + whati |= 1 << (evoice->number & 0x1f); + what |= 1 << (evoice->number & 0x1f); + } + if (do_start) { + pvoice->running = 1; + if (evoice != NULL) + evoice->running = 1; + } else { + pvoice->running = 0; + if (evoice != NULL) + evoice->running = 0; + } + snd_pcm_trigger_done(s, substream); + if (pvoice->mode) + capture_flag = 1; + } + s = s->link_next; + } while (s != substream); + spin_lock(&codec->reg_lock); + if (! do_start) { + outl(what, ALI_REG(codec, ALI_STOP)); + } + val = inl(ALI_REG(codec, ALI_AINTEN)); + if (do_start) { + val |= whati; + } else { + val &= ~whati; + } + outl(val, ALI_REG(codec, ALI_AINTEN)); + if (do_start) { + outl(what, ALI_REG(codec, ALI_START)); + } + snd_ali_printk("trigger: what=%xh whati=%xh\n",what,whati); + spin_unlock(&codec->reg_lock); + + return 0; +} + +static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice->extra; + int err; + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) return err; + + /* voice management */ + + if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + if (evoice == NULL) + return -ENOMEM; + pvoice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = evoice = NULL; + } + } + + return 0; +} + +static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice ? pvoice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = NULL; + } + return 0; +} + +static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ali_playback_prepare(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice->extra; + unsigned long flags; + + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + + snd_ali_printk("playback_prepare ...\n"); + + spin_lock_irqsave(&codec->reg_lock, flags); + + /* set Delta (rate) value */ + Delta = snd_ali_convert_rate(runtime->rate, 0); + + if ((pvoice->number == ALI_SPDIF_IN_CHANNEL) || + (pvoice->number == ALI_PCM_IN_CHANNEL)) + snd_ali_disable_special_channel(codec, pvoice->number); + else if ((inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) + && (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) { + if (codec->revision == ALI_5451_V02) { + snd_ali_set_spdif_out_rate(codec, runtime->rate); + Delta = 0x1000; + } + } + + /* set Loop Back Address */ + LBA = runtime->dma_addr; + + /* set interrupt count size */ + pvoice->count = runtime->period_size; + + /* set target ESO for channel */ + pvoice->eso = runtime->buffer_size; + + snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",pvoice->eso,pvoice->count); + + /* set ESO to capture first MIDLP interrupt */ + ESO = pvoice->eso -1; + /* set ctrl mode */ + CTRL = snd_ali_control_mode(substream); + + GVSEL = 1; + PAN = 0; + VOL = 0; + EC = 0; + snd_ali_printk("playback_prepare:\n ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL); + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + if (evoice != NULL) { + evoice->count = pvoice->count; + evoice->eso = pvoice->count << 1; + ESO = evoice->eso - 1; + snd_ali_write_voice_regs(codec, + evoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + (unsigned int)0x7f, + (unsigned int)0x3ff, + CTRL, + EC); + } + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; +} + + +static int snd_ali_capture_prepare(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned long flags; + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + u8 bValue; + + spin_lock_irqsave(&codec->reg_lock, flags); + + snd_ali_printk("capture_prepare...\n"); + + snd_ali_enable_special_channel(codec,pvoice->number); + + Delta = snd_ali_convert_rate(runtime->rate, 1); + + // Prepare capture intr channel + if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { + + unsigned int rate; + + if (codec->revision != ALI_5451_V02) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -1; + } + rate = snd_ali_get_spdif_in_rate(codec); + if (rate == 0) { + snd_printk("ali_capture_preapre: spdif rate detect err!\n"); + rate = 48000; + } + bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + if (bValue & 0x10) { + outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL)); + printk("clear SPDIF parity error flag.\n"); + } + + if (rate != 48000) + Delta = ((rate << 12)/runtime->rate)&0x00ffff; + } + + // set target ESO for channel + pvoice->eso = runtime->buffer_size; + + // set interrupt count size + pvoice->count = runtime->period_size; + + // set Loop Back Address + LBA = runtime->dma_addr; + + // set ESO to capture first MIDLP interrupt + ESO = pvoice->eso - 1; + CTRL = snd_ali_control_mode(substream); + GVSEL = 0; + PAN = 0x00; + VOL = 0x00; + EC = 0; + + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + + + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return 0; +} + + +static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned int cso; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + if (!pvoice->running) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock_irqrestore(&codec->reg_lock, flags); + snd_ali_printk("playback pointer returned cso=%xh.\n", cso); + + return cso; +} + + +static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned int cso; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + if (!pvoice->running) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return cso; +} + +static snd_pcm_hardware_t snd_ali_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 64, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_ali_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + unsigned long flags; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + ali_t *codec; + + if (pvoice) { + codec = pvoice->codec; + spin_lock_irqsave(&codec->reg_lock, flags); + snd_ali_free_voice(pvoice->codec, pvoice); + spin_unlock_irqrestore(&codec->reg_lock, flags); + } +} + +static int snd_ali_playback_open(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice; + unsigned long flags = 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + if (pvoice == NULL) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -EAGAIN; + } + pvoice->codec = codec; + spin_unlock_irqrestore(&codec->reg_lock, flags); + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + + runtime->hw = snd_ali_playback; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +static int snd_ali_capture_open(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1); + if (pvoice == NULL) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -EAGAIN; + } + pvoice->codec = codec; + spin_unlock_irqrestore(&codec->reg_lock, flags); + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + runtime->hw = snd_ali_capture; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +static int snd_ali_playback_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_ali_capture_close(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + + snd_ali_disable_special_channel(codec,pvoice->number); + + return 0; +} + +static snd_pcm_ops_t snd_ali_playback_ops = { + .open = snd_ali_playback_open, + .close = snd_ali_playback_close, + .ioctl = snd_ali_ioctl, + .hw_params = snd_ali_playback_hw_params, + .hw_free = snd_ali_playback_hw_free, + .prepare = snd_ali_playback_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_playback_pointer, +}; + +static snd_pcm_ops_t snd_ali_capture_ops = { + .open = snd_ali_capture_open, + .close = snd_ali_capture_close, + .ioctl = snd_ali_ioctl, + .hw_params = snd_ali_capture_hw_params, + .hw_free = snd_ali_capture_hw_free, + .prepare = snd_ali_capture_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_capture_pointer, +}; + + +static void snd_ali_pcm_free(snd_pcm_t *pcm) +{ + ali_t *codec = snd_magic_cast(ali_t, pcm->private_data, return); + codec->pcm = NULL; +} + +static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) *rpcm = NULL; + err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm); + if (err < 0) { + snd_printk("snd_ali_pcm: err called snd_pcm_new.\n"); + return err; + } + pcm->private_data = codec; + pcm->private_free = snd_ali_pcm_free; + pcm->info_flags = 0; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops); + + snd_pcm_lib_preallocate_pci_pages_for_all(codec->pci, pcm, 64*1024, 128*1024); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "ALI 5451"); + codec->pcm = pcm; + if (rpcm) *rpcm = pcm; + return 0; +} + +#define ALI5451_SPDIF(xname, xindex, value) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ +.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \ +.put = snd_ali5451_spdif_put, .private_value = value} + +static int snd_ali5451_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + unsigned int enable; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + switch(kcontrol->private_value) { + case 0: + enable = (codec->spdif_mask & 0x02) ? 1 : 0; + break; + case 1: + enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0; + break; + case 2: + enable = (codec->spdif_mask & 0x01) ? 1 : 0; + break; + default: + break; + } + ucontrol->value.integer.value[0] = enable; + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; +} + +static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + unsigned int change = 0, enable = 0; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + switch (kcontrol->private_value) { + case 0: + change = (codec->spdif_mask & 0x02) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x02; + snd_ali_enable_spdif_out(codec); + } else { + codec->spdif_mask &= ~(0x02); + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_out(codec); + } + } + break; + case 1: + change = (codec->spdif_mask & 0x04) ? 1 : 0; + change = change ^ enable; + if (change && (codec->spdif_mask & 0x02)) { + if (enable) { + codec->spdif_mask |= 0x04; + snd_ali_enable_spdif_chnout(codec); + } else { + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_chnout(codec); + } + } + break; + case 2: + change = (codec->spdif_mask & 0x01) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x01; + snd_ali_enable_spdif_in(codec); + } else { + codec->spdif_mask &= ~(0x01); + snd_ali_disable_spdif_in(codec); + } + } + break; + default: + break; + } + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return change; +} + +static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinitdata = { + /* spdif aplayback switch */ + /* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */ + ALI5451_SPDIF("IEC958 Output switch", 0, 0), + /* spdif out to spdif channel */ + ALI5451_SPDIF("IEC958 Channel Output Switch", 0, 1), + /* spdif in from spdif channel */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) +}; + +static void snd_ali_mixer_free_ac97(ac97_t *ac97) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + codec->ac97 = NULL; +} + +static int __devinit snd_ali_mixer(ali_t * codec) +{ + ac97_t ac97; + unsigned int idx; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ali_codec_write; + ac97.read = snd_ali_codec_read; + ac97.private_data = codec; + ac97.private_free = snd_ali_mixer_free_ac97; + if ((err = snd_ac97_mixer(codec->card, &ac97, &codec->ac97)) < 0) { + snd_printk("ali mixer creating error.\n"); + return err; + } + if (codec->revision == ALI_5451_V02) { + for(idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) { + err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); + if (err < 0) return err; + } + } + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_ali_suspend(struct pci_dev *dev, u32 state) +#else +static void snd_ali_suspend(struct pci_dev *dev) +#endif +{ +#ifndef PCI_OLD_SUSPEND + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO); +#else + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return); +#endif + ali_image_t *im; + int i, j; + + im = chip->image; + if (! im) +#ifndef PCI_OLD_SUSPEND + return -ENXIO; +#else + return; +#endif + + spin_lock_irq(&chip->reg_lock); + + im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT)); + // im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); + im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP)); + + // disable all IRQ bits + outl(0, ALI_REG(chip, ALI_MISCINT)); + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP)) + continue; + im->regs[i] = inl(ALI_REG(chip, i*4)); + } + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0)); + } + + // stop all HW channel + outl(0xffffffff, ALI_REG(chip, ALI_STOP)); + + spin_unlock_irq(&chip->reg_lock); +#ifndef PCI_OLD_SUSPEND + return 0; +#endif +} + +#ifndef PCI_OLD_SUSPEND +static int snd_ali_resume(struct pci_dev *dev) +#else +static void snd_ali_resume(struct pci_dev *dev) +#endif +{ +#ifndef PCI_OLD_SUSPEND + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO); +#else + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return); +#endif + ali_image_t *im; + int i, j; + + im = chip->image; + if (! im) +#ifndef PCI_OLD_SUSPEND + return -ENXIO; +#else + return; +#endif + + pci_enable_device(chip->pci); + + spin_lock_irq(&chip->reg_lock); + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0)); + } + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || (i*4 == ALI_START)) + continue; + outl(im->regs[i], ALI_REG(chip, i*4)); + } + + snd_ac97_resume(chip->ac97); + + // start HW channel + outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START)); + // restore IRQ enable bits + outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT)); + + spin_unlock_irq(&chip->reg_lock); +#ifndef PCI_OLD_SUSPEND + return 0; +#endif +} +#endif + +static int snd_ali_free(ali_t * codec) +{ + if (codec->hw_initialized) + snd_ali_disable_address_interrupt(codec); + if (codec->irq >= 0) { + synchronize_irq(codec->irq); + free_irq(codec->irq, (void *)codec); + } + if (codec->res_port) { + release_resource(codec->res_port); + kfree_nocheck(codec->res_port); + } +#ifdef CONFIG_PM + if (codec->image) + kfree(codec->image); +#endif + snd_magic_kfree(codec); + return 0; +} + +static int snd_ali_chip_init(ali_t *codec) +{ + unsigned int legacy; + unsigned char temp; + struct pci_dev *pci_dev = NULL; + + snd_ali_printk("chip initializing ... \n"); + + if (snd_ali_reset_5451(codec)) { + snd_printk("ali_chip_init: reset 5451 error.\n"); + return -1; + } + + if (codec->revision == ALI_5451_V02) { + pci_dev = codec->pci_m1533; + pci_read_config_byte(pci_dev, 0x59, &temp); + temp |= 0x80; + pci_write_config_byte(pci_dev, 0x59, temp); + + pci_dev = codec->pci_m7101; + pci_read_config_byte(pci_dev, 0xb8, &temp); + temp |= 0x20; + pci_write_config_byte(pci_dev, 0xB8, temp); + } + + pci_read_config_dword(codec->pci, 0x44, &legacy); + legacy &= 0xff00ff00; + legacy |= 0x000800aa; + pci_write_config_dword(codec->pci, 0x44, legacy); + + outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + outl(0x00000000, ALI_REG(codec, ALI_AINTEN)); + outl(0xffffffff, ALI_REG(codec, ALI_AINT)); + outl(0x00000000, ALI_REG(codec, ALI_VOLUME)); + outb(0x10, ALI_REG(codec, ALI_MPUR2)); + + codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID); + codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_STATUS); + if (codec->revision == ALI_5451_V02) { + snd_ali_enable_spdif_out(codec); + codec->spdif_mask = 0x00000002; + } + + snd_ali_printk("chip initialize succeed.\n"); + return 0; + +} + +static int __devinit snd_ali_resources(ali_t *codec) +{ + snd_ali_printk("resouces allocation ...\n"); + if ((codec->res_port = request_region(codec->port, 0x100, "ALI 5451")) == NULL) { + snd_printk("Unalbe to request io ports.\n"); + return -EBUSY; + } + + if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) { + snd_printk("Unable to request irq.\n"); + return -EBUSY; + } + codec->irq = codec->pci->irq; + snd_ali_printk("resouces allocated.\n"); + return 0; +} +static int snd_ali_dev_free(snd_device_t *device) +{ + ali_t *codec=snd_magic_cast(ali_t, device->device_data, return -ENXIO); + snd_ali_free(codec); + return 0; +} + +static int __devinit snd_ali_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + ali_t ** r_ali) +{ + ali_t *codec; + int i, err; + unsigned short cmdw = 0; + struct pci_dev *pci_dev = NULL; + static snd_device_ops_t ops = { + (snd_dev_free_t *)snd_ali_dev_free, + NULL, + NULL + }; + + *r_ali = NULL; + + snd_ali_printk("creating ...\n"); + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 31 bits */ + if (!pci_dma_supported(pci, 0x7fffffff)) { + snd_printk("architecture does not support 31bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x7fffffff); + + if ((codec = snd_magic_kcalloc(ali_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_alloc); + + codec->card = card; + codec->pci = pci; + codec->irq = -1; + codec->port = pci_resource_start(pci, 0); + pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision); + + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + + pci_set_master(pci); + pci_read_config_word(pci, PCI_COMMAND, &cmdw); + if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) { + cmdw |= PCI_COMMAND_IO; + pci_write_config_word(pci, PCI_COMMAND, cmdw); + } + pci_set_master(pci); + + if (snd_ali_resources(codec)) { + snd_ali_free(codec); + return -EBUSY; + } + + synchronize_irq(pci->irq); + + codec->synth.chmap = 0; + codec->synth.chcnt = 0; + codec->spdif_mask = 0; + codec->synth.synthcount = 0; + + if (codec->revision == ALI_5451_V02) + codec->chregs.regs.ac97read = ALI_AC97_WRITE; + else + codec->chregs.regs.ac97read = ALI_AC97_READ; + codec->chregs.regs.ac97write = ALI_AC97_WRITE; + + codec->chregs.regs.start = ALI_START; + codec->chregs.regs.stop = ALI_STOP; + codec->chregs.regs.aint = ALI_AINT; + codec->chregs.regs.ainten = ALI_AINTEN; + + codec->chregs.data.start = 0x00; + codec->chregs.data.stop = 0x00; + codec->chregs.data.aint = 0x00; + codec->chregs.data.ainten = 0x00; + + /* M1533: southbridge */ + pci_dev = pci_find_device(0x10b9, 0x1533, NULL); + codec->pci_m1533 = pci_dev; + if (! codec->pci_m1533) { + snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n"); + snd_ali_free(codec); + return -ENODEV; + } + /* M7101: power management */ + pci_dev = pci_find_device(0x10b9, 0x7101, NULL); + codec->pci_m7101 = pci_dev; + if (! codec->pci_m7101) { + snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n"); + snd_ali_free(codec); + return -ENODEV; + } + + snd_ali_printk("snd_device_new is called.\n"); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) { + snd_ali_free(codec); + return err; + } + + /* initialise synth voices*/ + for (i = 0; i < ALI_CHANNELS; i++ ) { + codec->synth.voices[i].number = i; + } + + if ((err = snd_ali_chip_init(codec)) < 0) { + snd_printk("ali create: chip init error.\n"); + return err; + } + +#ifdef CONFIG_PM + codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL); + if (! codec->image) + snd_printk("can't allocate apm buffer\n"); +#endif + + snd_ali_enable_address_interrupt(codec); + codec->hw_initialized = 1; + + *r_ali = codec; + snd_ali_printk("created.\n"); + return 0; +} + +static int __devinit snd_ali_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + ali_t *codec; + int err; + + snd_ali_printk("probe ...\n"); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ali_create(card, pci, pcm_channels[dev], &codec)) < 0) { + snd_card_free(card); + return err; + } + + snd_ali_printk("mixer building ...\n"); + if ((err = snd_ali_mixer(codec)) < 0) { + snd_card_free(card); + return err; + } + + snd_ali_printk("pcm building ...\n"); + if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "ALI5451"); + strcpy(card->shortname, "ALI 5451"); + + sprintf(card->longname, "%s at 0x%lx, irq %li", + card->shortname, codec->port, codec->irq); + + snd_ali_printk("register card.\n"); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, codec); + dev++; + return 0; +} + +static void __devexit snd_ali_remove(struct pci_dev *pci) +{ + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ALI 5451", + .id_table = snd_ali_ids, + .probe = snd_ali_probe, + .remove = __devexit_p(snd_ali_remove), +#ifdef CONFIG_PM + .suspend = snd_ali_suspend, + .resume = snd_ali_resume, +#endif +}; + +static int __init alsa_card_ali_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ALi pci audio not found or device busy.\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ali_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ali_init) +module_exit(alsa_card_ali_exit) + +#ifndef MODULE + +/* format is: snd-ali5451=enable,index,id,pcm_channels */ + +static int __init alsa_card_ali_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ali5451=", alsa_card_ali_setup); + +#endif /* ifndef */ diff -urN linux-2.4.21-rc1.orig/sound/pci/als4000.c linux/sound/pci/als4000.c --- linux-2.4.21-rc1.orig/sound/pci/als4000.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/als4000.c 2003-02-25 06:35:40.000000000 -0700 @@ -0,0 +1,749 @@ +/* + * card-als4000.c - driver for Avance Logic ALS4000 based soundcards. + * Copyright (C) 2000 by Bart Hartgers , + * Jaroslav Kysela + * Copyright (C) 2002 by Andreas Mohr + * + * Framework borrowed from Massimo Piccioni's card-als100.c. + * + * NOTES + * + * Since Avance does not provide any meaningful documentation, and I + * bought an ALS4000 based soundcard, I was forced to base this driver + * on reverse engineering. + * + * Note: this is no longer true. Pretty verbose chip docu (ALS4000a.PDF) + * can be found on the ALSA web site. + * + * The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an + * ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport + * interface. These subsystems can be mapped into ISA io-port space, + * using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ + * services to the subsystems. + * + * While ALS4000 is very similar to a SoundBlaster, the differences in + * DMA and capturing require more changes to the SoundBlaster than + * desirable, so I made this separate driver. + * + * The ALS4000 can do real full duplex playback/capture. + * + * FMDAC: + * - 0x4f -> port 0x14 + * - port 0x15 |= 1 + * + * Enable/disable 3D sound: + * - 0x50 -> port 0x14 + * - change bit 6 (0x40) of port 0x15 + * + * Set QSound: + * - 0xdb -> port 0x14 + * - set port 0x15: + * 0x3e (mode 3), 0x3c (mode 2), 0x3a (mode 1), 0x38 (mode 0) + * + * Set KSound: + * - value -> some port 0x0c0d + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Bart Hartgers "); +MODULE_DESCRIPTION("Avance Logic ALS4000"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Avance Logic,ALS4000}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int joystick_port[SNDRV_CARDS] = +#ifdef CONFIG_ISA + {0x200}; /* enable as default */ +#else + {0}; /* disabled */ +#endif + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ALS4000 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC); +MODULE_PARM(joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)"); +MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED); + +#define chip_t sb_t + +typedef struct { + unsigned long gcr; +} snd_card_als4000_t; + +static struct pci_device_id snd_als4000_ids[] __devinitdata = { + { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_als4000_ids); + +static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val) +{ + outb(reg, port+0x0c); + outl(val, port+0x08); +} + +static inline void snd_als4000_gcr_write(sb_t *sb, u32 reg, u32 val) +{ + snd_als4000_gcr_write_addr(sb->alt_port, reg, val); +} + +static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg) +{ + outb(reg, port+0x0c); + return inl(port+0x08); +} + +static inline u32 snd_als4000_gcr_read(sb_t *sb, u32 reg) +{ + return snd_als4000_gcr_read_addr(sb->alt_port, reg); +} + +static void snd_als4000_set_rate(sb_t *chip, unsigned int rate) +{ + if (!(chip->mode & SB_RATE_LOCK)) { + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); + snd_sbdsp_command(chip, rate>>8); + snd_sbdsp_command(chip, rate); + } +} + +static void snd_als4000_set_capture_dma(sb_t *chip, dma_addr_t addr, unsigned size) +{ + snd_als4000_gcr_write(chip, 0xa2, addr); + snd_als4000_gcr_write(chip, 0xa3, (size-1)); +} + +static void snd_als4000_set_playback_dma(sb_t *chip, dma_addr_t addr, unsigned size) +{ + snd_als4000_gcr_write(chip, 0x91, addr); + snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000); +} + +#define ALS4000_FORMAT_SIGNED (1<<0) +#define ALS4000_FORMAT_16BIT (1<<1) +#define ALS4000_FORMAT_STEREO (1<<2) + +static int snd_als4000_get_format(snd_pcm_runtime_t *runtime) +{ + int result; + + result = 0; + if (snd_pcm_format_signed(runtime->format)) + result |= ALS4000_FORMAT_SIGNED; + if (snd_pcm_format_physical_width(runtime->format) == 16) + result |= ALS4000_FORMAT_16BIT; + if (runtime->channels > 1) + result |= ALS4000_FORMAT_STEREO; + return result; +} + +/* structure for setting up playback */ +static struct { + unsigned char dsp_cmd, dma_on, dma_off, format; +} playback_cmd_vals[]={ +/* ALS4000_FORMAT_U8_MONO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO }, +/* ALS4000_FORMAT_S8_MONO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO }, +/* ALS4000_FORMAT_U16L_MONO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO }, +/* ALS4000_FORMAT_S16L_MONO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO }, +/* ALS4000_FORMAT_U8_STEREO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO }, +/* ALS4000_FORMAT_S8_STEREO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO }, +/* ALS4000_FORMAT_U16L_STEREO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO }, +/* ALS4000_FORMAT_S16L_STEREO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO }, +}; +#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format]) + +/* structure for setting up capture */ +enum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 }; +static unsigned char capture_cmd_vals[]= +{ +CMD_WIDTH8|CMD_MONO, /* ALS4000_FORMAT_U8_MONO */ +CMD_WIDTH8|CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S8_MONO */ +CMD_MONO, /* ALS4000_FORMAT_U16L_MONO */ +CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S16L_MONO */ +CMD_WIDTH8|CMD_STEREO, /* ALS4000_FORMAT_U8_STEREO */ +CMD_WIDTH8|CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S8_STEREO */ +CMD_STEREO, /* ALS4000_FORMAT_U16L_STEREO */ +CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S16L_STEREO */ +}; +#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format]) + +static int snd_als4000_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_als4000_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_als4000_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long size; + unsigned count; + + chip->capture_format = snd_als4000_get_format(runtime); + + size = snd_pcm_lib_buffer_bytes(substream); + count = snd_pcm_lib_period_bytes(substream); + + if (chip->capture_format & ALS4000_FORMAT_16BIT) + count >>=1; + count--; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_als4000_set_rate(chip, runtime->rate); + snd_als4000_set_capture_dma(chip, runtime->dma_addr, size); + spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_lock_irqsave(&chip->mixer_lock, flags ); + snd_sbmixer_write(chip, 0xdc, count); + snd_sbmixer_write(chip, 0xdd, count>>8); + spin_unlock_irqrestore(&chip->mixer_lock, flags ); + return 0; +} + +static int snd_als4000_playback_prepare(snd_pcm_substream_t *substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long size; + unsigned count; + + chip->playback_format = snd_als4000_get_format(runtime); + + size = snd_pcm_lib_buffer_bytes(substream); + count = snd_pcm_lib_period_bytes(substream); + + if (chip->playback_format & ALS4000_FORMAT_16BIT) + count >>=1; + count--; + + /* FIXME: from second playback on, there's a lot more clicks and pops + * involved here than on first playback. Fiddling with + * tons of different settings didn't help (DMA, speaker on/off, + * reordering, ...). Something seems to get enabled on playback + * that I haven't found out how to disable again, which then causes + * the switching pops to reach the speakers the next time here. */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_als4000_set_rate(chip, runtime->rate); + snd_als4000_set_playback_dma(chip, runtime->dma_addr, size); + + /* SPEAKER_ON not needed, since dma_on seems to also enable speaker */ + /* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */ + snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd); + snd_sbdsp_command(chip, playback_cmd(chip).format); + snd_sbdsp_command(chip, count); + snd_sbdsp_command(chip, count>>8); + snd_sbdsp_command(chip, playback_cmd(chip).dma_off); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irqsave(&chip->mixer_lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_sbmixer_write(chip, 0xde, capture_cmd(chip)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + snd_sbmixer_write(chip, 0xde, 0); + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return result; +} + +static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->mode |= SB_RATE_LOCK_PLAYBACK; + snd_sbdsp_command(chip, playback_cmd(chip).dma_on); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_sbdsp_command(chip, playback_cmd(chip).dma_off); + chip->mode &= ~SB_RATE_LOCK_PLAYBACK; + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames( substream->runtime, result ); +} + +static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames( substream->runtime, result ); +} + +static void snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + unsigned long flags; + unsigned gcr_status; + unsigned sb_status; + + /* find out which bit of the ALS4000 produced the interrupt */ + gcr_status = inb(chip->alt_port + 0xe); + + if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */ + snd_pcm_period_elapsed(chip->playback_substream); + if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */ + snd_pcm_period_elapsed(chip->capture_substream); + if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */ + snd_mpu401_uart_interrupt(irq, chip->rmidi, regs); + /* release the gcr */ + outb(gcr_status, chip->alt_port + 0xe); + + spin_lock_irqsave(&chip->mixer_lock, flags); + sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + if (sb_status & SB_IRQTYPE_8BIT) + snd_sb_ack_8bit(chip); + if (sb_status & SB_IRQTYPE_16BIT) + snd_sb_ack_16bit(chip); + if (sb_status & SB_IRQTYPE_MPUIN) + inb(chip->mpu_port); + if (sb_status & 0x20) + inb(SBP(chip, RESET)); +} + +/*****************************************************************/ + +static snd_pcm_hardware_t snd_als4000_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */ + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0 +}; + +static snd_pcm_hardware_t snd_als4000_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */ + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0 +}; + +/*****************************************************************/ + +static int snd_als4000_playback_open(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback_substream = substream; + runtime->hw = snd_als4000_playback; + return 0; +} + +static int snd_als4000_playback_close(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_als4000_capture_open(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_substream = substream; + runtime->hw = snd_als4000_capture; + return 0; +} + +static int snd_als4000_capture_close(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +/******************************************************************/ + +static snd_pcm_ops_t snd_als4000_playback_ops = { + .open = snd_als4000_playback_open, + .close = snd_als4000_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_als4000_hw_params, + .hw_free = snd_als4000_hw_free, + .prepare = snd_als4000_playback_prepare, + .trigger = snd_als4000_playback_trigger, + .pointer = snd_als4000_playback_pointer +}; + +static snd_pcm_ops_t snd_als4000_capture_ops = { + .open = snd_als4000_capture_open, + .close = snd_als4000_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_als4000_hw_params, + .hw_free = snd_als4000_hw_free, + .prepare = snd_als4000_capture_prepare, + .trigger = snd_als4000_capture_trigger, + .pointer = snd_als4000_capture_pointer +}; + +static void snd_als4000_pcm_free(snd_pcm_t *pcm) +{ + sb_t *chip = snd_magic_cast(sb_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_als4000_pcm(sb_t *chip, int device) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0) + return err; + pcm->private_free = snd_als4000_pcm_free; + pcm->private_data = chip; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops); + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + chip->pcm = pcm; + + return 0; +} + +/******************************************************************/ + +static void __devinit snd_als4000_set_addr(unsigned long gcr, + unsigned int sb, + unsigned int mpu, + unsigned int opl, + unsigned int game) +{ + u32 confA = 0; + u32 confB = 0; + + if (mpu > 0) + confB |= (mpu | 1) << 16; + if (sb > 0) + confB |= (sb | 1); + if (game > 0) + confA |= (game | 1) << 16; + if (opl > 0) + confA |= (opl | 1); + snd_als4000_gcr_write_addr(gcr, 0xa8, confA); + snd_als4000_gcr_write_addr(gcr, 0xa9, confB); +} + +static void __devinit snd_als4000_configure(sb_t *chip) +{ + unsigned long flags; + unsigned tmp; + int i; + + /* do some more configuration */ + spin_lock_irqsave(&chip->mixer_lock, flags); + tmp = snd_sbmixer_read(chip, 0xc0); + snd_sbmixer_write(chip, 0xc0, tmp|0x80); + /* always select DMA channel 0, since we do not actually use DMA */ + snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0); + snd_sbmixer_write(chip, 0xc0, tmp&0x7f); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + spin_lock_irqsave(&chip->reg_lock,flags); + /* magic number. Enables interrupts(?) */ + snd_als4000_gcr_write(chip, 0x8c, 0x28000); + for(i = 0x91; i <= 0x96; ++i) + snd_als4000_gcr_write(chip, i, 0); + + snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99)); + spin_unlock_irqrestore(&chip->reg_lock,flags); +} + +static void snd_card_als4000_free( snd_card_t *card ) +{ + snd_card_als4000_t * acard = (snd_card_als4000_t *)card->private_data; + /* make sure that interrupts are disabled */ + snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0); +} + +static int __devinit snd_card_als4000_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + snd_card_als4000_t *acard; + unsigned long gcr; + struct resource *res_gcr_port; + sb_t *chip; + opl3_t *opl3; + unsigned short word; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) { + return err; + } + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + gcr = pci_resource_start(pci, 0); + if ((res_gcr_port = request_region(gcr, 0x40, "ALS4000")) == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", gcr, gcr + 0x40 - 1); + return -EBUSY; + } + + pci_read_config_word(pci, PCI_COMMAND, &word); + pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO); + pci_set_master(pci); + + /* disable all legacy ISA stuff except for joystick */ + snd_als4000_set_addr(gcr, 0, 0, 0, joystick_port[dev]); + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof( snd_card_als4000_t ) ); + if (card == NULL) { + release_resource(res_gcr_port); + kfree_nocheck(res_gcr_port); + return -ENOMEM; + } + + acard = (snd_card_als4000_t *)card->private_data; + acard->gcr = gcr; + card->private_free = snd_card_als4000_free; + + if ((err = snd_sbdsp_create(card, + gcr + 0x10, + pci->irq, + snd_als4000_interrupt, + -1, + -1, + SB_HW_ALS4000, + &chip)) < 0) { + release_resource(res_gcr_port); + kfree_nocheck(res_gcr_port); + snd_card_free(card); + return err; + } + + chip->pci = pci; + chip->alt_port = gcr; + chip->res_alt_port = res_gcr_port; + + snd_als4000_configure(chip); + + if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000, + gcr+0x30, 1, pci->irq, 0, + &chip->rmidi)) < 0) { + snd_card_free(card); + printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30); + return err; + } + + if ((err = snd_als4000_pcm(chip, 0)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, gcr+0x10, gcr+0x12, + OPL3_HW_AUTO, 1, &opl3) < 0) { + printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n", + gcr+0x10, gcr+0x12 ); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + strcpy(card->driver, "ALS4000"); + strcpy(card->shortname, "Avance Logic ALS4000"); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->alt_port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_als4000_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ALS4000", + .id_table = snd_als4000_ids, + .probe = snd_card_als4000_probe, + .remove = __devexit_p(snd_card_als4000_remove), +}; + +static int __init alsa_card_als4000_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "no ALS4000 based soundcards found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_als4000_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_als4000_init) +module_exit(alsa_card_als4000_exit) + +#ifndef MODULE + +/* format is: snd-als4000=enable,index,id */ + +static int __init alsa_card_als4000_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-als4000=", alsa_card_als4000_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cmipci.c linux/sound/pci/cmipci.c --- linux-2.4.21-rc1.orig/sound/pci/cmipci.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cmipci.c 2003-01-31 08:20:02.000000000 -0700 @@ -0,0 +1,3225 @@ +/* + * Driver for C-Media CMI8338 and 8738 PCI soundcards. + * Copyright (c) 2000 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Does not work. Warning may block system in capture mode */ +/* #define USE_VAR48KRATE */ + +/* Define this if you want soft ac3 encoding */ +#define DO_SOFT_AC3 +#define USE_AES_IEC958 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("C-Media CMI8x38 PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{C-Media,CMI8738}," + "{C-Media,CMI8738B}," + "{C-Media,CMI8338A}," + "{C-Media,CMI8338B}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ +static long mpu_port[SNDRV_CARDS] = {0x330, [1 ... (SNDRV_CARDS-1)]=-1}; +static long fm_port[SNDRV_CARDS] = {0x388, [1 ... (SNDRV_CARDS-1)]=-1}; +#ifdef DO_SOFT_AC3 +static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1}; +#endif + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{-1},{0x330},{0x320},{0x310},{0x300}},dialog:list"); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM port."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list"); +#ifdef DO_SOFT_AC3 +MODULE_PARM(soft_ac3, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only)."); +MODULE_PARM_SYNTAX(soft_ac3, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +#endif + +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif + +/* + * CM8x38 registers definition + */ + +#define CM_REG_FUNCTRL0 0x00 +#define CM_RST_CH1 0x00080000 +#define CM_RST_CH0 0x00040000 +#define CM_CHEN1 0x00020000 /* ch1: enable */ +#define CM_CHEN0 0x00010000 /* ch0: enable */ +#define CM_PAUSE1 0x00000008 /* ch1: pause */ +#define CM_PAUSE0 0x00000004 /* ch0: pause */ +#define CM_CHADC1 0x00000002 /* ch1, 0:playback, 1:record */ +#define CM_CHADC0 0x00000001 /* ch0, 0:playback, 1:record */ + +#define CM_REG_FUNCTRL1 0x04 +#define CM_ASFC_MASK 0x0000E000 /* ADC sampling frequency */ +#define CM_ASFC_SHIFT 13 +#define CM_DSFC_MASK 0x00001C00 /* DAC sampling frequency */ +#define CM_DSFC_SHIFT 10 +#define CM_SPDF_1 0x00000200 /* SPDIF IN/OUT at channel B */ +#define CM_SPDF_0 0x00000100 /* SPDIF OUT only channel A */ +#define CM_SPDFLOOP 0x00000080 /* ext. SPDIIF/OUT -> IN loopback */ +#define CM_SPDO2DAC 0x00000040 /* SPDIF/OUT can be heard from internal DAC */ +#define CM_INTRM 0x00000020 /* master control block (MCB) interrupt enabled */ +#define CM_BREQ 0x00000010 /* bus master enabled */ +#define CM_VOICE_EN 0x00000008 /* legacy voice (SB16,FM) */ +#define CM_UART_EN 0x00000004 /* UART */ +#define CM_JYSTK_EN 0x00000002 /* joy stick */ + +#define CM_REG_CHFORMAT 0x08 + +#define CM_CHB3D5C 0x80000000 /* 5,6 channels */ +#define CM_CHB3D 0x20000000 /* 4 channels */ + +#define CM_CHIP_MASK1 0x1f000000 +#define CM_CHIP_037 0x01000000 + +#define CM_SPDIF_SELECT1 0x00080000 /* for model <= 037 ? */ +#define CM_AC3EN1 0x00100000 /* enable AC3: model 037 */ +#define CM_SPD24SEL 0x00020000 /* 24bit spdif: model 037 */ +/* #define CM_SPDIF_INVERSE 0x00010000 */ /* ??? */ + +#define CM_ADCBITLEN_MASK 0x0000C000 +#define CM_ADCBITLEN_16 0x00000000 +#define CM_ADCBITLEN_15 0x00004000 +#define CM_ADCBITLEN_14 0x00008000 +#define CM_ADCBITLEN_13 0x0000C000 + +#define CM_ADCDACLEN_MASK 0x00003000 +#define CM_ADCDACLEN_060 0x00000000 +#define CM_ADCDACLEN_066 0x00001000 +#define CM_ADCDACLEN_130 0x00002000 +#define CM_ADCDACLEN_280 0x00003000 + +#define CM_CH1_SRATE_176K 0x00000800 +#define CM_CH1_SRATE_88K 0x00000400 +#define CM_CH0_SRATE_176K 0x00000200 +#define CM_CH0_SRATE_88K 0x00000100 + +#define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */ + +#define CM_CH1FMT_MASK 0x0000000C +#define CM_CH1FMT_SHIFT 2 +#define CM_CH0FMT_MASK 0x00000003 +#define CM_CH0FMT_SHIFT 0 + +#define CM_REG_INT_HLDCLR 0x0C +#define CM_CHIP_MASK2 0xff000000 +#define CM_CHIP_039 0x04000000 +#define CM_CHIP_039_6CH 0x01000000 +#define CM_TDMA_INT_EN 0x00040000 +#define CM_CH1_INT_EN 0x00020000 +#define CM_CH0_INT_EN 0x00010000 +#define CM_INT_HOLD 0x00000002 +#define CM_INT_CLEAR 0x00000001 + +#define CM_REG_INT_STATUS 0x10 +#define CM_INTR 0x80000000 +#define CM_VCO 0x08000000 /* Voice Control? CMI8738 */ +#define CM_MCBINT 0x04000000 /* Master Control Block abort cond.? */ +#define CM_UARTINT 0x00010000 +#define CM_LTDMAINT 0x00008000 +#define CM_HTDMAINT 0x00004000 +#define CM_XDO46 0x00000080 /* Modell 033? Direct programming EEPROM (read data register) */ +#define CM_LHBTOG 0x00000040 /* High/Low status from DMA ctrl register */ +#define CM_LEG_HDMA 0x00000020 /* Legacy is in High DMA channel */ +#define CM_LEG_STEREO 0x00000010 /* Legacy is in Stereo mode */ +#define CM_CH1BUSY 0x00000008 +#define CM_CH0BUSY 0x00000004 +#define CM_CHINT1 0x00000002 +#define CM_CHINT0 0x00000001 + +#define CM_REG_LEGACY_CTRL 0x14 +#define CM_NXCHG 0x80000000 /* h/w multi channels? */ +#define CM_VMPU_MASK 0x60000000 /* MPU401 i/o port address */ +#define CM_VMPU_330 0x00000000 +#define CM_VMPU_320 0x20000000 +#define CM_VMPU_310 0x40000000 +#define CM_VMPU_300 0x60000000 +#define CM_VSBSEL_MASK 0x0C000000 /* SB16 base address */ +#define CM_VSBSEL_220 0x00000000 +#define CM_VSBSEL_240 0x04000000 +#define CM_VSBSEL_260 0x08000000 +#define CM_VSBSEL_280 0x0C000000 +#define CM_FMSEL_MASK 0x03000000 /* FM OPL3 base address */ +#define CM_FMSEL_388 0x00000000 +#define CM_FMSEL_3C8 0x01000000 +#define CM_FMSEL_3E0 0x02000000 +#define CM_FMSEL_3E8 0x03000000 +#define CM_ENSPDOUT 0x00800000 /* enable XPDIF/OUT to I/O interface */ +#define CM_SPDCOPYRHT 0x00400000 /* set copyright spdif in/out */ +#define CM_DAC2SPDO 0x00200000 /* enable wave+fm_midi -> SPDIF/OUT */ +#define CM_SETRETRY 0x00010000 /* 0: legacy i/o wait (default), 1: legacy i/o bus retry */ +#define CM_CHB3D6C 0x00008000 /* 5.1 channels support */ +#define CM_LINE_AS_BASS 0x00006000 /* use line-in as bass */ + +#define CM_REG_MISC_CTRL 0x18 +#define CM_PWD 0x80000000 +#define CM_RESET 0x40000000 +#define CM_SFIL_MASK 0x30000000 +#define CM_TXVX 0x08000000 +#define CM_N4SPK3D 0x04000000 /* 4ch output */ +#define CM_SPDO5V 0x02000000 /* 5V spdif output (1 = 0.5v (coax)) */ +#define CM_SPDIF48K 0x01000000 /* write */ +#define CM_SPATUS48K 0x01000000 /* read */ +#define CM_ENDBDAC 0x00800000 /* enable dual dac */ +#define CM_XCHGDAC 0x00400000 /* 0: front=ch0, 1: front=ch1 */ +#define CM_SPD32SEL 0x00200000 /* 0: 16bit SPDIF, 1: 32bit */ +#define CM_SPDFLOOPI 0x00100000 /* int. SPDIF-IN -> int. OUT */ +#define CM_FM_EN 0x00080000 /* enalbe FM */ +#define CM_AC3EN2 0x00040000 /* enable AC3: model 039 */ +#define CM_VIDWPDSB 0x00010000 +#define CM_SPDF_AC97 0x00008000 /* 0: SPDIF/OUT 44.1K, 1: 48K */ +#define CM_MASK_EN 0x00004000 +#define CM_VIDWPPRT 0x00002000 +#define CM_SFILENB 0x00001000 +#define CM_MMODE_MASK 0x00000E00 +#define CM_SPDIF_SELECT2 0x00000100 /* for model > 039 ? */ +#define CM_ENCENTER 0x00000080 +#define CM_FLINKON 0x00000040 +#define CM_FLINKOFF 0x00000020 +#define CM_MIDSMP 0x00000010 +#define CM_UPDDMA_MASK 0x0000000C +#define CM_TWAIT_MASK 0x00000003 + + /* byte */ +#define CM_REG_MIXER0 0x20 + +#define CM_REG_SB16_DATA 0x22 +#define CM_REG_SB16_ADDR 0x23 + +#define CM_REFFREQ_XIN (315*1000*1000)/22 /* 14.31818 Mhz reference clock frequency pin XIN */ +#define CM_ADCMULT_XIN 512 /* Guessed (487 best for 44.1kHz, not for 88/176kHz) */ +#define CM_TOLERANCE_RATE 0.001 /* Tolerance sample rate pitch (1000ppm) */ +#define CM_MAXIMUM_RATE 80000000 /* Note more than 80MHz */ + +#define CM_REG_MIXER1 0x24 +#define CM_FMMUTE 0x80 /* mute FM */ +#define CM_FMMUTE_SHIFT 7 +#define CM_WSMUTE 0x40 /* mute PCM */ +#define CM_WSMUTE_SHIFT 6 +#define CM_SPK4 0x20 /* lin-in -> rear line out */ +#define CM_SPK4_SHIFT 5 +#define CM_REAR2FRONT 0x10 /* exchange rear/front */ +#define CM_REAR2FRONT_SHIFT 4 +#define CM_WAVEINL 0x08 /* digital wave rec. left chan */ +#define CM_WAVEINL_SHIFT 3 +#define CM_WAVEINR 0x04 /* digical wave rec. right */ +#define CM_WAVEINR_SHIFT 2 +#define CM_X3DEN 0x02 /* 3D surround enable */ +#define CM_X3DEN_SHIFT 1 +#define CM_CDPLAY 0x01 /* enable SPDIF/IN PCM -> DAC */ +#define CM_CDPLAY_SHIFT 0 + +#define CM_REG_MIXER2 0x25 +#define CM_RAUXREN 0x80 /* AUX right capture */ +#define CM_RAUXREN_SHIFT 7 +#define CM_RAUXLEN 0x40 /* AUX left capture */ +#define CM_RAUXLEN_SHIFT 6 +#define CM_VAUXRM 0x20 /* AUX right mute */ +#define CM_VAUXRM_SHIFT 5 +#define CM_VAUXLM 0x10 /* AUX left mute */ +#define CM_VAUXLM_SHIFT 4 +#define CM_VADMIC_MASK 0x0e /* mic gain level (0-3) << 1 */ +#define CM_VADMIC_SHIFT 1 +#define CM_MICGAINZ 0x01 /* mic boost */ +#define CM_MICGAINZ_SHIFT 0 + +#define CM_REG_AUX_VOL 0x26 +#define CM_VAUXL_MASK 0xf0 +#define CM_VAUXR_MASK 0x0f + +#define CM_REG_MISC 0x27 +#define CM_XGPO1 0x20 +// #define CM_XGPBIO 0x04 +#define CM_MIC_CENTER_LFE 0x04 /* mic as center/lfe out? (model 039 or later?) */ +#define CM_SPDIF_INVERSE 0x04 /* spdif input phase inverse (model 037) */ +#define CM_SPDVALID 0x02 /* spdif input valid check */ +#define CM_DMAUTO 0x01 + +#define CM_REG_AC97 0x28 /* hmmm.. do we have ac97 link? */ +/* + * For CMI-8338 (0x28 - 0x2b) .. is this valid for CMI-8738 + * or identical with AC97 codec? + */ +#define CM_REG_EXTERN_CODEC CM_REG_AC97 + +/* + * MPU401 pci port index address 0x40 - 0x4f (CMI-8738 spec ver. 0.6) + */ +#define CM_REG_MPU_PCI 0x40 + +/* + * FM pci port index address 0x50 - 0x5f (CMI-8738 spec ver. 0.6) + */ +#define CM_REG_FM_PCI 0x50 + +/* + * for CMI-8338 .. this is not valid for CMI-8738. + */ +#define CM_REG_EXTENT_IND 0xf0 +#define CM_VPHONE_MASK 0xe0 /* Phone volume control (0-3) << 5 */ +#define CM_VPHONE_SHIFT 5 +#define CM_VPHOM 0x10 /* Phone mute control */ +#define CM_VSPKM 0x08 /* Speaker mute control, default high */ +#define CM_RLOOPREN 0x04 /* Rec. R-channel enable */ +#define CM_RLOOPLEN 0x02 /* Rec. L-channel enable */ + +/* + * CMI-8338 spec ver 0.5 (this is not valid for CMI-8738): + * the 8 registers 0xf8 - 0xff are used for programming m/n counter by the PLL + * unit (readonly?). + */ +#define CM_REG_PLL 0xf8 + +/* + * extended registers + */ +#define CM_REG_CH0_FRAME1 0x80 /* base address */ +#define CM_REG_CH0_FRAME2 0x84 +#define CM_REG_CH1_FRAME1 0x88 /* 0-15: count of samples at bus master; buffer size */ +#define CM_REG_CH1_FRAME2 0x8C /* 16-31: count of samples at codec; fragment size */ + +/* + * size of i/o region + */ +#define CM_EXTENT_CODEC 0x100 +#define CM_EXTENT_MIDI 0x2 +#define CM_EXTENT_SYNTH 0x4 + +/* + * pci ids + */ +#ifndef PCI_VENDOR_ID_CMEDIA +#define PCI_VENDOR_ID_CMEDIA 0x13F6 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif + +/* + * channels for playback / capture + */ +#define CM_CH_PLAY 0 +#define CM_CH_CAPT 1 + +/* + * flags to check device open/close + */ +#define CM_OPEN_NONE 0 +#define CM_OPEN_CH_MASK 0x01 +#define CM_OPEN_DAC 0x10 +#define CM_OPEN_ADC 0x20 +#define CM_OPEN_SPDIF 0x40 +#define CM_OPEN_MCHAN 0x80 +#define CM_OPEN_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC) +#define CM_OPEN_PLAYBACK2 (CM_CH_CAPT | CM_OPEN_DAC) +#define CM_OPEN_PLAYBACK_MULTI (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_MCHAN) +#define CM_OPEN_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC) +#define CM_OPEN_SPDIF_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF) +#define CM_OPEN_SPDIF_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF) + + +#if CM_CH_PLAY == 1 +#define CM_PLAYBACK_SRATE_176K CM_CH1_SRATE_176K +#define CM_PLAYBACK_SPDF CM_SPDF_1 +#define CM_CAPTURE_SPDF CM_SPDF_0 +#else +#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K +#define CM_PLAYBACK_SPDF CM_SPDF_0 +#define CM_CAPTURE_SPDF CM_SPDF_1 +#endif + + +/* + * driver data + */ + +typedef struct snd_stru_cmipci cmipci_t; +typedef struct snd_stru_cmipci_pcm cmipci_pcm_t; + +#define chip_t cmipci_t + +struct snd_stru_cmipci_pcm { + snd_pcm_substream_t *substream; + int running; /* dac/adc running? */ + unsigned int dma_size; /* in frames */ + unsigned int period_size; /* in frames */ + unsigned int offset; /* physical address of the buffer */ + unsigned int fmt; /* format bits */ + int ch; /* channel (0/1) */ + unsigned int is_dac; /* is dac? */ + int bytes_per_frame; + int shift; + int ac3_shift; /* extra shift: 1 on soft ac3 mode */ +}; + +/* mixer elements toggled/resumed during ac3 playback */ +struct cmipci_mixer_auto_switches { + const char *name; /* switch to toggle */ + int toggle_on; /* value to change when ac3 mode */ +}; +static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = { + {"PCM Playback Switch", 0}, + {"IEC958 Output Switch", 1}, + {"IEC958 Mix Analog", 0}, + // {"IEC958 Out To DAC", 1}, // no longer used + {"IEC958 Loop", 0}, +}; +#define CM_SAVED_MIXERS ARRAY_SIZE(cm_saved_mixer) + +struct snd_stru_cmipci { + snd_card_t *card; + + struct pci_dev *pci; + unsigned int device; /* device ID */ + int irq; + + unsigned long iobase; + struct resource *res_iobase; + unsigned int ctrl; /* FUNCTRL0 current value */ + + snd_pcm_t *pcm; /* DAC/ADC PCM */ + snd_pcm_t *pcm2; /* 2nd DAC */ + snd_pcm_t *pcm_spdif; /* SPDIF */ + + int chip_version; + int max_channels; + unsigned int has_dual_dac: 1; + unsigned int can_ac3_sw: 1; + unsigned int can_ac3_hw: 1; + unsigned int can_multi_ch: 1; + unsigned int do_soft_ac3: 1; + + unsigned int spdif_playback_avail: 1; /* spdif ready? */ + unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */ + int spdif_counter; /* for software AC3 */ + + unsigned int dig_status; + unsigned int dig_pcm_status; +#ifdef USE_AES_IEC958 + snd_ctl_elem_value_t *spdif_channel; +#endif + snd_kcontrol_t *spdif_pcm_ctl; + + snd_pcm_hardware_t *hw_info[3]; /* for playbacks */ + + int opened[2]; /* open mode */ + struct semaphore open_mutex; + + int mixer_insensitive: 1; + snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS]; + int mixer_res_status[CM_SAVED_MIXERS]; + + opl3_t *opl3; + snd_hwdep_t *opl3hwdep; + + cmipci_pcm_t channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */ + + /* external MIDI */ + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; +}; + + +/* read/write operations for dword register */ +inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data) +{ + outl(data, cm->iobase + cmd); +} +inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd) +{ + return inl(cm->iobase + cmd); +} + +/* read/write operations for word register */ +inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data) +{ + outw(data, cm->iobase + cmd); +} +inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd) +{ + return inw(cm->iobase + cmd); +} + +/* read/write operations for byte register */ +inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data) +{ + outb(data, cm->iobase + cmd); +} + +inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd) +{ + return inb(cm->iobase + cmd); +} + +/* bit operations for dword register */ +static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +{ + unsigned int val; + val = inl(cm->iobase + cmd); + val |= flag; + outl(val, cm->iobase + cmd); +} + +static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +{ + unsigned int val; + val = inl(cm->iobase + cmd); + val &= ~flag; + outl(val, cm->iobase + cmd); +} + +#if 0 // not used +/* bit operations for byte register */ +static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +{ + unsigned char val; + val = inb(cm->iobase + cmd); + val |= flag; + outb(val, cm->iobase + cmd); +} + +static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +{ + unsigned char val; + val = inb(cm->iobase + cmd); + val &= ~flag; + outb(val, cm->iobase + cmd); +} +#endif + + +/* + * PCM interface + */ + +/* + * calculate frequency + */ + +static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 }; + +static unsigned int snd_cmipci_rate_freq(unsigned int rate) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (rates[i] == rate) + return i; + } + snd_BUG(); + return 0; +} + +#ifdef USE_VAR48KRATE +/* + * Determine PLL values for frequency setup, maybe the CMI8338 (CMI8738???) + * does it this way .. maybe not. Never get any information from C-Media about + * that . + */ +static int snd_cmipci_pll_rmn(unsigned int rate, unsigned int adcmult, int *r, int *m, int *n) +{ + unsigned int delta, tolerance; + int xm, xn, xr; + + for (*r = 0; rate < CM_MAXIMUM_RATE/adcmult; *r += (1<<5)) + rate <<= 1; + *n = -1; + if (*r > 0xff) + goto out; + tolerance = rate*CM_TOLERANCE_RATE; + + for (xn = (1+2); xn < (0x1f+2); xn++) { + for (xm = (1+2); xm < (0xff+2); xm++) { + xr = ((CM_REFFREQ_XIN/adcmult) * xm) / xn; + + if (xr < rate) + delta = rate - xr; + else + delta = xr - rate; + + /* + * If we found one, remember this, + * and try to find a closer one + */ + if (delta < tolerance) { + tolerance = delta; + *m = xm - 2; + *n = xn - 2; + } + } + } +out: + return (*n > -1); +} + +/* + * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff + * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen + * at the register CM_REG_FUNCTRL1 (0x04). + * Problem: other ways are also possible (any information about that?) + */ +static void snd_cmipci_set_pll(cmipci_t *cm, unsigned int rate, unsigned int slot) +{ + unsigned int reg = CM_REG_PLL + slot; + /* + * Guess that this programs at reg. 0x04 the pos 15:13/12:10 + * for DSFC/ASFC (000 upto 111). + */ + + /* FIXME: Init (Do we've to set an other register first before programming?) */ + + /* FIXME: Is this correct? Or shouldn't the m/n/r values be used for that? */ + snd_cmipci_write_b(cm, reg, rate>>8); + snd_cmipci_write_b(cm, reg, rate&0xff); + + /* FIXME: Setup (Do we've to set an other register first to enable this?) */ +} +#endif /* USE_VAR48KRATE */ + +static int snd_cmipci_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_cmipci_playback2_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + if (params_channels(hw_params) > 2) { + down(&cm->open_mutex); + if (cm->opened[CM_CH_PLAY]) { + up(&cm->open_mutex); + return -EBUSY; + } + /* reserve the channel A */ + cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI; + up(&cm->open_mutex); + } + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static void snd_cmipci_ch_reset(cmipci_t *cm, int ch) +{ + int reset = CM_RST_CH0 << (cm->channel[ch].ch); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); + udelay(10); +} + +static int snd_cmipci_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + + +/* + */ + +static unsigned int hw_channels[] = {1, 2, 4, 5, 6}; +static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = { + .count = 3, + .list = hw_channels, + .mask = 0, +}; +static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = { + .count = 5, + .list = hw_channels, + .mask = 0, +}; + +static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels) +{ + unsigned long flags; + + if (channels > 2) { + if (! cm->can_multi_ch) + return -EINVAL; + if (rec->fmt != 0x03) /* stereo 16bit only */ + return -EINVAL; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); + if (channels > 4) { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + } + if (channels == 6) { + snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + } else { + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + + } else { + if (cm->can_multi_ch) { + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + } + return 0; +} + + +/* + * prepare playback/capture channel + * channel to be used must have been set in rec->ch. + */ +static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream) +{ + unsigned long flags; + unsigned int reg, freq, val; + snd_pcm_runtime_t *runtime = substream->runtime; + + rec->fmt = 0; + rec->shift = 0; + if (snd_pcm_format_width(runtime->format) >= 16) { + rec->fmt |= 0x02; + if (snd_pcm_format_width(runtime->format) > 16) + rec->shift++; /* 24/32bit */ + } + if (runtime->channels > 1) + rec->fmt |= 0x01; + if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) { + snd_printd("cannot set dac channels\n"); + return -EINVAL; + } + + rec->offset = runtime->dma_addr; + /* buffer and period sizes in frame */ + rec->dma_size = runtime->buffer_size << rec->shift; + rec->period_size = runtime->period_size << rec->shift; + rec->dma_size <<= rec->ac3_shift; + rec->period_size <<= rec->ac3_shift; + if (runtime->channels > 2) { + /* multi-channels */ + rec->dma_size = (rec->dma_size * runtime->channels) / 2; + rec->period_size = (rec->period_size * runtime->channels) / 2; + } + + spin_lock_irqsave(&cm->reg_lock, flags); + + /* set buffer address */ + reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; + snd_cmipci_write(cm, reg, rec->offset); + /* program sample counts */ + reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; + snd_cmipci_write_w(cm, reg, rec->dma_size - 1); + snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1); + + /* set adc/dac flag */ + val = rec->ch ? CM_CHADC1 : CM_CHADC0; + if (rec->is_dac) + cm->ctrl &= ~val; + else + cm->ctrl |= val; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); + + /* set sample rate */ + freq = snd_cmipci_rate_freq(runtime->rate); + val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); + if (rec->ch) { + val &= ~CM_ASFC_MASK; + val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK; + } else { + val &= ~CM_DSFC_MASK; + val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK; + } + snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); + //snd_printd("cmipci: functrl1 = %08x\n", val); + + /* set format */ + val = snd_cmipci_read(cm, CM_REG_CHFORMAT); + if (rec->ch) { + val &= ~CM_CH1FMT_MASK; + val |= rec->fmt << CM_CH1FMT_SHIFT; + } else { + val &= ~CM_CH0FMT_MASK; + val |= rec->fmt << CM_CH0FMT_SHIFT; + } + snd_cmipci_write(cm, CM_REG_CHFORMAT, val); + //snd_printd("cmipci: chformat = %08x\n", val); + + rec->running = 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return 0; +} + +/* + * PCM trigger/stop + */ +static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream, int cmd) +{ + unsigned int inthld, chen, reset, pause; + int result = 0; + + inthld = CM_CH0_INT_EN << rec->ch; + chen = CM_CHEN0 << rec->ch; + reset = CM_RST_CH0 << rec->ch; + pause = CM_PAUSE0 << rec->ch; + + spin_lock(&cm->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + rec->running = 1; + /* set interrupt */ + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld); + cm->ctrl |= chen; + /* enable channel */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_STOP: + rec->running = 0; + /* disable interrupt */ + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld); + /* reset */ + cm->ctrl &= ~chen; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + cm->ctrl |= pause; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cm->ctrl &= ~pause; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&cm->reg_lock); + return result; +} + +/* + * return the current pointer + */ +static snd_pcm_uframes_t snd_cmipci_pcm_pointer(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream) +{ + size_t ptr; + unsigned int reg; + if (!rec->running) + return 0; +#if 1 // this seems better.. + reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; + ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1); + ptr >>= rec->shift; +#else + reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; + ptr = snd_cmipci_read(cm, reg) - rec->offset; + ptr = bytes_to_frames(substream->runtime, ptr); +#endif + ptr >>= rec->ac3_shift; + if (substream->runtime->channels > 2) + ptr = (ptr * 2) / substream->runtime->channels; + return ptr; +} + +/* + * playback + */ + +static int snd_cmipci_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd); +} + +static snd_pcm_uframes_t snd_cmipci_playback_pointer(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream); +} + + + +/* + * capture + */ + +static int snd_cmipci_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd); +} + +static snd_pcm_uframes_t snd_cmipci_capture_pointer(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream); +} + +#ifdef DO_SOFT_AC3 +/* + * special tricks for soft ac3 transfer: + * + * we compose an iec958 subframe from 16bit ac3 sample and + * write the raw subframe via 32bit data mode. + */ + +# ifndef USE_AES_IEC958 + +/* find parity for bit 4~30 */ +static unsigned int parity(unsigned int data) +{ + unsigned int parity = 0; + int counter = 4; + + data >>= 4; /* start from bit 4 */ + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +/* + * compose 32bit iec958 subframe with non-audio data. + * bit 0-3 = preamble + * 4-7 = aux (=0) + * 8-27 = data (12-27 for 16bit) + * 28 = validity (=0) + * 29 = user data (=0) + * 30 = channel status + * 31 = parity + * + * channel status is assumed as consumer, non-audio + * thus all 0 except bit 1 + */ +inline static u32 convert_ac3_32bit(cmipci_t *cm, u32 val) +{ + u32 data = (u32)val << 12; + + if (cm->spdif_counter == 2 || cm->spdif_counter == 3) /* bit 1 */ + data |= 0x40000000; /* indicate AC-3 raw data */ + if (parity(data)) /* parity bit 4-30 */ + data |= 0x80000000; + if (cm->spdif_counter == 0) + data |= 3; /* preamble 'M' */ + else if (cm->spdif_counter & 1) + data |= 5; /* odd, 'W' */ + else + data |= 9; /* even, 'M' */ + + cm->spdif_counter++; + if (cm->spdif_counter == 384) + cm->spdif_counter = 0; + + return data; +} + +# else /* if USE_AES_IEC958 */ + +/* + * The bitstream handling + */ +typedef struct iec958_stru_bitstream { + u32 *data; /* Holds the current position */ + u32 left; /* Bits left in current 32bit frame */ + u32 word; /* The 32bit frame of the current position */ + u32 bits; /* All bits together */ + int err; /* Error condition */ +} iec958_bitstream_t ; + +static iec958_bitstream_t bs; + +/* Initialize ptr on the buffer */ +static void iec958_init_bitstream(u8 *buf, u32 size) +{ + bs.data = (u32 *)buf; /* Set initial position */ + bs.word = *bs.data; /* The first 32bit frame */ + bs.left = 32; /* has exactly 32bits */ + bs.bits = size; + bs.err = 0; +} + +/* Remove ptr on the buffer */ +static void iec958_clear_bitstream(void) +{ + bs.data = NULL; + bs.left = 0; + bs.err = 0; +} + +/* Get bits from bitstream (max 32) */ +static inline u32 iec958_getbits(u32 bits) +{ + u32 res; + + if (bs.bits < bits) { + bits = bs.bits; + bs.err = 1; + } + if (bits > 32) { + bits = 32; + bs.err = 1; + } + bs.bits -= bits; + +# ifdef WORDS_BIGENDIAN + if (bits < bs.left) { /* Within 32bit frame */ + res = (bs.word << (32 - bs.left)) >> (32 - bits); + bs.left -= bits; + goto out; + } /* We may cross the frame boundary */ + res = (bs.word << (32 - bs.left)) >> (32 - bs.left); + bits -= bs.left; + + bs.word = *(++bs.data); /* Next 32bit frame */ + + if (bits) /* Add remaining bits, if any */ + res = (res << bits) | (bs.word >> (32 - bits)); + +# else /* not WORDS_BIGENDIAN */ + + if (bits < bs.left) { /* Within 32bit frame */ + res = (bs.word << (32 - bits)) >> (32 - bits); + bs.word >>= bits; + bs.left -= bits; + goto out; + } /* We may cross the frame boundary */ + res = bs.word; + bits -= bs.left; + + bs.word = *(++bs.data); /* Next 32bit frame */ + + if (bits) { /* Add remaining bits, if any */ + res = res | (((bs.word << (32 - bits)) >> (32 - bits)) << bits); + bs.word >>= bits; + } +# endif /* not WORDS_BIGENDIAN */ + + bs.left = (32 - bits); +out: + return res; +} + +static inline u32 iec958_bits_avail(void) +{ + return bs.bits; +} + +static inline int iec958_error(void) +{ + return bs.err; +} + +/* + * Determine parity for time slots 4 upto 30 + * to be sure that bit 4 upt 31 will carry + * an even number of ones and zeros. + */ +static u32 iec958_parity(u32 data) +{ + u32 parity = 0; + int counter = 4; + + data >>= 4; /* start from bit 4 */ + while (counter++ <= 30) { + if (data & 0x00000001) + parity++; + data >>= 1; + } + return (parity & 0x00000001); +} + +/* + * Compose 32bit iec958 subframe, two sub frames + * build one frame with two channels. + * + * bit 0-3 = preamble + * 4-7 = AUX (=0) + * 8-27 = data (12-27 for 16bit, 8-27 for 20bit, and 24bit without AUX) + * 28 = validity (0 for valid data, else 'in error') + * 29 = user data (0) + * 30 = channel status (24 bytes for 192 frames) + * 31 = parity + */ + +static inline u32 iec958_subframe(cmipci_t *cm, snd_ctl_elem_value_t * ucontrol) +{ + u32 data; + u32 byte = cm->spdif_counter >> 4; + u32 mask = 1 << ((cm->spdif_counter >> 1) - (byte << 3)); + u8 * status = ucontrol->value.iec958.status; + + if (status[2] & IEC958_AES2_PRO_SBITS_24) { + /* Does this work for LE systems ??? */ + if (status[2] & IEC958_AES2_PRO_WORDLEN_24_20) { + data = iec958_getbits(24); + data <<= 4; + } else { + data = iec958_getbits(20); + data <<= 8; + } + } else { + if (status[2] & IEC958_AES2_PRO_WORDLEN_24_20) { + /* Does this work for LE systems ??? */ + data = iec958_getbits(20); + data <<= 8; + } else { + data = iec958_getbits(16); + data <<= 12; + } + } + + /* + * Set one of the 192 bits of the channel status (AES3 and higher) + */ + if (status[byte] & mask) + data |= 0x40000000; + + if (iec958_parity(data)) /* parity bit 4-30 */ + data |= 0x80000000; + + /* Preamble */ + if (!cm->spdif_counter) + data |= 0x03; /* Block start, 'Z' */ + else if (cm->spdif_counter % 2) + data |= 0x05; /* odd sub frame, 'Y' */ + else + data |= 0x09; /* even sub frame, 'X' */ + + /* + * sub frame counter: 2 sub frame are one audio frame + * and 192 frames are one block + */ + cm->spdif_counter = (++cm->spdif_counter) % 384; + + return data; +} +# endif /* if USE_AES_IEC958 */ + +static int snd_cmipci_ac3_copy(snd_pcm_substream_t *subs, int channel, + snd_pcm_uframes_t pos, void *src, + snd_pcm_uframes_t count) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + u32 *dst; + snd_pcm_uframes_t offset; + snd_pcm_runtime_t *runtime = subs->runtime; +#ifndef USE_AES_IEC958 + u16 *srcp = src, val; +#else + char buf[1920]; /* bits can be divided by 20, 24, 16 */ + size_t bytes = frames_to_bytes(runtime, count); +#endif + + + if (!cm->channel[CM_CH_PLAY].ac3_shift) { + if (copy_from_user(runtime->dma_area + + frames_to_bytes(runtime, pos), src, + frames_to_bytes(runtime, count))) + return -EFAULT; + return 0; + } + + if (! access_ok(VERIFY_READ, src, count)) + return -EFAULT; + + /* frame = 16bit stereo */ + offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2); + dst = (u32*)(runtime->dma_area + offset); +# ifndef USE_AES_IEC958 + count /= 2; + while (count-- > 0) { + get_user(val, srcp); + srcp++; + *dst++ = convert_ac3_32bit(cm, val); + } +# else + while (bytes) { + size_t c = bytes; + + if (c > sizeof(buf)) + c = sizeof(buf); + + if (copy_from_user(buf, src, c)) + return -EFAULT; + bytes -= c; + src += c; + + iec958_init_bitstream(buf, c*8); + while (iec958_bits_avail()) { + *(dst++) = iec958_subframe(cm, cm->spdif_channel); + if (iec958_error()) + return -EINVAL; + } + iec958_clear_bitstream(); + } +# endif + return 0; +} + +static int snd_cmipci_ac3_silence(snd_pcm_substream_t *subs, int channel, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + u32 *dst; + snd_pcm_uframes_t offset; + snd_pcm_runtime_t *runtime = subs->runtime; +# ifdef USE_AES_IEC958 + char buf[1920]; /* bits can be divided by 20, 24, 16 */ + size_t bytes = frames_to_bytes(runtime, count); +# endif + if (! cm->channel[CM_CH_PLAY].ac3_shift) + return snd_pcm_format_set_silence(runtime->format, + runtime->dma_area + frames_to_bytes(runtime, pos), count); + + /* frame = 16bit stereo */ + offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2); + dst = (u32*)(subs->runtime->dma_area + offset); +# ifndef USE_AES_IEC958 + count /= 2; + while (count-- > 0) { + *dst++ = convert_ac3_32bit(cm, 0); + } +# else + while (bytes) { + size_t c = bytes; + + if (c > sizeof(buf)) + c = sizeof(buf); + + /* Q: Does this function know about 24bit silence? */ + if (snd_pcm_format_set_silence(runtime->format, buf, bytes_to_frames(runtime, c))) + return -EINVAL; + + iec958_init_bitstream(buf, c*8); + while (iec958_bits_avail()) { + *(dst++) = iec958_subframe(cm, cm->spdif_channel); + if (iec958_error()) + return -EINVAL; + } + iec958_clear_bitstream(); + } +# endif + return 0; +} +#endif /* DO_SOFT_AC3 */ + + +/* + * hw preparation for spdif + */ + +static int snd_cmipci_spdif_default_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_default_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cmipci_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i, change; + unsigned int val; + + val = 0; + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->dig_status; + chip->dig_status = val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_cmipci_spdif_default_info, + .get = snd_cmipci_spdif_default_get, + .put = snd_cmipci_spdif_default_put +}; + +static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_cmipci_spdif_mask_info, + .get = snd_cmipci_spdif_mask_get, +}; + +static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_stream_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff; +#ifdef USE_AES_IEC958 + ucontrol = chip->spdif_channel; +#endif + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cmipci_spdif_stream_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i, change; + unsigned int val; + + val = 0; + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->dig_pcm_status; + chip->dig_pcm_status = val; +#ifdef USE_AES_IEC958 + chip->spdif_channel = ucontrol; +#endif + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_cmipci_spdif_stream_info, + .get = snd_cmipci_spdif_stream_get, + .put = snd_cmipci_spdif_stream_put +}; + +/* + */ + +/* save mixer setting and mute for AC3 playback */ +static void save_mixer_state(cmipci_t *cm) +{ + if (! cm->mixer_insensitive) { + unsigned int i; + for (i = 0; i < CM_SAVED_MIXERS; i++) { + snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; + if (ctl) { + snd_ctl_elem_value_t val; + int event; + memset(&val, 0, sizeof(val)); + ctl->get(ctl, &val); + cm->mixer_res_status[i] = val.value.integer.value[0]; + val.value.integer.value[0] = cm_saved_mixer[i].toggle_on; + event = SNDRV_CTL_EVENT_MASK_INFO; + if (cm->mixer_res_status[i] != val.value.integer.value[0]) { + ctl->put(ctl, &val); /* toggle */ + event |= SNDRV_CTL_EVENT_MASK_VALUE; + } + ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(cm->card, event, &ctl->id); + } + } + cm->mixer_insensitive = 1; + } +} + + +/* restore the previously saved mixer status */ +static void restore_mixer_state(cmipci_t *cm) +{ + if (cm->mixer_insensitive) { + unsigned int i; + cm->mixer_insensitive = 0; /* at first clear this; + otherwise the changes will be ignored */ + for (i = 0; i < CM_SAVED_MIXERS; i++) { + snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; + if (ctl) { + snd_ctl_elem_value_t val; + int event; + + memset(&val, 0, sizeof(val)); + ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->get(ctl, &val); + event = SNDRV_CTL_EVENT_MASK_INFO; + if (val.value.integer.value[0] != cm->mixer_res_status[i]) { + val.value.integer.value[0] = cm->mixer_res_status[i]; + ctl->put(ctl, &val); + event |= SNDRV_CTL_EVENT_MASK_VALUE; + } + snd_ctl_notify(cm->card, event, &ctl->id); + } + } + } +} + +/* spinlock held! */ +static void setup_ac3(cmipci_t *cm, snd_pcm_substream_t *subs, int do_ac3, int rate) +{ + cm->channel[CM_CH_PLAY].ac3_shift = 0; + cm->spdif_counter = 0; + + if (do_ac3) { + /* AC3EN for 037 */ + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); + /* AC3EN for 039 */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); + + if (cm->can_ac3_hw) { + /* SPD24SEL for 037, 0x02 */ + /* SPD24SEL for 039, 0x20, but cannot be set */ + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + } else { /* can_ac3_sw */ +#ifdef DO_SOFT_AC3 + /* FIXME: ugly hack! */ + subs->runtime->buffer_size /= 2; + /* SPD32SEL for 037 & 039, 0x20 */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + /* set 176K sample rate to fix 033 HW bug */ + if (cm->chip_version == 33) { + if (rate >= 48000) { + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); + } + } + cm->channel[CM_CH_PLAY].ac3_shift = 1; /* use 32bit */ +#endif /* DO_SOFT_AC3 */ + } + + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); + + if (cm->can_ac3_hw) { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + } else { +#ifdef DO_SOFT_AC3 + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); +#endif /* DO_SOFT_AC3 */ + } + } +} + +static void setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3) +{ + int rate; + unsigned long flags; + + rate = subs->runtime->rate; + + if (up && do_ac3) + save_mixer_state(cm); + + spin_lock_irqsave(&cm->reg_lock, flags); + cm->spdif_playback_avail = up; + if (up) { + /* they are controlled via "IEC958 Output Switch" */ + /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ + /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ + if (cm->spdif_playback_enabled) + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + setup_ac3(cm, subs, do_ac3, rate); + + if (rate == 48000) + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); + else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); + + } else { + /* they are controlled via "IEC958 Output Switch" */ + /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ + /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ + snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + setup_ac3(cm, subs, 0, 0); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); +} + + +/* + * preparation + */ + +/* playback - enable spdif only on the certain condition */ +static int snd_cmipci_playback_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + int rate = substream->runtime->rate; + int do_spdif, do_ac3; + do_spdif = ((rate == 44100 || rate == 48000) && + substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && + substream->runtime->channels == 2); + do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; +#ifdef DO_SOFT_AC3 + if (do_ac3 && cm->can_ac3_sw) + do_spdif = 0; +#endif + setup_spdif_playback(cm, substream, do_spdif, do_ac3); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); +} + +/* playback (via device #2) - enable spdif always */ +static int snd_cmipci_playback_spdif_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + setup_spdif_playback(cm, substream, 1, cm->dig_pcm_status & IEC958_AES0_NONAUDIO); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); +} + +static int snd_cmipci_playback_hw_free(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + setup_spdif_playback(cm, substream, 0, 0); + restore_mixer_state(cm); + return snd_cmipci_hw_free(substream); +} + +/* capture */ +static int snd_cmipci_capture_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); +} + +/* capture with spdif (via device #2) */ +static int snd_cmipci_capture_spdif_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); +} + +static int snd_cmipci_capture_spdif_hw_free(snd_pcm_substream_t *subs) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + unsigned long flags; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return snd_cmipci_hw_free(subs); +} + + +/* + * interrupt handler + */ +static void snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, dev_id, return); + unsigned int status, mask = 0; + + /* fastpath out, to ease interrupt sharing */ + status = snd_cmipci_read(cm, CM_REG_INT_STATUS); + if (!(status & CM_INTR)) + return; + + /* acknowledge interrupt */ + spin_lock(&cm->reg_lock); + if (status & CM_CHINT0) + mask |= CM_CH0_INT_EN; + if (status & CM_CHINT1) + mask |= CM_CH1_INT_EN; + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask); + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask); + spin_unlock(&cm->reg_lock); + + if (cm->rmidi && (status & CM_UARTINT)) + snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs); + + if (cm->pcm) { + if ((status & CM_CHINT0) && cm->channel[0].running) + snd_pcm_period_elapsed(cm->channel[0].substream); + if ((status & CM_CHINT1) && cm->channel[1].running) + snd_pcm_period_elapsed(cm->channel[1].substream); + } +} + +/* + * h/w infos + */ + +/* playback on channel A */ +static snd_pcm_hardware_t snd_cmipci_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5512, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* capture on channel B */ +static snd_pcm_hardware_t snd_cmipci_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5512, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* playback on channel B - stereo 16bit only? */ +static snd_pcm_hardware_t snd_cmipci_playback2 = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5512, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* spdif playback on channel A */ +static snd_pcm_hardware_t snd_cmipci_playback_spdif = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* spdif capture on channel B */ +static snd_pcm_hardware_t snd_cmipci_capture_spdif = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * check device open/close + */ +static int open_device_check(cmipci_t *cm, int mode, snd_pcm_substream_t *subs) +{ + unsigned long flags; + int ch = mode & CM_OPEN_CH_MASK; + + /* FIXME: a file should wait until the device becomes free + * when it's opened on blocking mode. however, since the current + * pcm framework doesn't pass file pointer before actually opened, + * we can't know whether blocking mode or not in open callback.. + */ + down(&cm->open_mutex); + if (cm->opened[ch]) { + up(&cm->open_mutex); + return -EBUSY; + } + cm->opened[ch] = mode; + cm->channel[ch].substream = subs; + if (! (mode & CM_OPEN_DAC)) { + /* disable dual DAC mode */ + cm->channel[ch].is_dac = 0; + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + up(&cm->open_mutex); + return 0; +} + +static void close_device_check(cmipci_t *cm, int mode) +{ + unsigned long flags; + int ch = mode & CM_OPEN_CH_MASK; + + down(&cm->open_mutex); + if (cm->opened[ch] == mode) { + if (cm->channel[ch].substream) { + snd_cmipci_ch_reset(cm, ch); + cm->channel[ch].running = 0; + cm->channel[ch].substream = NULL; + } + cm->opened[ch] = 0; + if (! cm->channel[ch].is_dac) { + /* enable dual DAC mode again */ + cm->channel[ch].is_dac = 1; + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + } + up(&cm->open_mutex); +} + +/* + */ + +static int snd_cmipci_playback_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0) + return err; + runtime->hw = snd_cmipci_playback; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_capture_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0) + return err; + runtime->hw = snd_cmipci_capture; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_playback2_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */ + return err; + runtime->hw = snd_cmipci_playback2; + down(&cm->open_mutex); + if (! cm->opened[CM_CH_PLAY]) { + if (cm->can_multi_ch) { + runtime->hw.channels_max = cm->max_channels; + if (cm->max_channels == 4) + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4); + else + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6); + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + } + up(&cm->open_mutex); + return 0; +} + +static int snd_cmipci_playback_spdif_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */ + return err; + runtime->hw = snd_cmipci_playback_spdif; +#ifdef DO_SOFT_AC3 + if (cm->can_ac3_hw) +#endif + runtime->hw.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); + cm->dig_pcm_status = cm->dig_status; + return 0; +} + +static int snd_cmipci_capture_spdif_open(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */ + return err; + runtime->hw = snd_cmipci_capture_spdif; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); + return 0; +} + + +/* + */ + +static int snd_cmipci_playback_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_PLAYBACK); + return 0; +} + +static int snd_cmipci_capture_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_CAPTURE); + return 0; +} + +static int snd_cmipci_playback2_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_PLAYBACK2); + close_device_check(cm, CM_OPEN_PLAYBACK_MULTI); + return 0; +} + +static int snd_cmipci_playback_spdif_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK); + return 0; +} + +static int snd_cmipci_capture_spdif_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_SPDIF_CAPTURE); + return 0; +} + + +/* + */ + +static snd_pcm_ops_t snd_cmipci_playback_ops = { + .open = snd_cmipci_playback_open, + .close = snd_cmipci_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_hw_params, + .hw_free = snd_cmipci_playback_hw_free, + .prepare = snd_cmipci_playback_prepare, + .trigger = snd_cmipci_playback_trigger, + .pointer = snd_cmipci_playback_pointer, +}; + +static snd_pcm_ops_t snd_cmipci_capture_ops = { + .open = snd_cmipci_capture_open, + .close = snd_cmipci_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_hw_params, + .hw_free = snd_cmipci_hw_free, + .prepare = snd_cmipci_capture_prepare, + .trigger = snd_cmipci_capture_trigger, + .pointer = snd_cmipci_capture_pointer, +}; + +static snd_pcm_ops_t snd_cmipci_playback2_ops = { + .open = snd_cmipci_playback2_open, + .close = snd_cmipci_playback2_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_playback2_hw_params, + .hw_free = snd_cmipci_hw_free, + .prepare = snd_cmipci_capture_prepare, /* channel B */ + .trigger = snd_cmipci_capture_trigger, /* channel B */ + .pointer = snd_cmipci_capture_pointer, /* channel B */ +}; + +static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = { + .open = snd_cmipci_playback_spdif_open, + .close = snd_cmipci_playback_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_hw_params, + .hw_free = snd_cmipci_playback_hw_free, + .prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */ + .trigger = snd_cmipci_playback_trigger, + .pointer = snd_cmipci_playback_pointer, +}; + +#ifdef DO_SOFT_AC3 +static snd_pcm_ops_t snd_cmipci_playback_spdif_soft_ops = { + .open = snd_cmipci_playback_spdif_open, + .close = snd_cmipci_playback_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_hw_params, + .hw_free = snd_cmipci_playback_hw_free, + .prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */ + .trigger = snd_cmipci_playback_trigger, + .pointer = snd_cmipci_playback_pointer, + .copy = snd_cmipci_ac3_copy, + .silence = snd_cmipci_ac3_silence, +}; +#endif + +static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = { + .open = snd_cmipci_capture_spdif_open, + .close = snd_cmipci_capture_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_hw_params, + .hw_free = snd_cmipci_capture_spdif_hw_free, + .prepare = snd_cmipci_capture_spdif_prepare, + .trigger = snd_cmipci_capture_trigger, + .pointer = snd_cmipci_capture_pointer, +}; + + +/* + */ + +static void snd_cmipci_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_cmipci_pcm_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI DAC/ADC"); + cm->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +static int __devinit snd_cmipci_pcm2_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI 2nd DAC"); + cm->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); + if (err < 0) + return err; + +#ifdef DO_SOFT_AC3 + if (cm->can_ac3_hw) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); + else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_soft_ops); +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); +#endif + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI IEC958"); + cm->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +/* + * mixer interface: + * - CM8338/8738 has a compatible mixer interface with SB16, but + * lack of some elements like tone control, i/o gain and AGC. + * - Access to native registers: + * - A 3D switch + * - Output mute switches + */ + +static void snd_cmipci_mixer_write(cmipci_t *s, unsigned char idx, unsigned char data) +{ + outb(idx, s->iobase + CM_REG_SB16_ADDR); + outb(data, s->iobase + CM_REG_SB16_DATA); +} + +static unsigned char snd_cmipci_mixer_read(cmipci_t *s, unsigned char idx) +{ + unsigned char v; + + outb(idx, s->iobase + CM_REG_SB16_ADDR); + v = inb(s->iobase + CM_REG_SB16_DATA); + return v; +} + +/* + * general mixer element + */ +typedef struct cmipci_sb_reg { + unsigned int left_reg, right_reg; + unsigned int left_shift, right_shift; + unsigned int mask; + unsigned int invert: 1; + unsigned int stereo: 1; +} cmipci_sb_reg_t; + +#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \ + ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23)) + +#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cmipci_info_volume, \ + .get = snd_cmipci_get_volume, .put = snd_cmipci_put_volume, \ + .private_value = COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \ +} + +#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1) +#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0) +#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1) +#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0) + +static void cmipci_sb_reg_decode(cmipci_sb_reg_t *r, unsigned long val) +{ + r->left_reg = val & 0xff; + r->right_reg = (val >> 8) & 0xff; + r->left_shift = (val >> 16) & 0x07; + r->right_shift = (val >> 19) & 0x07; + r->invert = (val >> 22) & 1; + r->stereo = (val >> 23) & 1; + r->mask = (val >> 24) & 0xff; +} + +static int snd_cmipci_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + cmipci_sb_reg_t reg; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = reg.stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg.mask; + return 0; +} + +static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[0] = val; + if (reg.stereo) { + val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[1] = val; + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int change; + int left, right, oleft, oright; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + left = ucontrol->value.integer.value[0] & reg.mask; + if (reg.invert) + left = reg.mask - left; + left <<= reg.left_shift; + if (reg.stereo) { + right = ucontrol->value.integer.value[1] & reg.mask; + if (reg.invert) + right = reg.mask - right; + right <<= reg.right_shift; + } else + right = 0; + spin_lock_irqsave(&cm->reg_lock, flags); + oleft = snd_cmipci_mixer_read(cm, reg.left_reg); + left |= oleft & ~(reg.mask << reg.left_shift); + change = left != oleft; + if (reg.stereo) { + if (reg.left_reg != reg.right_reg) { + snd_cmipci_mixer_write(cm, reg.left_reg, left); + oright = snd_cmipci_mixer_read(cm, reg.right_reg); + } else + oright = left; + right |= oright & ~(reg.mask << reg.right_shift); + change |= right != oright; + snd_cmipci_mixer_write(cm, reg.right_reg, right); + } else + snd_cmipci_mixer_write(cm, reg.left_reg, left); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +/* + * input route (left,right) -> (left,right) + */ +#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cmipci_info_input_sw, \ + .get = snd_cmipci_get_input_sw, .put = snd_cmipci_put_input_sw, \ + .private_value = COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \ +} + +static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int val1, val2; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + val1 = snd_cmipci_mixer_read(cm, reg.left_reg); + val2 = snd_cmipci_mixer_read(cm, reg.right_reg); + spin_unlock_irqrestore(&cm->reg_lock, flags); + ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1; + ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1; + ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1; + ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1; + return 0; +} + +static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int change; + int val1, val2, oval1, oval2; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oval1 = snd_cmipci_mixer_read(cm, reg.left_reg); + oval2 = snd_cmipci_mixer_read(cm, reg.right_reg); + val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); + val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift; + change = val1 != oval1 || val2 != oval2; + snd_cmipci_mixer_write(cm, reg.left_reg, val1); + snd_cmipci_mixer_write(cm, reg.right_reg, val2); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +/* + * native mixer switches/volumes + */ + +#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cmipci_info_native_mixer, \ + .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ + .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \ +} + +#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cmipci_info_native_mixer, \ + .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ + .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \ +} + +#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cmipci_info_native_mixer, \ + .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ + .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \ +} + +#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cmipci_info_native_mixer, \ + .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ + .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \ +} + +static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + cmipci_sb_reg_t reg; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = reg.stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg.mask; + return 0; + +} + +static int snd_cmipci_get_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + cmipci_sb_reg_t reg; + unsigned long flags; + unsigned char oreg, val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oreg = inb(cm->iobase + reg.left_reg); + val = (oreg >> reg.left_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[0] = val; + if (reg.stereo) { + val = (oreg >> reg.right_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[1] = val; + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_put_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + cmipci_sb_reg_t reg; + unsigned long flags; + unsigned char oreg, nreg, val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oreg = inb(cm->iobase + reg.left_reg); + val = ucontrol->value.integer.value[0] & reg.mask; + if (reg.invert) + val = reg.mask - val; + nreg = oreg & ~(reg.mask << reg.left_shift); + nreg |= (val << reg.left_shift); + if (reg.stereo) { + val = ucontrol->value.integer.value[1] & reg.mask; + if (reg.invert) + val = reg.mask - val; + nreg &= ~(reg.mask << reg.right_shift); + nreg |= (val << reg.right_shift); + } + outb(nreg, cm->iobase + reg.left_reg); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return (nreg != oreg); +} + +/* + * special case - check mixer sensitivity + */ +static int snd_cmipci_get_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + //cmipci_t *cm = snd_kcontrol_chip(kcontrol); + return snd_cmipci_get_native_mixer(kcontrol, ucontrol); +} + +static int snd_cmipci_put_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + if (cm->mixer_insensitive) { + /* ignored */ + return 0; + } + return snd_cmipci_put_native_mixer(kcontrol, ucontrol); +} + + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = { + CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31), + CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0), + CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31), + //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1), + { /* switch with sensitivity */ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_cmipci_info_native_mixer, + .get = snd_cmipci_get_native_mixer_sensitive, + .put = snd_cmipci_put_native_mixer_sensitive, + .private_value = COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0), + }, + CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0), + CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31), + CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1), + CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5), + CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31), + CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1), + CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1), + CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31), + CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3), + CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3), + CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), + CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), + CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), + CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), + CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), + CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), + CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), + CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), + CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), +}; + +/* + * other switches + */ + +typedef struct snd_cmipci_switch_args { + int reg; /* register index */ + unsigned int mask; /* mask bits */ + unsigned int mask_on; /* mask bits to turn on */ + int is_byte: 1; /* byte access? */ + int ac3_sensitive: 1; /* access forbidden during non-audio operation? */ +} snd_cmipci_switch_args_t; + +static int snd_cmipci_uswitch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) +{ + unsigned long flags; + unsigned int val; + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (args->ac3_sensitive && cm->mixer_insensitive) { + ucontrol->value.integer.value[0] = 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; + } + if (args->is_byte) + val = inb(cm->iobase + args->reg); + else + val = snd_cmipci_read(cm, args->reg); + ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; + snd_assert(args != NULL, return -EINVAL); + return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args); +} + +static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) +{ + unsigned long flags; + unsigned int val; + int change; + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (args->ac3_sensitive && cm->mixer_insensitive) { + /* ignored */ + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; + } + if (args->is_byte) + val = inb(cm->iobase + args->reg); + else + val = snd_cmipci_read(cm, args->reg); + change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0); + if (change) { + val &= ~args->mask; + if (ucontrol->value.integer.value[0]) + val |= args->mask_on; + else + val |= (args->mask & ~args->mask_on); + if (args->is_byte) + outb((unsigned char)val, cm->iobase + args->reg); + else + snd_cmipci_write(cm, args->reg, val); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +static int snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; + snd_assert(args != NULL, return -EINVAL); + return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args); +} + +#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \ +static snd_cmipci_switch_args_t cmipci_switch_arg_##sname = { \ + .reg = xreg, \ + .mask = xmask, \ + .mask_on = xmask_on, \ + .is_byte = xis_byte, \ + .ac3_sensitive = xac3, \ +} + +#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \ + DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3) + +#if 0 /* these will be controlled in pcm device */ +DEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_out, CM_REG_FUNCTRL1, CM_SPDF_0, 0, 0); +#endif +DEFINE_BIT_SWITCH_ARG(spdif_in_sel1, CM_REG_CHFORMAT, CM_SPDIF_SELECT1, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_in_sel2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT2, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0); +DEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1); +DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */ +// DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0); +/* DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0); */ +DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_MISC, CM_SPDIF_INVERSE, 1, 0); +DEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0); +#if CM_CH_PLAY == 1 +DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */ +#else +DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0); +#endif +DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0); +DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0); +DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0); +DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); +DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0); + +#define DEFINE_SWITCH(sname, stype, sarg) \ +{ .name = sname, \ + .iface = stype, \ + .info = snd_cmipci_uswitch_info, \ + .get = snd_cmipci_uswitch_get, \ + .put = snd_cmipci_uswitch_put, \ + .private_value = (unsigned long)&cmipci_switch_arg_##sarg,\ +} + +#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg) +#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg) + + +/* + * callbacks for spdif output switch + * needs toggle two registers.. + */ +static int snd_cmipci_spdout_enable_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int changed; + changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); + changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + return changed; +} + +static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + int changed; + changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); + changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + if (changed) { + if (ucontrol->value.integer.value[0]) { + if (chip->spdif_playback_avail) + snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + } else { + if (chip->spdif_playback_avail) + snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + } + } + chip->spdif_playback_enabled = ucontrol->value.integer.value[0]; + return changed; +} + + +/* both for CM8338/8738 */ +static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac), + DEFINE_MIXER_SWITCH("Four Channel Mode", fourch), + DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear), +}; + +/* only for CM8738 */ +static snd_kcontrol_new_t snd_cmipci_8738_mixer_switches[] __devinitdata = { +#if 0 /* controlled in pcm device */ + DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in), + DEFINE_MIXER_SWITCH("IEC958 Out", spdif_out), + DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac), +#endif + // DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable), + { .name = "IEC958 Output Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_cmipci_uswitch_info, + .get = snd_cmipci_spdout_enable_get, + .put = snd_cmipci_spdout_enable_put, + }, + DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid), + DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright), + DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v), +// DEFINE_MIXER_SWITCH("IEC958 In/Out 48KHz", spdo_48k), + DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop), + DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor), +}; + +/* only for model 033/037 */ +static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase), + DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1), +}; + +/* only for model 039 or later */ +static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass), + DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2), + DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */ +}; + +/* card control switches */ +static snd_kcontrol_new_t snd_cmipci_control_switches[] __devinitdata = { + DEFINE_CARD_SWITCH("Joystick", joystick), + DEFINE_CARD_SWITCH("Modem", modem), +}; + + +static int __devinit snd_cmipci_mixer_new(cmipci_t *cm, int pcm_spdif_device) +{ + unsigned long flags; + snd_card_t *card; + snd_kcontrol_new_t *sw; + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + snd_assert(cm != NULL && cm->card != NULL, return -EINVAL); + + card = cm->card; + + strcpy(card->mixername, "CMedia PCI"); + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ + spin_unlock_irqrestore(&cm->reg_lock, flags); + + for (idx = 0; idx < num_controls(snd_cmipci_mixers); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0) + return err; + } + + /* mixer switches */ + sw = snd_cmipci_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 || + cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { + sw = snd_cmipci_8738_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_8738_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + cm->spdif_pcm_ctl = kctl; + if (cm->chip_version <= 37) { + sw = snd_cmipci_old_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_old_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + } + } + if (cm->chip_version >= 39) { + sw = snd_cmipci_extra_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + } + + /* card switches */ + sw = snd_cmipci_control_switches; + for (idx = 0; idx < num_controls(snd_cmipci_control_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + + for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { + snd_ctl_elem_id_t id; + snd_kcontrol_t *ctl; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, cm_saved_mixer[idx].name); + if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL) + cm->mixer_res_ctl[idx] = ctl; + } + + return 0; +} + + +/* + * proc interface + */ + +static void snd_cmipci_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, entry->private_data, return); + int i; + + snd_iprintf(buffer, "%s\n\n", cm->card->longname); + for (i = 0; i < 0x40; i++) { + int v = inb(cm->iobase + i); + if (i % 4 == 0) + snd_iprintf(buffer, "%02x: ", i); + snd_iprintf(buffer, "%02x", v); + if (i % 4 == 3) + snd_iprintf(buffer, "\n"); + else + snd_iprintf(buffer, " "); + } +} + +static void __devinit snd_cmipci_proc_init(cmipci_t *cm) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(cm->card, "cmipci", &entry)) + snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); +} + + +static struct pci_device_id snd_cmipci_ids[] __devinitdata = { + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + + +/* + * check chip version and capabilities + * driver name is modified according to the chip model + */ +static void __devinit query_chip(cmipci_t *cm) +{ + unsigned int detect; + + /* check reg 0Ch, bit 24-31 */ + detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2; + if (! detect) { + /* check reg 08h, bit 24-28 */ + detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1; + if (! detect) { + cm->chip_version = 33; + cm->max_channels = 2; + if (cm->do_soft_ac3) + cm->can_ac3_sw = 1; + else + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + } else { + cm->chip_version = 37; + cm->max_channels = 2; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + } + } else { + /* check reg 0Ch, bit 26 */ + if (detect & CM_CHIP_039) { + cm->chip_version = 39; + if (detect & CM_CHIP_039_6CH) + cm->max_channels = 6; + else + cm->max_channels = 4; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + cm->can_multi_ch = 1; + } else { + cm->chip_version = 55; /* 4 or 6 channels */ + cm->max_channels = 6; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + cm->can_multi_ch = 1; + } + } + + /* added -MCx suffix for chip supporting multi-channels */ + if (cm->can_multi_ch) + sprintf(cm->card->driver + strlen(cm->card->driver), + "-MC%d", cm->max_channels); +} + + +static int snd_cmipci_free(cmipci_t *cm) +{ + if (cm->irq >= 0) { + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); + snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ + snd_cmipci_ch_reset(cm, CM_CH_PLAY); + snd_cmipci_ch_reset(cm, CM_CH_CAPT); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ + snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0); + + /* reset mixer */ + snd_cmipci_mixer_write(cm, 0, 0); + + synchronize_irq(cm->irq); + + free_irq(cm->irq, (void *)cm); + } + if (cm->res_iobase) { + release_resource(cm->res_iobase); + kfree_nocheck(cm->res_iobase); + } + snd_magic_kfree(cm); + return 0; +} + +static int snd_cmipci_dev_free(snd_device_t *device) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, device->device_data, return -ENXIO); + return snd_cmipci_free(cm); +} + +static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci, + int dev, cmipci_t **rcmipci) +{ + cmipci_t *cm; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_cmipci_dev_free, + }; + unsigned int val = 0; + unsigned long iomidi = mpu_port[dev]; + unsigned long iosynth = fm_port[dev]; + int pcm_index, pcm_spdif_index; + + *rcmipci = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + cm = snd_magic_kcalloc(cmipci_t, 0, GFP_KERNEL); + if (cm == NULL) + return -ENOMEM; + + spin_lock_init(&cm->reg_lock); + init_MUTEX(&cm->open_mutex); + cm->device = pci->device; + cm->card = card; + cm->pci = pci; + cm->irq = -1; + cm->iobase = pci_resource_start(pci, 0); + cm->channel[0].ch = 0; + cm->channel[1].ch = 1; + cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */ + + if ((cm->res_iobase = request_region(cm->iobase, CM_EXTENT_CODEC, card->driver)) == NULL) { + snd_printk("unable to grab ports 0x%lx-0x%lx\n", cm->iobase, cm->iobase + CM_EXTENT_CODEC - 1); + err = -EBUSY; + goto __error; + } + if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)cm)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + err = -EBUSY; + goto __error; + } + cm->irq = pci->irq; + + pci_set_master(cm->pci); + + /* + * check chip version, max channels and capabilities + */ + + cm->chip_version = 0; + cm->max_channels = 2; +#ifdef DO_SOFT_AC3 + cm->do_soft_ac3 = soft_ac3[dev]; +#endif + + query_chip(cm); + + cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + +#if CM_CH_PLAY == 1 + cm->ctrl = CM_CHADC0; /* default FUNCNTRL0 */ +#else + cm->ctrl = CM_CHADC1; /* default FUNCNTRL0 */ +#endif + + /* initialize codec registers */ + snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ + snd_cmipci_ch_reset(cm, CM_CH_PLAY); + snd_cmipci_ch_reset(cm, CM_CH_CAPT); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ + snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0); + + snd_cmipci_write(cm, CM_REG_CHFORMAT, 0); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D); +#if CM_CH_PLAY == 1 + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); +#else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); +#endif + /* Set Bus Master Request */ + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_BREQ); + + /* Assume TX and compatible chip set (Autodetection required for VX chip sets) */ + switch (pci->device) { + case PCI_DEVICE_ID_CMEDIA_CM8738: + case PCI_DEVICE_ID_CMEDIA_CM8738B: + /* PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX */ + if (! pci_find_device(0x8086, 0x7030, NULL)) + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_TXVX); + break; + default: + break; + } + + /* set MPU address */ + switch (iomidi) { + case 0x320: val = CM_VMPU_320; break; + case 0x310: val = CM_VMPU_310; break; + case 0x300: val = CM_VMPU_300; break; + case 0x330: val = CM_VMPU_330; break; + default: + iomidi = 0; break; + } + if (iomidi > 0) { + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); + /* enable UART */ + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN); + } + + /* set FM address */ + val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK; + switch (iosynth) { + case 0x3E8: val |= CM_FMSEL_3E8; break; + case 0x3E0: val |= CM_FMSEL_3E0; break; + case 0x3C8: val |= CM_FMSEL_3C8; break; + case 0x388: val |= CM_FMSEL_388; break; + default: + iosynth = 0; break; + } + if (iosynth > 0) { + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); + /* enable FM */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); + + if (snd_opl3_create(card, iosynth, iosynth + 2, + OPL3_HW_OPL3, 0, &cm->opl3) < 0) { + printk(KERN_ERR "cmipci: no OPL device at 0x%lx, skipping...\n", iosynth); + iosynth = 0; + } else { + if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) { + printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n"); + return err; + } + } + } + if (! iosynth) { + /* disable FM */ + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val & ~CM_FMSEL_MASK); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); + } + + /* reset mixer */ + snd_cmipci_mixer_write(cm, 0, 0); + + snd_cmipci_proc_init(cm); + + /* create pcm devices */ + pcm_index = pcm_spdif_index = 0; + if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0) + goto __error; + pcm_index++; + if (cm->has_dual_dac) { + if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0) + goto __error; + pcm_index++; + } + if (cm->can_ac3_hw || cm->can_ac3_sw) { + pcm_spdif_index = pcm_index; + if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0) + goto __error; + } + + /* create mixer interface & switches */ + if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0) + goto __error; + + if (iomidi > 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + iomidi, 0, + cm->irq, 0, &cm->rmidi)) < 0) { + printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi); + } + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) { + snd_cmipci_free(cm); + return err; + } +#ifdef USE_VAR48KRATE + for (val = 0; val < ARRAY_SIZE(rates); val++) + snd_cmipci_set_pll(cm, rates[val], val); + + /* + * (Re-)Enable external switch spdo_48k + */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K|CM_SPDF_AC97); +#endif /* USE_VAR48KRATE */ + + *rcmipci = cm; + return 0; + + __error: + snd_cmipci_free(cm); + return err; +} + +/* + */ + +MODULE_DEVICE_TABLE(pci, snd_cmipci_ids); + +static int __devinit snd_cmipci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + cmipci_t *cm; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (! enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_CMEDIA_CM8738: + case PCI_DEVICE_ID_CMEDIA_CM8738B: + strcpy(card->driver, "CMI8738"); + break; + case PCI_DEVICE_ID_CMEDIA_CM8338A: + case PCI_DEVICE_ID_CMEDIA_CM8338B: + strcpy(card->driver, "CMI8338"); + break; + default: + strcpy(card->driver, "CMIPCI"); + break; + } + + if ((err = snd_cmipci_create(card, pci, dev, &cm)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "C-Media PCI %s", card->driver); + sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i", + card->shortname, + cm->chip_version, + cm->iobase, + cm->irq); + + //snd_printd("%s is detected\n", card->longname); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; + +} + +static void __devexit snd_cmipci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + .name = "C-Media PCI", + .id_table = snd_cmipci_ids, + .probe = snd_cmipci_probe, + .remove = __devexit_p(snd_cmipci_remove), +}; + +static int __init alsa_card_cmipci_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "C-Media PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cmipci_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cmipci_init) +module_exit(alsa_card_cmipci_exit) + +#ifndef MODULE + +/* format is: snd-cmipci=enable,index,id, + mpu_port,fm_port */ + +static int __init alsa_card_cmipci_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cmipci=", alsa_card_cmipci_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs4281.c linux/sound/pci/cs4281.c --- linux-2.4.21-rc1.orig/sound/pci/cs4281.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs4281.c 2003-02-25 06:35:41.000000000 -0700 @@ -0,0 +1,2228 @@ +/* + * Driver for Cirrus Logic CS4281 based PCI soundcard + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Cirrus Logic CS4281"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Cirrus Logic,CS4281}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ +static int dual_codec[SNDRV_CARDS]; /* dual codec */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for CS4281 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for CS4281 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable CS4281 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(dual_codec, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled)."); +MODULE_PARM_SYNTAX(dual_codec, SNDRV_ENABLED ",allows:{{0,3}}"); + +/* + * + */ + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4281 +#define PCI_DEVICE_ID_CIRRUS_4281 0x6005 +#endif + +/* + * Direct registers + */ + +#define CS4281_BA0_SIZE 0x1000 +#define CS4281_BA1_SIZE 0x10000 + +/* + * BA0 registers + */ +#define BA0_HISR 0x0000 /* Host Interrupt Status Register */ +#define BA0_HISR_INTENA (1<<31) /* Internal Interrupt Enable Bit */ +#define BA0_HISR_MIDI (1<<22) /* MIDI port interrupt */ +#define BA0_HISR_FIFOI (1<<20) /* FIFO polled interrupt */ +#define BA0_HISR_DMAI (1<<18) /* DMA interrupt (half or end) */ +#define BA0_HISR_FIFO(c) (1<<(12+(c))) /* FIFO channel interrupt */ +#define BA0_HISR_DMA(c) (1<<(8+(c))) /* DMA channel interrupt */ +#define BA0_HISR_GPPI (1<<5) /* General Purpose Input (Primary chip) */ +#define BA0_HISR_GPSI (1<<4) /* General Purpose Input (Secondary chip) */ +#define BA0_HISR_GP3I (1<<3) /* GPIO3 pin Interrupt */ +#define BA0_HISR_GP1I (1<<2) /* GPIO1 pin Interrupt */ +#define BA0_HISR_VUPI (1<<1) /* VOLUP pin Interrupt */ +#define BA0_HISR_VDNI (1<<0) /* VOLDN pin Interrupt */ + +#define BA0_HICR 0x0008 /* Host Interrupt Control Register */ +#define BA0_HICR_CHGM (1<<1) /* INTENA Change Mask */ +#define BA0_HICR_IEV (1<<0) /* INTENA Value */ +#define BA0_HICR_EOI (3<<0) /* End of Interrupt command */ + +#define BA0_HIMR 0x000c /* Host Interrupt Mask Register */ + /* Use same contants as for BA0_HISR */ + +#define BA0_IIER 0x0010 /* ISA Interrupt Enable Register */ + +#define BA0_HDSR0 0x00f0 /* Host DMA Engine 0 Status Register */ +#define BA0_HDSR1 0x00f4 /* Host DMA Engine 1 Status Register */ +#define BA0_HDSR2 0x00f8 /* Host DMA Engine 2 Status Register */ +#define BA0_HDSR3 0x00fc /* Host DMA Engine 3 Status Register */ + +#define BA0_HDSR_CH1P (1<<25) /* Channel 1 Pending */ +#define BA0_HDSR_CH2P (1<<24) /* Channel 2 Pending */ +#define BA0_HDSR_DHTC (1<<17) /* DMA Half Terminal Count */ +#define BA0_HDSR_DTC (1<<16) /* DMA Terminal Count */ +#define BA0_HDSR_DRUN (1<<15) /* DMA Running */ +#define BA0_HDSR_RQ (1<<7) /* Pending Request */ + +#define BA0_DCA0 0x0110 /* Host DMA Engine 0 Current Address */ +#define BA0_DCC0 0x0114 /* Host DMA Engine 0 Current Count */ +#define BA0_DBA0 0x0118 /* Host DMA Engine 0 Base Address */ +#define BA0_DBC0 0x011c /* Host DMA Engine 0 Base Count */ +#define BA0_DCA1 0x0120 /* Host DMA Engine 1 Current Address */ +#define BA0_DCC1 0x0124 /* Host DMA Engine 1 Current Count */ +#define BA0_DBA1 0x0128 /* Host DMA Engine 1 Base Address */ +#define BA0_DBC1 0x012c /* Host DMA Engine 1 Base Count */ +#define BA0_DCA2 0x0130 /* Host DMA Engine 2 Current Address */ +#define BA0_DCC2 0x0134 /* Host DMA Engine 2 Current Count */ +#define BA0_DBA2 0x0138 /* Host DMA Engine 2 Base Address */ +#define BA0_DBC2 0x013c /* Host DMA Engine 2 Base Count */ +#define BA0_DCA3 0x0140 /* Host DMA Engine 3 Current Address */ +#define BA0_DCC3 0x0144 /* Host DMA Engine 3 Current Count */ +#define BA0_DBA3 0x0148 /* Host DMA Engine 3 Base Address */ +#define BA0_DBC3 0x014c /* Host DMA Engine 3 Base Count */ +#define BA0_DMR0 0x0150 /* Host DMA Engine 0 Mode */ +#define BA0_DCR0 0x0154 /* Host DMA Engine 0 Command */ +#define BA0_DMR1 0x0158 /* Host DMA Engine 1 Mode */ +#define BA0_DCR1 0x015c /* Host DMA Engine 1 Command */ +#define BA0_DMR2 0x0160 /* Host DMA Engine 2 Mode */ +#define BA0_DCR2 0x0164 /* Host DMA Engine 2 Command */ +#define BA0_DMR3 0x0168 /* Host DMA Engine 3 Mode */ +#define BA0_DCR3 0x016c /* Host DMA Engine 3 Command */ + +#define BA0_DMR_DMA (1<<29) /* Enable DMA mode */ +#define BA0_DMR_POLL (1<<28) /* Enable poll mode */ +#define BA0_DMR_TBC (1<<25) /* Transfer By Channel */ +#define BA0_DMR_CBC (1<<24) /* Count By Channel (0 = frame resolution) */ +#define BA0_DMR_SWAPC (1<<22) /* Swap Left/Right Channels */ +#define BA0_DMR_SIZE20 (1<<20) /* Sample is 20-bit */ +#define BA0_DMR_USIGN (1<<19) /* Unsigned */ +#define BA0_DMR_BEND (1<<18) /* Big Endian */ +#define BA0_DMR_MONO (1<<17) /* Mono */ +#define BA0_DMR_SIZE8 (1<<16) /* Sample is 8-bit */ +#define BA0_DMR_TYPE_DEMAND (0<<6) +#define BA0_DMR_TYPE_SINGLE (1<<6) +#define BA0_DMR_TYPE_BLOCK (2<<6) +#define BA0_DMR_TYPE_CASCADE (3<<6) /* Not supported */ +#define BA0_DMR_DEC (1<<5) /* Access Increment (0) or Decrement (1) */ +#define BA0_DMR_AUTO (1<<4) /* Auto-Initialize */ +#define BA0_DMR_TR_VERIFY (0<<2) /* Verify Transfer */ +#define BA0_DMR_TR_WRITE (1<<2) /* Write Transfer */ +#define BA0_DMR_TR_READ (2<<2) /* Read Transfer */ + +#define BA0_DCR_HTCIE (1<<17) /* Half Terminal Count Interrupt */ +#define BA0_DCR_TCIE (1<<16) /* Terminal Count Interrupt */ +#define BA0_DCR_MSK (1<<0) /* DMA Mask bit */ + +#define BA0_FCR0 0x0180 /* FIFO Control 0 */ +#define BA0_FCR1 0x0184 /* FIFO Control 1 */ +#define BA0_FCR2 0x0188 /* FIFO Control 2 */ +#define BA0_FCR3 0x018c /* FIFO Control 3 */ + +#define BA0_FCR_FEN (1<<31) /* FIFO Enable bit */ +#define BA0_FCR_DACZ (1<<30) /* DAC Zero */ +#define BA0_FCR_PSH (1<<29) /* Previous Sample Hold */ +#define BA0_FCR_RS(x) (((x)&0x1f)<<24) /* Right Slot Mapping */ +#define BA0_FCR_LS(x) (((x)&0x1f)<<16) /* Left Slot Mapping */ +#define BA0_FCR_SZ(x) (((x)&0x7f)<<8) /* FIFO buffer size (in samples) */ +#define BA0_FCR_OF(x) (((x)&0x7f)<<0) /* FIFO starting offset (in samples) */ + +#define BA0_FPDR0 0x0190 /* FIFO Polled Data 0 */ +#define BA0_FPDR1 0x0194 /* FIFO Polled Data 1 */ +#define BA0_FPDR2 0x0198 /* FIFO Polled Data 2 */ +#define BA0_FPDR3 0x019c /* FIFO Polled Data 3 */ + +#define BA0_FCHS 0x020c /* FIFO Channel Status */ +#define BA0_FCHS_RCO(x) (1<<(7+(((x)&3)<<3))) /* Right Channel Out */ +#define BA0_FCHS_LCO(x) (1<<(6+(((x)&3)<<3))) /* Left Channel Out */ +#define BA0_FCHS_MRP(x) (1<<(5+(((x)&3)<<3))) /* Move Read Pointer */ +#define BA0_FCHS_FE(x) (1<<(4+(((x)&3)<<3))) /* FIFO Empty */ +#define BA0_FCHS_FF(x) (1<<(3+(((x)&3)<<3))) /* FIFO Full */ +#define BA0_FCHS_IOR(x) (1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */ +#define BA0_FCHS_RCI(x) (1<<(1+(((x)&3)<<3))) /* Right Channel In */ +#define BA0_FCHS_LCI(x) (1<<(0+(((x)&3)<<3))) /* Left Channel In */ + +#define BA0_FSIC0 0x0210 /* FIFO Status and Interrupt Control 0 */ +#define BA0_FSIC1 0x0214 /* FIFO Status and Interrupt Control 1 */ +#define BA0_FSIC2 0x0218 /* FIFO Status and Interrupt Control 2 */ +#define BA0_FSIC3 0x021c /* FIFO Status and Interrupt Control 3 */ + +#define BA0_FSIC_FIC(x) (((x)&0x7f)<<24) /* FIFO Interrupt Count */ +#define BA0_FSIC_FORIE (1<<23) /* FIFO OverRun Interrupt Enable */ +#define BA0_FSIC_FURIE (1<<22) /* FIFO UnderRun Interrupt Enable */ +#define BA0_FSIC_FSCIE (1<<16) /* FIFO Sample Count Interrupt Enable */ +#define BA0_FSIC_FSC(x) (((x)&0x7f)<<8) /* FIFO Sample Count */ +#define BA0_FSIC_FOR (1<<7) /* FIFO OverRun */ +#define BA0_FSIC_FUR (1<<6) /* FIFO UnderRun */ +#define BA0_FSIC_FSCR (1<<0) /* FIFO Sample Count Reached */ + +#define BA0_PMCS 0x0344 /* Power Management Control/Status */ +#define BA0_CWPR 0x03e0 /* Configuration Write Protect */ +#define BA0_EPPMC 0x03e4 /* Extended PCI Power Management Control */ +#define BA0_GPIOR 0x03e8 /* GPIO Pin Interface Register */ + +#define BA0_SPMC 0x03ec /* Serial Port Power Management Control (& ASDIN2 enable) */ +#define BA0_SPMC_GIPPEN (1<<15) /* GP INT Primary PME# Enable */ +#define BA0_SPMC_GISPEN (1<<14) /* GP INT Secondary PME# Enable */ +#define BA0_SPMC_EESPD (1<<9) /* EEPROM Serial Port Disable */ +#define BA0_SPMC_ASDI2E (1<<8) /* ASDIN2 Enable */ +#define BA0_SPMC_ASDO (1<<7) /* Asynchronous ASDOUT Assertion */ +#define BA0_SPMC_WUP2 (1<<3) /* Wakeup for Secondary Input */ +#define BA0_SPMC_WUP1 (1<<2) /* Wakeup for Primary Input */ +#define BA0_SPMC_ASYNC (1<<1) /* Asynchronous ASYNC Assertion */ +#define BA0_SPMC_RSTN (1<<0) /* Reset Not! */ + +#define BA0_CFLR 0x03f0 /* Configuration Load Register (EEPROM or BIOS) */ +#define BA0_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ +#define BA0_IISR 0x03f4 /* ISA Interrupt Select */ +#define BA0_TMS 0x03f8 /* Test Register */ +#define BA0_SSVID 0x03fc /* Subsystem ID register */ + +#define BA0_CLKCR1 0x0400 /* Clock Control Register 1 */ +#define BA0_CLKCR1_CLKON (1<<25) /* Read Only */ +#define BA0_CLKCR1_DLLRDY (1<<24) /* DLL Ready */ +#define BA0_CLKCR1_DLLOS (1<<6) /* DLL Output Select */ +#define BA0_CLKCR1_SWCE (1<<5) /* Clock Enable */ +#define BA0_CLKCR1_DLLP (1<<4) /* DLL PowerUp */ +#define BA0_CLKCR1_DLLSS (((x)&3)<<3) /* DLL Source Select */ + +#define BA0_FRR 0x0410 /* Feature Reporting Register */ +#define BA0_SLT12O 0x041c /* Slot 12 GPIO Output Register for AC-Link */ + +#define BA0_SERMC 0x0420 /* Serial Port Master Control */ +#define BA0_SERMC_FCRN (1<<27) /* Force Codec Ready Not */ +#define BA0_SERMC_ODSEN2 (1<<25) /* On-Demand Support Enable ASDIN2 */ +#define BA0_SERMC_ODSEN1 (1<<24) /* On-Demand Support Enable ASDIN1 */ +#define BA0_SERMC_SXLB (1<<21) /* ASDIN2 to ASDOUT Loopback */ +#define BA0_SERMC_SLB (1<<20) /* ASDOUT to ASDIN2 Loopback */ +#define BA0_SERMC_LOVF (1<<19) /* Loopback Output Valid Frame bit */ +#define BA0_SERMC_TCID(x) (((x)&3)<<16) /* Target Secondary Codec ID */ +#define BA0_SERMC_PXLB (5<<1) /* Primary Port External Loopback */ +#define BA0_SERMC_PLB (4<<1) /* Primary Port Internal Loopback */ +#define BA0_SERMC_PTC (7<<1) /* Port Timing Configuration */ +#define BA0_SERMC_PTC_AC97 (1<<1) /* AC97 mode */ +#define BA0_SERMC_MSPE (1<<0) /* Master Serial Port Enable */ + +#define BA0_SERC1 0x0428 /* Serial Port Configuration 1 */ +#define BA0_SERC1_SO1F(x) (((x)&7)>>1) /* Primary Output Port Format */ +#define BA0_SERC1_AC97 (1<<1) +#define BA0_SERC1_SO1EN (1<<0) /* Primary Output Port Enable */ + +#define BA0_SERC2 0x042c /* Serial Port Configuration 2 */ +#define BA0_SERC2_SI1F(x) (((x)&7)>>1) /* Primary Input Port Format */ +#define BA0_SERC2_AC97 (1<<1) +#define BA0_SERC2_SI1EN (1<<0) /* Primary Input Port Enable */ + +#define BA0_SLT12M 0x045c /* Slot 12 Monitor Register for Primary AC-Link */ + +#define BA0_ACCTL 0x0460 /* AC'97 Control */ +#define BA0_ACCTL_TC (1<<6) /* Target Codec */ +#define BA0_ACCTL_CRW (1<<4) /* 0=Write, 1=Read Command */ +#define BA0_ACCTL_DCV (1<<3) /* Dynamic Command Valid */ +#define BA0_ACCTL_VFRM (1<<2) /* Valid Frame */ +#define BA0_ACCTL_ESYN (1<<1) /* Enable Sync */ + +#define BA0_ACSTS 0x0464 /* AC'97 Status */ +#define BA0_ACSTS_VSTS (1<<1) /* Valid Status */ +#define BA0_ACSTS_CRDY (1<<0) /* Codec Ready */ + +#define BA0_ACOSV 0x0468 /* AC'97 Output Slot Valid */ +#define BA0_ACOSV_SLV(x) (1<<((x)-3)) + +#define BA0_ACCAD 0x046c /* AC'97 Command Address */ +#define BA0_ACCDA 0x0470 /* AC'97 Command Data */ + +#define BA0_ACISV 0x0474 /* AC'97 Input Slot Valid */ +#define BA0_ACISV_SLV(x) (1<<((x)-3)) + +#define BA0_ACSAD 0x0478 /* AC'97 Status Address */ +#define BA0_ACSDA 0x047c /* AC'97 Status Data */ +#define BA0_JSPT 0x0480 /* Joystick poll/trigger */ +#define BA0_JSCTL 0x0484 /* Joystick control */ +#define BA0_JSC1 0x0488 /* Joystick control */ +#define BA0_JSC2 0x048c /* Joystick control */ +#define BA0_JSIO 0x04a0 + +#define BA0_MIDCR 0x0490 /* MIDI Control */ +#define BA0_MIDCR_MRST (1<<5) /* Reset MIDI Interface */ +#define BA0_MIDCR_MLB (1<<4) /* MIDI Loop Back Enable */ +#define BA0_MIDCR_TIE (1<<3) /* MIDI Transmuit Interrupt Enable */ +#define BA0_MIDCR_RIE (1<<2) /* MIDI Receive Interrupt Enable */ +#define BA0_MIDCR_RXE (1<<1) /* MIDI Receive Enable */ +#define BA0_MIDCR_TXE (1<<0) /* MIDI Transmit Enable */ + +#define BA0_MIDCMD 0x0494 /* MIDI Command (wo) */ + +#define BA0_MIDSR 0x0494 /* MIDI Status (ro) */ +#define BA0_MIDSR_RDA (1<<15) /* Sticky bit (RBE 1->0) */ +#define BA0_MIDSR_TBE (1<<14) /* Sticky bit (TBF 0->1) */ +#define BA0_MIDSR_RBE (1<<7) /* Receive Buffer Empty */ +#define BA0_MIDSR_TBF (1<<6) /* Transmit Buffer Full */ + +#define BA0_MIDWP 0x0498 /* MIDI Write */ +#define BA0_MIDRP 0x049c /* MIDI Read (ro) */ + +#define BA0_AODSD1 0x04a8 /* AC'97 On-Demand Slot Disable for primary link (ro) */ +#define BA0_AODSD1_NDS(x) (1<<((x)-3)) + +#define BA0_AODSD2 0x04ac /* AC'97 On-Demand Slot Disable for secondary link (ro) */ +#define BA0_AODSD2_NDS(x) (1<<((x)-3)) + +#define BA0_CFGI 0x04b0 /* Configure Interface (EEPROM interface) */ +#define BA0_SLT12M2 0x04dc /* Slot 12 Monitor Register 2 for secondary AC-link */ +#define BA0_ACSTS2 0x04e4 /* AC'97 Status Register 2 */ +#define BA0_ACISV2 0x04f4 /* AC'97 Input Slot Valid Register 2 */ +#define BA0_ACSAD2 0x04f8 /* AC'97 Status Address Register 2 */ +#define BA0_ACSDA2 0x04fc /* AC'97 Status Data Register 2 */ +#define BA0_FMSR 0x0730 /* FM Synthesis Status (ro) */ +#define BA0_B0AP 0x0730 /* FM Bank 0 Address Port (wo) */ +#define BA0_FMDP 0x0734 /* FM Data Port */ +#define BA0_B1AP 0x0738 /* FM Bank 1 Address Port */ +#define BA0_B1DP 0x073c /* FM Bank 1 Data Port */ + +#define BA0_SSPM 0x0740 /* Sound System Power Management */ +#define BA0_SSPM_MIXEN (1<<6) /* Playback SRC + FM/Wavetable MIX */ +#define BA0_SSPM_CSRCEN (1<<5) /* Capture Sample Rate Converter Enable */ +#define BA0_SSPM_PSRCEN (1<<4) /* Playback Sample Rate Converter Enable */ +#define BA0_SSPM_JSEN (1<<3) /* Joystick Enable */ +#define BA0_SSPM_ACLEN (1<<2) /* Serial Port Engine and AC-Link Enable */ +#define BA0_SSPM_FMEN (1<<1) /* FM Synthesis Block Enable */ + +#define BA0_DACSR 0x0744 /* DAC Sample Rate - Playback SRC */ +#define BA0_ADCSR 0x0748 /* ADC Sample Rate - Capture SRC */ + +#define BA0_SSCR 0x074c /* Sound System Control Register */ +#define BA0_SSCR_HVS1 (1<<23) /* Hardwave Volume Step (0=1,1=2) */ +#define BA0_SSCR_MVCS (1<<19) /* Master Volume Codec Select */ +#define BA0_SSCR_MVLD (1<<18) /* Master Volume Line Out Disable */ +#define BA0_SSCR_MVAD (1<<17) /* Master Volume Alternate Out Disable */ +#define BA0_SSCR_MVMD (1<<16) /* Master Volume Mono Out Disable */ +#define BA0_SSCR_XLPSRC (1<<8) /* External SRC Loopback Mode */ +#define BA0_SSCR_LPSRC (1<<7) /* SRC Loopback Mode */ +#define BA0_SSCR_CDTX (1<<5) /* CD Transfer Data */ +#define BA0_SSCR_HVC (1<<3) /* Harware Volume Control Enable */ + +#define BA0_FMLVC 0x0754 /* FM Synthesis Left Volume Control */ +#define BA0_FMRVC 0x0758 /* FM Synthesis Right Volume Control */ +#define BA0_SRCSA 0x075c /* SRC Slot Assignments */ +#define BA0_PPLVC 0x0760 /* PCM Playback Left Volume Control */ +#define BA0_PPRVC 0x0764 /* PCM Playback Right Volume Control */ +#define BA0_PASR 0x0768 /* playback sample rate */ +#define BA0_CASR 0x076C /* capture sample rate */ + +/* Source Slot Numbers - Playback */ +#define SRCSLOT_LEFT_PCM_PLAYBACK 0 +#define SRCSLOT_RIGHT_PCM_PLAYBACK 1 +#define SRCSLOT_PHONE_LINE_1_DAC 2 +#define SRCSLOT_CENTER_PCM_PLAYBACK 3 +#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK 4 +#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK 5 +#define SRCSLOT_LFE_PCM_PLAYBACK 6 +#define SRCSLOT_PHONE_LINE_2_DAC 7 +#define SRCSLOT_HEADSET_DAC 8 +#define SRCSLOT_LEFT_WT 29 /* invalid for BA0_SRCSA */ +#define SRCSLOT_RIGHT_WT 30 /* invalid for BA0_SRCSA */ + +/* Source Slot Numbers - Capture */ +#define SRCSLOT_LEFT_PCM_RECORD 10 +#define SRCSLOT_RIGHT_PCM_RECORD 11 +#define SRCSLOT_PHONE_LINE_1_ADC 12 +#define SRCSLOT_MIC_ADC 13 +#define SRCSLOT_PHONE_LINE_2_ADC 17 +#define SRCSLOT_HEADSET_ADC 18 +#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD 20 +#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD 21 +#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC 22 +#define SRCSLOT_SECONDARY_MIC_ADC 23 +#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC 27 +#define SRCSLOT_SECONDARY_HEADSET_ADC 28 + +/* Source Slot Numbers - Others */ +#define SRCSLOT_POWER_DOWN 31 + +/* MIDI modes */ +#define CS4281_MODE_OUTPUT (1<<0) +#define CS4281_MODE_INPUT (1<<1) + +/* joystick bits */ +/* Bits for JSPT */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* Bits for JSCTL */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* Data register pairs masks */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* JS GPIO */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * + */ + +#define chip_t cs4281_t + +typedef struct snd_cs4281 cs4281_t; +typedef struct snd_cs4281_dma cs4281_dma_t; + +struct snd_cs4281_dma { + snd_pcm_substream_t *substream; + unsigned int regDBA; /* offset to DBA register */ + unsigned int regDCA; /* offset to DCA register */ + unsigned int regDBC; /* offset to DBC register */ + unsigned int regDCC; /* offset to DCC register */ + unsigned int regDMR; /* offset to DMR register */ + unsigned int regDCR; /* offset to DCR register */ + unsigned int regHDSR; /* offset to HDSR register */ + unsigned int regFCR; /* offset to FCR register */ + unsigned int regFSIC; /* offset to FSIC register */ + unsigned int valDMR; /* DMA mode */ + unsigned int valDCR; /* DMA command */ + unsigned int valFCR; /* FIFO control */ + unsigned int fifo_offset; /* FIFO offset within BA1 */ + unsigned char left_slot; /* FIFO left slot */ + unsigned char right_slot; /* FIFO right slot */ + int frag; /* period number */ +}; + +#define SUSPEND_REGISTERS 20 + +struct snd_cs4281 { + int irq; + + unsigned long ba0; /* virtual (accessible) address */ + unsigned long ba1; /* virtual (accessible) address */ + unsigned long ba0_addr; + unsigned long ba1_addr; + struct resource *ba0_res; + struct resource *ba1_res; + + int dual_codec; + + ac97_t *ac97; + ac97_t *ac97_secondary; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + cs4281_dma_t dma[4]; + + unsigned char src_left_play_slot; + unsigned char src_right_play_slot; + unsigned char src_left_rec_slot; + unsigned char src_right_rec_slot; + + unsigned int spurious_dhtc_irq; + unsigned int spurious_dtc_irq; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + + struct snd_cs4281_gameport *gameport; + +#ifdef CONFIG_PM + u32 suspend_regs[SUSPEND_REGISTERS]; +#endif + +}; + +static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_cs4281_ids[] __devinitdata = { + { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_cs4281_ids); + +/* + * constants + */ + +#define CS4281_FIFO_SIZE 32 + +/* + * common I/O routines + */ + +static void snd_cs4281_delay(unsigned int delay, int can_schedule) +{ + if (delay > 999) { + if (can_schedule) { + signed long end_time; + delay = (delay * HZ) / 1000000; + if (delay < 1) + delay = 1; + end_time = jiffies + delay; + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + } else { + delay += 999; + delay /= 1000; + mdelay(delay > 0 ? delay : 1); + } + } else { + udelay(delay); + } +} + +inline static void snd_cs4281_delay_long(int can_schedule) +{ + if (can_schedule) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } else + mdelay(10); +} + +static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val) +{ + writel(val, chip->ba0 + offset); +} + +static inline unsigned int snd_cs4281_peekBA0(cs4281_t *chip, unsigned long offset) +{ + return readl(chip->ba0 + offset); +} + +static void snd_cs4281_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + int count; + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs4281_pokeBA0(chip, BA0_ACCDA, val); + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM | + BA0_ACCTL_ESYN | (ac97->num ? BA0_ACCTL_TC : 0)); + for (count = 0; count < 2000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) { + return; + } + } + snd_printk(KERN_ERR "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val); +} + +static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO); + int count; + unsigned short result; + // FIXME: volatile is necessary in the following due to a bug of + // some gcc versions + volatile int ac97_num = ((volatile ac97_t *)ac97)->num; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0); + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW | + BA0_ACCTL_VFRM | BA0_ACCTL_ESYN | + (ac97_num ? BA0_ACCTL_TC : 0)); + + + /* + * Wait for the read to occur. + */ + for (count = 0; count < 500; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) + goto __ok1; + } + + snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); + result = 0xffff; + goto __end; + + __ok1: + /* + * Wait for the valid status bit to go active. + */ + for (count = 0; count < 100; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS) + goto __ok2; + udelay(10); + } + + snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); + result = 0xffff; + goto __end; + + __ok2: + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ + result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); + + __end: + return result; +} + +/* + * PCM part + */ + +static int snd_cs4281_trigger(snd_pcm_substream_t *substream, int cmd) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma->valDCR |= BA0_DCR_MSK; + dma->valFCR |= BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dma->valDCR &= ~BA0_DCR_MSK; + dma->valFCR &= ~BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA); + dma->valDMR |= BA0_DMR_DMA; + dma->valDCR &= ~BA0_DCR_MSK; + dma->valFCR |= BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL); + dma->valDCR |= BA0_DCR_MSK; + dma->valFCR &= ~BA0_FCR_FEN; + /* Leave wave playback FIFO enabled for FM */ + if (dma->regFCR != BA0_FCR0) + dma->valFCR &= ~BA0_FCR_FEN; + break; + default: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -EINVAL; + } + snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR); + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR); + snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate) +{ + unsigned int val = ~0; + + if (real_rate) + *real_rate = rate; + /* special "hardcoded" rates */ + switch (rate) { + case 8000: return 5; + case 11025: return 4; + case 16000: return 3; + case 22050: return 2; + case 44100: return 1; + case 48000: return 0; + default: + goto __variable; + } + __variable: + val = 1536000 / rate; + if (real_rate) + *real_rate = 1536000 / val; + return val; +} + +static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src) +{ + int rec_mono; + + dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO | + (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ); + if (runtime->channels == 1) + dma->valDMR |= BA0_DMR_MONO; + if (snd_pcm_format_unsigned(runtime->format) > 0) + dma->valDMR |= BA0_DMR_USIGN; + if (snd_pcm_format_big_endian(runtime->format) > 0) + dma->valDMR |= BA0_DMR_BEND; + switch (snd_pcm_format_width(runtime->format)) { + case 8: dma->valDMR |= BA0_DMR_SIZE8; + if (runtime->channels == 1) + dma->valDMR |= BA0_DMR_SWAPC; + break; + case 32: dma->valDMR |= BA0_DMR_SIZE20; break; + } + dma->frag = 0; /* for workaround */ + dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK; + if (runtime->buffer_size != runtime->period_size) + dma->valDCR |= BA0_DCR_HTCIE; + /* Initialize DMA */ + snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr); + snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1); + rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; + snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | + (chip->src_right_play_slot << 8) | + (chip->src_left_rec_slot << 16) | + ((rec_mono ? 31 : chip->src_right_rec_slot) << 24)); + if (!src) + goto __skip_src; + if (!capture) { + if (dma->left_slot == chip->src_left_play_slot) { + unsigned int val = snd_cs4281_rate(runtime->rate, NULL); + snd_assert(dma->right_slot == chip->src_right_play_slot, ); + snd_cs4281_pokeBA0(chip, BA0_DACSR, val); + } + } else { + if (dma->left_slot == chip->src_left_rec_slot) { + unsigned int val = snd_cs4281_rate(runtime->rate, NULL); + snd_assert(dma->right_slot == chip->src_right_rec_slot, ); + snd_cs4281_pokeBA0(chip, BA0_ADCSR, val); + } + } + __skip_src: + /* Deactivate wave playback FIFO before changing slot assignments */ + if (dma->regFCR == BA0_FCR0) + snd_cs4281_pokeBA0(chip, dma->regFCR, snd_cs4281_peekBA0(chip, dma->regFCR) & ~BA0_FCR_FEN); + /* Initialize FIFO */ + dma->valFCR = BA0_FCR_LS(dma->left_slot) | + BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(dma->fifo_offset); + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0)); + /* Activate FIFO again for FM playback */ + if (dma->regFCR == BA0_FCR0) + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | BA0_FCR_FEN); + /* Clear FIFO Status and Interrupt Control Register */ + snd_cs4281_pokeBA0(chip, dma->regFSIC, 0); +} + +static int snd_cs4281_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_cs4281_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4281_playback_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4281_mode(chip, dma, runtime, 0, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_capture_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4281_mode(chip, dma, runtime, 1, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_cs4281_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + + // printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies); + return runtime->buffer_size - + snd_cs4281_peekBA0(chip, dma->regDCC) - 1; +} + +static snd_pcm_hardware_t snd_cs4281_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (512*1024), + .period_bytes_min = 64, + .period_bytes_max = (512*1024), + .periods_min = 1, + .periods_max = 2, + .fifo_size = CS4281_FIFO_SIZE, +}; + +static snd_pcm_hardware_t snd_cs4281_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (512*1024), + .period_bytes_min = 64, + .period_bytes_max = (512*1024), + .periods_min = 1, + .periods_max = 2, + .fifo_size = CS4281_FIFO_SIZE, +}; + +static int snd_cs4281_playback_open(snd_pcm_substream_t * substream) +{ + cs4281_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma; + + dma = &chip->dma[0]; + dma->substream = substream; + dma->left_slot = 0; + dma->right_slot = 1; + runtime->private_data = dma; + runtime->hw = snd_cs4281_playback; + snd_pcm_set_sync(substream); + /* should be detected from the AC'97 layer, but it seems + that although CS4297A rev B reports 18-bit ADC resolution, + samples are 20-bit */ + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + return 0; +} + +static int snd_cs4281_capture_open(snd_pcm_substream_t * substream) +{ + cs4281_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma; + + dma = &chip->dma[1]; + dma->substream = substream; + dma->left_slot = 10; + dma->right_slot = 11; + runtime->private_data = dma; + runtime->hw = snd_cs4281_capture; + snd_pcm_set_sync(substream); + /* should be detected from the AC'97 layer, but it seems + that although CS4297A rev B reports 18-bit ADC resolution, + samples are 20-bit */ + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + return 0; +} + +static int snd_cs4281_playback_close(snd_pcm_substream_t * substream) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + + dma->substream = NULL; + return 0; +} + +static int snd_cs4281_capture_close(snd_pcm_substream_t * substream) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + + dma->substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_cs4281_playback_ops = { + .open = snd_cs4281_playback_open, + .close = snd_cs4281_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs4281_hw_params, + .hw_free = snd_cs4281_hw_free, + .prepare = snd_cs4281_playback_prepare, + .trigger = snd_cs4281_trigger, + .pointer = snd_cs4281_pointer, +}; + +static snd_pcm_ops_t snd_cs4281_capture_ops = { + .open = snd_cs4281_capture_open, + .close = snd_cs4281_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs4281_hw_params, + .hw_free = snd_cs4281_hw_free, + .prepare = snd_cs4281_capture_prepare, + .trigger = snd_cs4281_trigger, + .pointer = snd_cs4281_pointer, +}; + +static void snd_cs4281_pcm_free(snd_pcm_t *pcm) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_cs4281_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "CS4281"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +#define CS_VOL_MASK 0x1f + +static int snd_cs4281_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = CS_VOL_MASK; + return 0; +} + +static int snd_cs4281_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4281_t *chip = snd_kcontrol_chip(kcontrol); + int regL = (kcontrol->private_value >> 16) & 0xffff; + int regR = kcontrol->private_value & 0xffff; + int volL, volR; + + volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK); + volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK); + + ucontrol->value.integer.value[0] = volL; + ucontrol->value.integer.value[1] = volR; + return 0; +} + +static int snd_cs4281_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4281_t *chip = snd_kcontrol_chip(kcontrol); + int change = 0; + int regL = (kcontrol->private_value >> 16) & 0xffff; + int regR = kcontrol->private_value & 0xffff; + int volL, volR; + + volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK); + volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK); + + if (ucontrol->value.integer.value[0] != volL) { + volL = CS_VOL_MASK - (ucontrol->value.integer.value[0] & CS_VOL_MASK); + snd_cs4281_pokeBA0(chip, regL, volL); + change = 1; + } + if (ucontrol->value.integer.value[0] != volL) { + volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK); + snd_cs4281_pokeBA0(chip, regR, volR); + change = 1; + } + return change; +} + +static snd_kcontrol_new_t snd_cs4281_fm_vol = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Synth Playback Volume", + .info = snd_cs4281_info_volume, + .get = snd_cs4281_get_volume, + .put = snd_cs4281_put_volume, + .private_value = ((BA0_FMLVC << 16) | BA0_FMRVC), +}; + +static snd_kcontrol_new_t snd_cs4281_pcm_vol = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Stream Playback Volume", + .info = snd_cs4281_info_volume, + .get = snd_cs4281_get_volume, + .put = snd_cs4281_put_volume, + .private_value = ((BA0_PPLVC << 16) | BA0_PPRVC), +}; + +static void snd_cs4281_mixer_free_ac97(ac97_t *ac97) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + if (ac97->num) + chip->ac97_secondary = NULL; + else + chip->ac97 = NULL; +} + +static int __devinit snd_cs4281_mixer(cs4281_t * chip) +{ + snd_card_t *card = chip->card; + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs4281_ac97_write; + ac97.read = snd_cs4281_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs4281_mixer_free_ac97; + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0) + return err; + if (chip->dual_codec) { + ac97.num = 1; + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97_secondary)) < 0) + return err; + } + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0) + return err; + return 0; +} + + +/* + * proc interface + */ + +static void snd_cs4281_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return); + + snd_iprintf(buffer, "Cirrus Logic CS4281\n\n"); + snd_iprintf(buffer, "Spurious half IRQs : %u\n", chip->spurious_dhtc_irq); + snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq); +} + +static long snd_cs4281_BA0_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + + size = count; + if (file->f_pos + size > CS4281_BA0_SIZE) + size = (long)CS4281_BA0_SIZE - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = chip->ba0 + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static long snd_cs4281_BA1_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + + size = count; + if (file->f_pos + size > CS4281_BA1_SIZE) + size = (long)CS4281_BA1_SIZE - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = chip->ba1 + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = { + .read = snd_cs4281_BA0_read, +}; + +static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = { + .read = snd_cs4281_BA1_read, +}; + +static void __devinit snd_cs4281_proc_init(cs4281_t * chip) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(chip->card, "cs4281", &entry)) + snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read); + if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs4281_proc_ops_BA0; + entry->size = CS4281_BA0_SIZE; + } + if (! snd_card_proc_new(chip->card, "cs4281_BA1", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs4281_proc_ops_BA1; + entry->size = CS4281_BA1_SIZE; + } +} + +/* + * joystick support + */ + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + +typedef struct snd_cs4281_gameport { + struct gameport info; + cs4281_t *chip; +} cs4281_gameport_t; + +static void snd_cs4281_gameport_trigger(struct gameport *gameport) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + snd_assert(gp, return); + chip = snd_magic_cast(cs4281_t, gp->chip, return); + snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff); +} + +static unsigned char snd_cs4281_gameport_read(struct gameport *gameport) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + snd_assert(gp, return 0); + chip = snd_magic_cast(cs4281_t, gp->chip, return 0); + return snd_cs4281_peekBA0(chip, BA0_JSPT); +} + +#ifdef COOKED_MODE +static int snd_cs4281_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + unsigned js1, js2, jst; + + snd_assert(gp, return 0); + chip = snd_magic_cast(cs4281_t, gp->chip, return 0); + + js1 = snd_cs4281_peekBA0(chip, BA0_JSC1); + js2 = snd_cs4281_peekBA0(chip, BA0_JSC2); + jst = snd_cs4281_peekBA0(chip, BA0_JSPT); + + *buttons = (~jst >> 4) & 0x0F; + + axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; + axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; + axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; + axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; + + for(jst=0;jst<4;++jst) + if(axes[jst]==0xFFFF) axes[jst] = -1; + return 0; +} +#endif + +static int snd_cs4281_gameport_open(struct gameport *gameport, int mode) +{ + switch (mode) { +#ifdef COOKED_MODE + case GAMEPORT_MODE_COOKED: + return 0; +#endif + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + return 0; +} + +static void __devinit snd_cs4281_gameport(cs4281_t *chip) +{ + cs4281_gameport_t *gp; + gp = kmalloc(sizeof(*gp), GFP_KERNEL); + if (! gp) { + snd_printk(KERN_ERR "cannot allocate gameport area\n"); + return; + } + memset(gp, 0, sizeof(*gp)); + gp->info.open = snd_cs4281_gameport_open; + gp->info.read = snd_cs4281_gameport_read; + gp->info.trigger = snd_cs4281_gameport_trigger; +#ifdef COOKED_MODE + gp->info.cooked_read = snd_cs4281_gameport_cooked_read; +#endif + gp->chip = chip; + chip->gameport = gp; + + snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ? + snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); + gameport_register_port(&gp->info); +} + +#else +#define snd_cs4281_gameport(chip) /*NOP*/ +#endif /* CONFIG_GAMEPORT || CONFIG_GAMEPORT_MODULE */ + + +/* + + */ + +static int snd_cs4281_free(cs4281_t *chip) +{ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + if (chip->gameport) { + gameport_unregister_port(&chip->gameport->info); + kfree(chip->gameport); + } +#endif + if (chip->irq >= 0) + synchronize_irq(chip->irq); + + /* Mask interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff); + /* Stop the DLL Clock logic. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + /* Sound System Power Management - Turn Everything OFF */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); + /* PCI interface - D3 state */ + pci_set_power_state(chip->pci, 3); + + if (chip->ba0) + iounmap((void *) chip->ba0); + if (chip->ba1) + iounmap((void *) chip->ba1); + if (chip->ba0_res) { + release_resource(chip->ba0_res); + kfree_nocheck(chip->ba0_res); + } + if (chip->ba1_res) { + release_resource(chip->ba1_res); + kfree_nocheck(chip->ba1_res); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs4281_dev_free(snd_device_t *device) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, device->device_data, return -ENXIO); + return snd_cs4281_free(chip); +} + +static int snd_cs4281_chip_init(cs4281_t *chip, int can_schedule); /* defined below */ +#ifdef CONFIG_PM +static int snd_cs4281_set_power_state(snd_card_t *card, unsigned int power_state); +#endif + +static int __devinit snd_cs4281_create(snd_card_t * card, + struct pci_dev *pci, + cs4281_t ** rchip, + int dual_codec) +{ + cs4281_t *chip; + unsigned int tmp; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_cs4281_dev_free, + }; + + *rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = snd_magic_kcalloc(cs4281_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + pci_set_master(pci); + if (dual_codec < 0 || dual_codec > 3) { + snd_printk(KERN_ERR "invalid dual_codec option %d\n", dual_codec); + dual_codec = 0; + } + chip->dual_codec = dual_codec; + + if ((chip->ba0_res = request_mem_region(chip->ba0_addr, CS4281_BA0_SIZE, "CS4281 BA0")) == NULL) { + snd_cs4281_free(chip); + snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->ba0_addr, chip->ba0_addr + CS4281_BA0_SIZE - 1); + return -ENOMEM; + } + if ((chip->ba1_res = request_mem_region(chip->ba1_addr, CS4281_BA1_SIZE, "CS4281 BA1")) == NULL) { + snd_cs4281_free(chip); + snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->ba1_addr, chip->ba1_addr + CS4281_BA1_SIZE - 1); + return -ENOMEM; + } + if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) { + snd_cs4281_free(chip); + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + return -ENOMEM; + } + chip->irq = pci->irq; + + chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, CS4281_BA0_SIZE); + chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, CS4281_BA1_SIZE); + if (!chip->ba0 || !chip->ba1) { + snd_cs4281_free(chip); + return -ENOMEM; + } + + tmp = snd_cs4281_chip_init(chip, 1); + if (tmp) { + snd_cs4281_free(chip); + return tmp; + } + + snd_cs4281_proc_init(chip); + +#ifdef CONFIG_PM + card->set_power_state = snd_cs4281_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs4281_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int snd_cs4281_chip_init(cs4281_t *chip, int can_schedule) +{ + unsigned int tmp; + int timeout; + + tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); + if (tmp != BA0_CFLR_DEFAULT) { + snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT); + tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); + if (tmp != BA0_CFLR_DEFAULT) { + snd_printk(KERN_ERR "CFLR setup failed (0x%x)\n", tmp); + return -EIO; + } + } + + /* Set the 'Configuration Write Protect' register + * to 4281h. Allows vendor-defined configuration + * space between 0e4h and 0ffh to be written. */ + snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281); + + if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) { + snd_printk(KERN_ERR "SERC1 AC'97 check failed (0x%x)\n", tmp); + return -EIO; + } + if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) { + snd_printk(KERN_ERR "SERC2 AC'97 check failed (0x%x)\n", tmp); + return -EIO; + } + + /* Sound System Power Management */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN | + BA0_SSPM_PSRCEN | BA0_SSPM_JSEN | + BA0_SSPM_ACLEN | BA0_SSPM_FMEN); + + /* Serial Port Power Management */ + /* Blast the clock control register to zero so that the + * PLL starts out in a known state, and blast the master serial + * port control register to zero so that the serial ports also + * start out in a known state. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); + + /* Make ESYN go to zero to turn off + * the Sync pulse on the AC97 link. */ + snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0); + udelay(50); + + /* Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS4281 that uses the ARST# line + * for a reset. */ + snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); + udelay(50); + snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN); + snd_cs4281_delay(50000, can_schedule); + + if (chip->dual_codec) + snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN | BA0_SPMC_ASDI2E); + + /* + * Set the serial port timing configuration. + */ + snd_cs4281_pokeBA0(chip, BA0_SERMC, + (chip->dual_codec ? BA0_SERMC_TCID(chip->dual_codec) : BA0_SERMC_TCID(1)) | + BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE); + + /* + * Start the DLL Clock logic. + */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP); + snd_cs4281_delay(50000, can_schedule); + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP); + + /* + * Wait for the DLL ready signal from the clock logic. + */ + timeout = HZ; + do { + /* + * Read the AC97 status register to see if we've seen a CODEC + * signal from the AC97 codec. + */ + if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY) + goto __ok0; + snd_cs4281_delay_long(can_schedule); + } while (timeout-- > 0); + + snd_printk(KERN_ERR "DLLRDY not seen\n"); + return -EIO; + + __ok0: + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN); + + /* + * Wait for the codec ready signal from the AC97 codec. + */ + timeout = HZ; + do { + /* + * Read the AC97 status register to see if we've seen a CODEC + * signal from the AC97 codec. + */ + if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY) + goto __ok1; + snd_cs4281_delay_long(can_schedule); + } while (timeout-- > 0); + + snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS)); + return -EIO; + + __ok1: + if (chip->dual_codec) { + timeout = HZ; + do { + if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY) + goto __codec2_ok; + snd_cs4281_delay_long(can_schedule); + } while (timeout-- > 0); + snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n"); + chip->dual_codec = 0; + __codec2_ok: ; + } + + /* + * Assert the valid frame signal so that we can start sending commands + * to the AC97 codec. + */ + + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN); + + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the codec is pumping ADC data across the AC-link. + */ + + timeout = HZ; + do { + /* + * Read the input slot valid register and see if input slots 3 + * 4 are valid yet. + */ + if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) + goto __ok2; + snd_cs4281_delay_long(can_schedule); + } while (timeout-- > 0); + + snd_printk(KERN_ERR "never read ISV3 and ISV4 from AC'97\n"); + return -EIO; + + __ok2: + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 codec. + */ + snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4)); + + /* + * Initialize DMA structures + */ + for (tmp = 0; tmp < 4; tmp++) { + cs4281_dma_t *dma = &chip->dma[tmp]; + dma->regDBA = BA0_DBA0 + (tmp * 0x10); + dma->regDCA = BA0_DCA0 + (tmp * 0x10); + dma->regDBC = BA0_DBC0 + (tmp * 0x10); + dma->regDCC = BA0_DCC0 + (tmp * 0x10); + dma->regDMR = BA0_DMR0 + (tmp * 8); + dma->regDCR = BA0_DCR0 + (tmp * 8); + dma->regHDSR = BA0_HDSR0 + (tmp * 4); + dma->regFCR = BA0_FCR0 + (tmp * 4); + dma->regFSIC = BA0_FSIC0 + (tmp * 4); + dma->fifo_offset = tmp * CS4281_FIFO_SIZE; + snd_cs4281_pokeBA0(chip, dma->regFCR, + BA0_FCR_LS(31) | + BA0_FCR_RS(31) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(dma->fifo_offset)); + } + + chip->src_left_play_slot = 0; /* AC'97 left PCM playback (3) */ + chip->src_right_play_slot = 1; /* AC'97 right PCM playback (4) */ + chip->src_left_rec_slot = 10; /* AC'97 left PCM record (3) */ + chip->src_right_rec_slot = 11; /* AC'97 right PCM record (4) */ + + /* Activate wave playback FIFO for FM playback */ + chip->dma[0].valFCR = BA0_FCR_FEN | BA0_FCR_LS(0) | + BA0_FCR_RS(1) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(chip->dma[0].fifo_offset); + snd_cs4281_pokeBA0(chip, chip->dma[0].regFCR, chip->dma[0].valFCR); + snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | + (chip->src_right_play_slot << 8) | + (chip->src_left_rec_slot << 16) | + (chip->src_right_rec_slot << 24)); + + /* Initialize digital volume */ + snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0); + snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0); + + /* Enable IRQs */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); + /* Unmask interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~( + BA0_HISR_MIDI | + BA0_HISR_DMAI | + BA0_HISR_DMA(0) | + BA0_HISR_DMA(1) | + BA0_HISR_DMA(2) | + BA0_HISR_DMA(3))); + synchronize_irq(chip->irq); + + return 0; +} + +/* + * MIDI section + */ + +static void snd_cs4281_midi_reset(cs4281_t *chip) +{ + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST); + udelay(100); + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); +} + +static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr |= BA0_MIDCR_RXE; + chip->midi_input = substream; + if (!(chip->uartm & CS4281_MODE_OUTPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE); + chip->midi_input = NULL; + if (!(chip->uartm & CS4281_MODE_OUTPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS4281_MODE_INPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS4281_MODE_OUTPUT; + chip->midcr |= BA0_MIDCR_TXE; + chip->midi_output = substream; + if (!(chip->uartm & CS4281_MODE_INPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE); + chip->midi_output = NULL; + if (!(chip->uartm & CS4281_MODE_INPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS4281_MODE_OUTPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & BA0_MIDCR_RIE) == 0) { + chip->midcr |= BA0_MIDCR_RIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & BA0_MIDCR_RIE) { + chip->midcr &= ~BA0_MIDCR_RIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & BA0_MIDCR_TIE) == 0) { + chip->midcr |= BA0_MIDCR_TIE; + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while ((chip->midcr & BA0_MIDCR_TIE) && + (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->midcr &= ~BA0_MIDCR_TIE; + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte); + } + } + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & BA0_MIDCR_TIE) { + chip->midcr &= ~BA0_MIDCR_TIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_cs4281_midi_output = +{ + .open = snd_cs4281_midi_output_open, + .close = snd_cs4281_midi_output_close, + .trigger = snd_cs4281_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_cs4281_midi_input = +{ + .open = snd_cs4281_midi_input_open, + .close = snd_cs4281_midi_input_close, + .trigger = snd_cs4281_midi_input_trigger, +}; + +static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "CS4281"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, dev_id, return); + unsigned int status, dma, val; + cs4281_dma_t *cdma; + + if (chip == NULL) + return; + status = snd_cs4281_peekBA0(chip, BA0_HISR); + if ((status & 0x7fffffff) == 0) { + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); + return; + } + + if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) { + for (dma = 0; dma < 4; dma++) + if (status & BA0_HISR_DMA(dma)) { + cdma = &chip->dma[dma]; + spin_lock(&chip->reg_lock); + /* ack DMA IRQ */ + val = snd_cs4281_peekBA0(chip, cdma->regHDSR); + /* workaround, sometimes CS4281 acknowledges */ + /* end or middle transfer position twice */ + cdma->frag++; + if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) { + cdma->frag--; + chip->spurious_dhtc_irq++; + spin_unlock(&chip->reg_lock); + continue; + } + if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) { + cdma->frag--; + chip->spurious_dtc_irq++; + spin_unlock(&chip->reg_lock); + continue; + } + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(cdma->substream); + } + } + + if ((status & BA0_HISR_MIDI) && chip->rmidi) { + unsigned char c; + + spin_lock(&chip->reg_lock); + while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) { + c = snd_cs4281_peekBA0(chip, BA0_MIDRP); + if ((chip->midcr & BA0_MIDCR_RIE) == 0) + continue; + snd_rawmidi_receive(chip->midi_input, &c, 1); + } + while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { + if ((chip->midcr & BA0_MIDCR_TIE) == 0) + break; + if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { + chip->midcr &= ~BA0_MIDCR_TIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + break; + } + snd_cs4281_pokeBA0(chip, BA0_MIDWP, c); + } + spin_unlock(&chip->reg_lock); + } + + /* EOI to the PCI part... reenables interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); +} + + +static int __devinit snd_cs4281_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + cs4281_t *chip; + opl3_t *opl3; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_cs4281_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_create(card, + (chip->ba0 + BA0_B0AP) >> 2, + (chip->ba0 + BA0_B1AP) >> 2, + OPL3_HW_OPL3_CS4281, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4281_gameport(chip); + strcpy(card->driver, "CS4281"); + strcpy(card->shortname, "Cirrus Logic CS4281"); + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, + chip->ba0_addr, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_cs4281_remove(struct pci_dev *pci) +{ + cs4281_t *chip = pci_get_drvdata(pci); + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +/* + * Power Management + */ +#ifdef CONFIG_PM + +static int saved_regs[SUSPEND_REGISTERS] = { + BA0_JSCTL, + BA0_GPIOR, + BA0_SSCR, + BA0_MIDCR, + BA0_SRCSA, + BA0_PASR, + BA0_CASR, + BA0_DACSR, + BA0_ADCSR, + BA0_FMLVC, + BA0_FMRVC, + BA0_PPLVC, + BA0_PPRVC, +}; + +#define number_of(array) (sizeof(array) / sizeof(array[0])) + +#define CLKCR1_CKRA 0x00010000L + +static void cs4281_suspend(cs4281_t *chip) +{ + snd_card_t *card = chip->card; + u32 ulCLK; + unsigned int i; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + snd_pcm_suspend_all(chip->pcm); + + ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); + ulCLK |= CLKCR1_CKRA; + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); + + /* Disable interrupts. */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM); + + /* remember the status registers */ + for (i = 0; i < number_of(saved_regs); i++) + if (saved_regs[i]) + chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]); + + /* Turn off the serial ports. */ + snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); + + /* Power off FM, Joystick, AC link, */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); + + /* DLL off. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + + /* AC link off. */ + snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); + + ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); + ulCLK &= ~CLKCR1_CKRA; + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void cs4281_resume(cs4281_t *chip) +{ + snd_card_t *card = chip->card; + unsigned int i; + u32 ulCLK; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + pci_enable_device(chip->pci); + + ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); + ulCLK |= CLKCR1_CKRA; + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); + + snd_cs4281_chip_init(chip, 0); + + /* restore the status registers */ + for (i = 0; i < number_of(saved_regs); i++) + if (saved_regs[i]) + snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]); + + if (chip->ac97) + snd_ac97_resume(chip->ac97); + if (chip->ac97_secondary) + snd_ac97_resume(chip->ac97_secondary); + + ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); + ulCLK &= ~CLKCR1_CKRA; + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_cs4281_suspend(struct pci_dev *dev, u32 state) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return -ENXIO); + cs4281_suspend(chip); + return 0; +} +static int snd_cs4281_resume(struct pci_dev *dev) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return -ENXIO); + cs4281_resume(chip); + return 0; +} +#else +static void snd_cs4281_suspend(struct pci_dev *dev) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return); + cs4281_suspend(chip); +} +static void snd_cs4281_resume(struct pci_dev *dev) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return); + cs4281_resume(chip); +} +#endif + +/* callback */ +static int snd_cs4281_set_power_state(snd_card_t *card, unsigned int power_state) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + cs4281_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + cs4281_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver driver = { + .name = "CS4281", + .id_table = snd_cs4281_ids, + .probe = snd_cs4281_probe, + .remove = __devexit_p(snd_cs4281_remove), +#ifdef CONFIG_PM + .suspend = snd_cs4281_suspend, + .resume = snd_cs4281_resume, +#endif +}; + +static int __init alsa_card_cs4281_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "CS4281 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cs4281_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs4281_init) +module_exit(alsa_card_cs4281_exit) + +#ifndef MODULE + +/* format is: snd-cs4281=enable,index,id */ + +static int __init alsa_card_cs4281_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs4281=", alsa_card_cs4281_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/Makefile linux/sound/pci/cs46xx/Makefile --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,21 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _cs46xx.o + +list-multi := snd-cs46xx.o + +snd-cs46xx-objs := cs46xx.o cs46xx_lib.o +ifeq ($(CONFIG_SND_CS46XX_NEW_DSP),y) + snd-cs46xx-objs += dsp_spos.o dsp_spos_scb_lib.o +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o + +include $(TOPDIR)/Rules.make + +snd-cs46xx.o: $(snd-cs46xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs46xx-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx.c linux/sound/pci/cs46xx/cs46xx.c --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/cs46xx.c 2002-11-04 02:56:20.000000000 -0700 @@ -0,0 +1,244 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - sometimes the sound is metallic and sibilant, unloading and + reloading the module may solve this. +*/ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Cirrus Logic,Sound Fusion (CS4280)}," + "{Cirrus Logic,Sound Fusion (CS4610)}," + "{Cirrus Logic,Sound Fusion (CS4612)}," + "{Cirrus Logic,Sound Fusion (CS4615)}," + "{Cirrus Logic,Sound Fusion (CS4622)}," + "{Cirrus Logic,Sound Fusion (CS4624)}," + "{Cirrus Logic,Sound Fusion (CS4630)}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable CS46xx soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(external_amp, "Force to enable external amplifer."); +MODULE_PARM_SYNTAX(external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(thinkpad, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control."); +MODULE_PARM_SYNTAX(thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(mmap_valid, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mmap_valid, "Support OSS mmap."); +MODULE_PARM_SYNTAX(mmap_valid, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +static struct pci_device_id snd_cs46xx_ids[] __devinitdata = { + { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */ + { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4612 */ + { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4615 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids); + +static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + cs46xx_t *chip; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if ((err = snd_cs46xx_create(card, pci, + external_amp[dev], thinkpad[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + chip->accept_valid = mmap_valid[dev]; + if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef CONFIG_SND_CS46XX_NEW_DSP + if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) { + snd_card_free(card); + return err; + } +#endif + if ((err = snd_cs46xx_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_start_dsp(chip)) < 0) { + snd_card_free(card); + return err; + } + + + snd_cs46xx_gameport(chip); + + strcpy(card->driver, "CS46xx"); + strcpy(card->shortname, "Sound Fusion CS46xx"); + sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", + card->shortname, + chip->ba0_addr, + chip->ba1_addr, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_cs46xx_suspend(struct pci_dev *pci, u32 state) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO); + snd_cs46xx_suspend(chip); + return 0; +} +static int snd_card_cs46xx_resume(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO); + snd_cs46xx_resume(chip); + return 0; +} +#else +static void snd_card_cs46xx_suspend(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + snd_cs46xx_suspend(chip); +} +static void snd_card_cs46xx_resume(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + snd_cs46xx_resume(chip); +} +#endif +#endif + +static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Sound Fusion CS46xx", + .id_table = snd_cs46xx_ids, + .probe = snd_card_cs46xx_probe, + .remove = __devexit_p(snd_card_cs46xx_remove), +#ifdef CONFIG_PM + .suspend = snd_card_cs46xx_suspend, + .resume = snd_card_cs46xx_resume, +#endif +}; + +static int __init alsa_card_cs46xx_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Sound Fusion CS46xx soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cs46xx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs46xx_init) +module_exit(alsa_card_cs46xx_exit) + +#ifndef MODULE + +/* format is: snd-cs46xx=enable,index,id */ + +static int __init alsa_card_cs46xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs46xx=", alsa_card_cs46xx_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx_image.h linux/sound/pci/cs46xx/cs46xx_image.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx_image.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/cs46xx_image.h 2002-08-01 06:30:52.000000000 -0600 @@ -0,0 +1,3468 @@ +struct BA1struct { + struct { + unsigned long offset; + unsigned long size; + } memory[BA1_MEMORY_COUNT]; + u32 map[BA1_DWORD_SIZE]; +}; + + +static struct BA1struct BA1Struct = { +{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }}, +{0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00200040,0x00008010,0x00000000, +0x00000000,0x80000001,0x00000001,0x00060000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00900080,0x00000173,0x00000000, +0x00000000,0x00000010,0x00800000,0x00900000, +0xf2c0000f,0x00000200,0x00000000,0x00010600, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x330300c2, +0x06000000,0x00000000,0x80008000,0x80008000, +0x3fc0000f,0x00000301,0x00010400,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00b00000,0x00d0806d,0x330480c3, +0x04800000,0x00000001,0x00800001,0x0000ffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x066a0600,0x06350070,0x0000929d,0x929d929d, +0x00000000,0x0000735a,0x00000600,0x00000000, +0x929d735a,0x8734abfe,0x00010000,0x735a735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000804f,0x000000c3, +0x05000000,0x00a00010,0x00000000,0x80008000, +0x00000000,0x00000000,0x00000700,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000080,0x00a00000,0x0000809a,0x000000c2, +0x07400000,0x00000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x000107a0, +0x00c80028,0x000000c2,0x06800000,0x00000000, +0x06e00080,0x00300000,0x000080bb,0x000000c9, +0x07a00000,0x04000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x00000780, +0x00c80028,0x000000c5,0xff800000,0x00000000, +0x00640080,0x00c00000,0x00008197,0x000000c9, +0x07800000,0x04000000,0x80008000,0xffffffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000805e,0x000000c1, +0x00000000,0x00800000,0x80008000,0x80008000, +0x00020000,0x0000ffff,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x929d0600,0x929d929d,0x929d929d,0x929d0000, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x00100635,0x060b013f,0x00000004, +0x00000001,0x007a0002,0x00000000,0x066e0610, +0x0105929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, +0x00000000,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x00000000,0x06400136, +0x0000270f,0x00010000,0x007a0000,0x00000000, +0x068e0645,0x0105929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0xa431ac75,0x0001735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x735a0100,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00010004, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00001705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00009705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00011705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00019705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00021705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00029705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00031705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00039705,0x00001400,0x000a411e,0x00001003, +0x000fe19e,0x00001003,0x0009c730,0x00001003, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00009705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00011705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00019705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00021705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00029705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00031705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00039705,0x00001400,0x000a211e,0x00001003, +0x0000a730,0x00001008,0x000e2730,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x00000000,0x00000000,0x000f619c,0x00001003, +0x0007f801,0x000c0000,0x00000037,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0000373c,0x00001000,0x00000000,0x00000000, +0x000ee19c,0x00001003,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000273c,0x00001000, +0x00000033,0x00001000,0x000e679e,0x00001003, +0x00007705,0x00001400,0x000ac71e,0x00001003, +0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000a730,0x00001003, +0x00000033,0x00001000,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x000c0000, +0x00000032,0x00001000,0x0000273d,0x00001000, +0x0004a730,0x00001003,0x00000f41,0x00097140, +0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +0x00000000,0x00000000,0x0001bf05,0x0003fc40, +0x00002725,0x000aa400,0x00013705,0x00093a00, +0x0000002e,0x0009d6c0,0x00038630,0x00001004, +0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, +0x00000000,0x000c70e0,0x0007d182,0x0002c640, +0x00000630,0x00001004,0x000799b8,0x0002c6c0, +0x00031705,0x00092240,0x00039f05,0x000932c0, +0x0003520a,0x00000000,0x00040731,0x0000100b, +0x00010705,0x000b20c0,0x00000000,0x000eba44, +0x00032108,0x000c60c4,0x00065208,0x000c2917, +0x000406b0,0x00001007,0x00012f05,0x00036880, +0x0002818e,0x000c0000,0x0004410a,0x00000000, +0x00040630,0x00001007,0x00029705,0x000c0000, +0x00000000,0x00000000,0x00003fc1,0x0003fc40, +0x000037c1,0x00091b40,0x00003fc1,0x000911c0, +0x000037c1,0x000957c0,0x00003fc1,0x000951c0, +0x000037c1,0x00000000,0x00003fc1,0x000991c0, +0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, +0x000037c1,0x00000000,0x0001ccc1,0x000915c0, +0x0001c441,0x0009d800,0x0009cdc1,0x00091240, +0x0001c541,0x00091d00,0x0009cfc1,0x00095240, +0x0001c741,0x00095c80,0x000e8ca9,0x00099240, +0x000e85ad,0x00095640,0x00069ca9,0x00099d80, +0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, +0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, +0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, +0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, +0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, +0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, +0x0006fca9,0x00002500,0x000fb208,0x000c59a0, +0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, +0x000683ad,0x00095241,0x00020f05,0x000991c1, +0x00000000,0x00000000,0x00086f88,0x00001000, +0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, +0x0009de81,0x000bd300,0x0009d601,0x000b1700, +0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, +0x000a0f81,0x000bd740,0x00020701,0x000b5c80, +0x000a1681,0x000b97c0,0x00021601,0x00002500, +0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, +0x00021681,0x00002d00,0x00020f81,0x000bd800, +0x000a0701,0x000b5bc0,0x00021601,0x00003500, +0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, +0x00021681,0x00003d00,0x00020f81,0x000b1d00, +0x000a0701,0x000b1fc0,0x00021601,0x00020500, +0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, +0x00021681,0x00020d00,0x00020f81,0x000bde80, +0x000a0701,0x000bdfc0,0x00021601,0x00021500, +0x00020f81,0x000b9341,0x00020701,0x000b53c1, +0x00021681,0x00021d00,0x000a0f81,0x000d0380, +0x0000b601,0x000b15c0,0x00007b01,0x00000000, +0x00007b81,0x000bd1c0,0x00007b01,0x00000000, +0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, +0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, +0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, +0x0007e488,0x000d7e45,0x00000000,0x000d7a44, +0x0007e48a,0x00000000,0x00011f05,0x00084080, +0x00000000,0x00000000,0x00001705,0x000b3540, +0x00008a01,0x000bf040,0x00007081,0x000bb5c0, +0x00055488,0x00000000,0x0000d482,0x0003fc40, +0x0003fc88,0x00000000,0x0001e401,0x000b3a00, +0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, +0x000c86b0,0x00001007,0x00008281,0x000bb240, +0x0000b801,0x000b7140,0x00007888,0x00000000, +0x0000073c,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x00055288,0x000c555c, +0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +0x0000fa88,0x00000000,0x00000032,0x00001000, +0x0000073d,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x0008c01c,0x00001003, +0x00002705,0x00001008,0x0008b201,0x000c1392, +0x0000ba01,0x00000000,0x00008731,0x00001400, +0x0004c108,0x000fe0c4,0x00057488,0x00000000, +0x000a6388,0x00001001,0x0008b334,0x000bc141, +0x0003020e,0x00000000,0x000886b0,0x00001008, +0x00003625,0x000c5dfa,0x000a638a,0x00001001, +0x0008020e,0x00001002,0x0008a6b0,0x00001008, +0x0007f301,0x00000000,0x00000000,0x00000000, +0x00002725,0x000a8c40,0x000000ae,0x00000000, +0x000d8630,0x00001008,0x00000000,0x000c74e0, +0x0007d182,0x0002d640,0x000a8630,0x00001008, +0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5, +0x0007420a,0x000c0000,0x00062208,0x000c4117, +0x00070630,0x00001009,0x00000000,0x000c0000, +0x0001022e,0x00000000,0x0003a630,0x00001009, +0x00000000,0x000c0000,0x00000036,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x0002a730,0x00001008,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0002a730,0x00001008, +0x00000033,0x00001000,0x0002a705,0x00001008, +0x00007a01,0x000c0000,0x000e6288,0x000d550a, +0x0006428a,0x00000000,0x00060730,0x0000100a, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0007aab0,0x00034880,0x00078fb0,0x0000100b, +0x00057488,0x00000000,0x00033b94,0x00081140, +0x000183ae,0x00000000,0x000786b0,0x0000100b, +0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +0x00042731,0x00001003,0x0007aab0,0x00034880, +0x00048fb0,0x0000100a,0x00057488,0x00000000, +0x00033b94,0x00081140,0x000183ae,0x00000000, +0x000806b0,0x0000100b,0x00022f05,0x00000000, +0x00007401,0x00091140,0x00048f05,0x000951c0, +0x00042731,0x00001003,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x000fe19e,0x00001003,0x00000000,0x00000000, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00000f41,0x00097140,0x0000a841,0x0009b240, +0x0000a0c1,0x0009f040,0x0001c641,0x00093540, +0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44, +0x00055208,0x00000000,0x00010705,0x000a2880, +0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5, +0x0004ef0a,0x000c0000,0x00012f05,0x00036880, +0x00065308,0x000c2997,0x000d86b0,0x0000100a, +0x0004410a,0x000d40c7,0x00000000,0x00000000, +0x00080730,0x00001004,0x00056f0a,0x000ea105, +0x00000000,0x00000000,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x0000273d,0x00001000,0x00000000,0x000eba44, +0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x00000000,0x000e5084, +0x00000000,0x000eba44,0x00087401,0x000e4782, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x0007c108,0x000c0000, +0x0007e721,0x000bed40,0x00005f25,0x000badc0, +0x0003ba97,0x000beb80,0x00065590,0x000b2e00, +0x00033217,0x00003ec0,0x00065590,0x000b8e40, +0x0003ed80,0x000491c0,0x00073fb0,0x00074c80, +0x000283a0,0x0000100c,0x000ee388,0x00042970, +0x00008301,0x00021ef2,0x000b8f14,0x0000000f, +0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6, +0x000032ac,0x000c3916,0x0004edc2,0x00074c80, +0x00078898,0x00001000,0x00038894,0x00000032, +0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6, +0x0004edc2,0x000c1956,0x0000722c,0x00034a00, +0x00041705,0x0009ed40,0x00058730,0x00001400, +0x000d7488,0x000c3a00,0x00048f05,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000} + }; diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx_lib.c linux/sound/pci/cs46xx/cs46xx_lib.c --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/cs46xx_lib.c 2003-03-06 12:29:21.000000000 -0700 @@ -0,0 +1,3904 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * Cirrus Logic, Inc. + * Routines for control of Cirrus Logic CS461x chips + * + * KNOWN BUGS: + * - Sometimes the SPDIF input DSP tasks get's unsynchronized + * and the SPDIF get somewhat "distorcionated", or/and left right channel + * are swapped. To get around this problem when it happens, mute and unmute + * the SPDIF input mixer controll. + * - On the Hercules Game Theater XP the amplifier are sometimes turned + * off on inadecuate moments which causes distorcions on sound. + * + * TODO: + * - Secondary CODEC on some soundcards + * - SPDIF input support for other sample rates then 48khz + * - Posibility to mix the SPDIF output with analog sources. + * - PCM channels for Center and LFE on secondary codec + * + * NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which + * is default configuration), no SPDIF, no secondary codec, no + * multi channel PCM. But known to work. + * + * FINALLY: A credit to the developers Tom and Jordan + * at Cirrus for have helping me out with the DSP, however we + * still dont have sufficient documentation and technical + * references to be able to implement all fancy feutures + * supported by the cs46xx DSP's. + * Benny + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "cs46xx_lib.h" +#include "dsp_spos.h" + +static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip, + unsigned short reg, + int codec_index) +{ + int count; + unsigned short result,tmp; + u32 offset = 0; + snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) || + (codec_index == CS46XX_SECONDARY_CODEC_INDEX), + return -EINVAL); + + if (codec_index == CS46XX_SECONDARY_CODEC_INDEX) + offset = CS46XX_SECONDARY_CODEC_OFFSET; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write7---55 + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset); + + tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL); + if ((tmp & ACCTL_VFRM) == 0) { + snd_printk(KERN_WARNING "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM ); + mdelay(50); + tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, tmp | ACCTL_ESYN | ACCTL_VFRM ); + + } + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0); + if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) { + snd_cs46xx_pokeBA0(chip, BA0_ACCTL,/* clear ACCTL_DCV */ ACCTL_CRW | + ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | + ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + } else { + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC | + ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + } + + /* + * Wait for the read to occur. + */ + for (count = 0; count < 1000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) + goto ok1; + } + + snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); + result = 0xffff; + goto end; + + ok1: + /* + * Wait for the valid status bit to go active. + */ + for (count = 0; count < 100; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS + offset) & ACSTS_VSTS) + goto ok2; + udelay(10); + } + + snd_printk("AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n", codec_index, reg); + result = 0xffff; + goto end; + + ok2: + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ +#if 0 + printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg, + snd_cs46xx_peekBA0(chip, BA0_ACSDA), + snd_cs46xx_peekBA0(chip, BA0_ACCAD)); +#endif + + //snd_cs46xx_peekBA0(chip, BA0_ACCAD); + result = snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset); + end: + return result; +} + +static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97, + unsigned short reg) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return -ENXIO); + unsigned short val; + int codec_index = -1; + + /* UGGLY: nr_ac97_codecs == 0 primery codec detection is in progress */ + if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] || chip->nr_ac97_codecs == 0) + codec_index = CS46XX_PRIMARY_CODEC_INDEX; + /* UGGLY: nr_ac97_codecs == 1 secondary codec detection is in progress */ + else if (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] || chip->nr_ac97_codecs == 1) + codec_index = CS46XX_SECONDARY_CODEC_INDEX; + else + snd_assert(0, return 0xffff); + chip->active_ctrl(chip, 1); + val = snd_cs46xx_codec_read(chip, reg, codec_index); + chip->active_ctrl(chip, -1); + return val; +} + + +static void snd_cs46xx_codec_write(cs46xx_t *chip, + unsigned short reg, + unsigned short val, + int codec_index) +{ + int count; + + snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) || + (codec_index == CS46XX_SECONDARY_CODEC_INDEX), + return); + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCAD , reg); + snd_cs46xx_pokeBA0(chip, BA0_ACCDA , val); + snd_cs46xx_peekBA0(chip, BA0_ACCTL); + + if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) { + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, /* clear ACCTL_DCV */ ACCTL_VFRM | + ACCTL_ESYN | ACCTL_RSTN); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | + ACCTL_ESYN | ACCTL_RSTN); + } else { + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC | + ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + } + + for (count = 0; count < 4000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) { + return; + } + } + snd_printk("AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val); +} + +static void snd_cs46xx_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + int codec_index = -1; + + /* UGGLY: nr_ac97_codecs == 0 primery codec detection is in progress */ + if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] || chip->nr_ac97_codecs == 0) + codec_index = CS46XX_PRIMARY_CODEC_INDEX; + /* UGGLY: nr_ac97_codecs == 1 secondary codec detection is in progress */ + else if (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] || chip->nr_ac97_codecs == 1) + codec_index = CS46XX_SECONDARY_CODEC_INDEX; + else + snd_assert(0,return); + + chip->active_ctrl(chip, 1); + snd_cs46xx_codec_write(chip, reg, val, codec_index); + chip->active_ctrl(chip, -1); +} + + +/* + * Chip initialization + */ + +int snd_cs46xx_download(cs46xx_t *chip, + u32 *src, + unsigned long offset, + unsigned long len) +{ + unsigned long dst; + unsigned int bank = offset >> 16; + offset = offset & 0xffff; + + snd_assert(!(offset & 3) && !(len & 3), return -EINVAL); + dst = chip->region.idx[bank+1].remap_addr + offset; + len /= sizeof(u32); + + /* writel already converts 32-bit value to right endianess */ + while (len-- > 0) { + writel(*src++, dst); + dst += sizeof(u32); + } + return 0; +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + +#include "imgs/cwc4630.h" +#include "imgs/cwcasync.h" +#include "imgs/cwcsnoop.h" +#include "imgs/cwcbinhack.h" +#include "imgs/cwcdma.h" + +int snd_cs46xx_clear_BA1(cs46xx_t *chip, + unsigned long offset, + unsigned long len) +{ + unsigned long dst; + unsigned int bank = offset >> 16; + offset = offset & 0xffff; + + snd_assert(!(offset & 3) && !(len & 3), return -EINVAL); + dst = chip->region.idx[bank+1].remap_addr + offset; + len /= sizeof(u32); + + /* writel already converts 32-bit value to right endianess */ + while (len-- > 0) { + writel(0, dst); + dst += sizeof(u32); + } + return 0; +} + +#else /* old DSP image */ + +#include "cs46xx_image.h" + +int snd_cs46xx_download_image(cs46xx_t *chip) +{ + int idx, err; + unsigned long offset = 0; + + for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) { + if ((err = snd_cs46xx_download(chip, + &BA1Struct.map[offset], + BA1Struct.memory[idx].offset, + BA1Struct.memory[idx].size)) < 0) + return err; + offset += BA1Struct.memory[idx].size >> 2; + } + return 0; +} +#endif /* CONFIG_SND_CS46XX_NEW_DSP */ + +/* + * Chip reset + */ + +static void snd_cs46xx_reset(cs46xx_t *chip) +{ + int idx; + + /* + * Write the reset bit of the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP); + + /* + * Write the control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN); + + /* + * Clear the trap registers. + */ + for (idx = 0; idx < 8; idx++) { + snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx); + snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF); + } + snd_cs46xx_poke(chip, BA1_DREG, 0); + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + snd_cs46xx_poke(chip, BA1_FRMT, 0xadf); +} + +static int cs46xx_wait_for_fifo(cs46xx_t * chip,int retry_timeout) +{ + u32 i, status = 0; + /* + * Make sure the previous FIFO write operation has completed. + */ + for(i = 0; i < 50; i++){ + status = snd_cs46xx_peekBA0(chip, BA0_SERBST); + + if( !(status & SERBST_WBSY) ) + break; + + mdelay(retry_timeout); + } + + if(status & SERBST_WBSY) { + snd_printk( KERN_ERR "cs46xx: failure waiting for FIFO command to complete\n"); + + return -EINVAL; + } + + return 0; +} + +static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip) +{ + int idx, powerdown = 0; + unsigned int tmp; + + /* + * See if the devices are powered down. If so, we must power them up first + * or they will not respond. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1); + if (!(tmp & CLKCR1_SWCE)) { + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE); + powerdown = 1; + } + + /* + * We want to clear out the serial port FIFOs so we don't end up playing + * whatever random garbage happens to be in them. We fill the sample FIFOS + * with zero (silence). + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0); + + /* + * Fill all 256 sample FIFO locations. + */ + for (idx = 0; idx < 0xFF; idx++) { + /* + * Make sure the previous FIFO write operation has completed. + */ + if (cs46xx_wait_for_fifo(chip,1)) { + snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx); + + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); + + break; + } + /* + * Write the serial port FIFO index. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx); + /* + * Tell the serial port to load the new value into the FIFO location. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC); + } + /* + * Now, if we powered up the devices, then power them back down again. + * This is kinda ugly, but should never happen. + */ + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); +} + +static void snd_cs46xx_proc_start(cs46xx_t *chip) +{ + int cnt; + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + snd_cs46xx_poke(chip, BA1_FRMT, 0xadf); + /* + * Turn on the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); + /* + * Wait until the run at frame bit resets itself in the SP control + * register. + */ + for (cnt = 0; cnt < 25; cnt++) { + udelay(50); + if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)) + break; + } + + if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR) + snd_printk("SPCR_RUNFR never reset\n"); +} + +static void snd_cs46xx_proc_stop(cs46xx_t *chip) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, 0); +} + +/* + * Sample rate routines + */ + +#define GOF_PER_SEC 200 + +static void snd_cs46xx_set_play_sample_rate(cs46xx_t *chip, unsigned int rate) +{ + unsigned long flags; + unsigned int tmp1, tmp2; + unsigned int phiIncr; + unsigned int correctionPerGOF, correctionPerSec; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M + * GOF_PER_SEC * correctionPerGOF + * + * i.e. + * + * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + */ + tmp1 = rate << 16; + phiIncr = tmp1 / 48000; + tmp1 -= phiIncr * 48000; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / 48000; + phiIncr += tmp2; + tmp1 -= tmp2 * 48000; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + + /* + * Fill in the SampleRateConverter control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_PSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + snd_cs46xx_poke(chip, BA1_PPI, phiIncr); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs46xx_set_capture_sample_rate(cs46xx_t *chip, unsigned int rate) +{ + unsigned long flags; + unsigned int phiIncr, coeffIncr, tmp1, tmp2; + unsigned int correctionPerGOF, correctionPerSec, initialDelay; + unsigned int frameGroupLength, cnt; + + /* + * We can only decimate by up to a factor of 1/9th the hardware rate. + * Correct the value if an attempt is made to stray outside that limit. + */ + if ((rate * 9) < 48000) + rate = 48000 / 9; + + /* + * We can not capture at at rate greater than the Input Rate (48000). + * Return an error if an attempt is made to stray outside that limit. + */ + if (rate > 48000) + rate = 48000; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * coeffIncr = -floor((Fs,out * 2^23) / Fs,in) + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - + * GOF_PER_SEC * correctionPerGOF + * initialDelay = ceil((24 * Fs,in) / Fs,out) + * + * i.e. + * + * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) + * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) + */ + + tmp1 = rate << 16; + coeffIncr = tmp1 / 48000; + tmp1 -= coeffIncr * 48000; + tmp1 <<= 7; + coeffIncr <<= 7; + coeffIncr += tmp1 / 48000; + coeffIncr ^= 0xFFFFFFFF; + coeffIncr++; + tmp1 = 48000 << 16; + phiIncr = tmp1 / rate; + tmp1 -= phiIncr * rate; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / rate; + phiIncr += tmp2; + tmp1 -= tmp2 * rate; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + initialDelay = ((48000 * 24) + rate - 1) / rate; + + /* + * Fill in the VariDecimate control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_CSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + snd_cs46xx_poke(chip, BA1_CCI, coeffIncr); + snd_cs46xx_poke(chip, BA1_CD, + (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); + snd_cs46xx_poke(chip, BA1_CPI, phiIncr); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* + * Figure out the frame group length for the write back task. Basically, + * this is just the factors of 24000 (2^6*3*5^3) that are not present in + * the output sample rate. + */ + frameGroupLength = 1; + for (cnt = 2; cnt <= 64; cnt *= 2) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 2; + } + if (((rate / 3) * 3) != rate) { + frameGroupLength *= 3; + } + for (cnt = 5; cnt <= 125; cnt *= 5) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 5; + } + + /* + * Fill in the WriteBack control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength); + snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength)); + snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF); + snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000)); + snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * PCM part + */ + +static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream) +{ + /* cs46xx_t *chip = snd_pcm_substream_chip(substream); */ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_pcm_t * cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - cpcm->appl_ptr; + int buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + cpcm->sw_ready += diff * (1 << cpcm->shift); + cpcm->appl_ptr = appl_ptr; + } + while (cpcm->hw_ready < buffer_size && + cpcm->sw_ready > 0) { + size_t hw_to_end = buffer_size - cpcm->hw_data; + size_t sw_to_end = cpcm->sw_bufsize - cpcm->sw_data; + size_t bytes = buffer_size - cpcm->hw_ready; + if (cpcm->sw_ready < (int)bytes) + bytes = cpcm->sw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + memcpy(cpcm->hw_area + cpcm->hw_data, + runtime->dma_area + cpcm->sw_data, + bytes); + cpcm->hw_data += bytes; + if ((int)cpcm->hw_data == buffer_size) + cpcm->hw_data = 0; + cpcm->sw_data += bytes; + if (cpcm->sw_data == cpcm->sw_bufsize) + cpcm->sw_data = 0; + cpcm->hw_ready += bytes; + cpcm->sw_ready -= bytes; + } + return 0; +} + +static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - chip->capt.appl_ptr; + int buffer_size = runtime->period_size * CS46XX_FRAGS << chip->capt.shift; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + chip->capt.sw_ready -= diff * (1 << chip->capt.shift); + chip->capt.appl_ptr = appl_ptr; + } + while (chip->capt.hw_ready > 0 && + chip->capt.sw_ready < (int)chip->capt.sw_bufsize) { + size_t hw_to_end = buffer_size - chip->capt.hw_data; + size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data; + size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready; + if (chip->capt.hw_ready < (int)bytes) + bytes = chip->capt.hw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + memcpy(runtime->dma_area + chip->capt.sw_data, + chip->capt.hw_area + chip->capt.hw_data, + bytes); + chip->capt.hw_data += bytes; + if ((int)chip->capt.hw_data == buffer_size) + chip->capt.hw_data = 0; + chip->capt.sw_data += bytes; + if (chip->capt.sw_data == chip->capt.sw_bufsize) + chip->capt.sw_data = 0; + chip->capt.hw_ready -= bytes; + chip->capt.sw_ready += bytes; + } + return 0; +} + +static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); + snd_assert (cpcm->pcm_channel,return -ENXIO); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2); +#else + ptr = snd_cs46xx_peek(chip, BA1_PBA); +#endif + ptr -= cpcm->hw_addr; + return ptr >> cpcm->shift; +} + +static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); + ssize_t bytes; + int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << cpcm->shift; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_assert (cpcm->pcm_channel,return -ENXIO); + ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2); +#else + ptr = snd_cs46xx_peek(chip, BA1_PBA); +#endif + ptr -= cpcm->hw_addr; + + bytes = ptr - cpcm->hw_io; + + if (bytes < 0) + bytes += buffer_size; + cpcm->hw_io = ptr; + cpcm->hw_ready -= bytes; + cpcm->sw_io += bytes; + if (cpcm->sw_io >= cpcm->sw_bufsize) + cpcm->sw_io -= cpcm->sw_bufsize; + snd_cs46xx_playback_transfer(substream); + return cpcm->sw_io >> cpcm->shift; +} + +static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + return ptr >> chip->capt.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + ssize_t bytes = ptr - chip->capt.hw_io; + int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift; + + if (bytes < 0) + bytes += buffer_size; + chip->capt.hw_io = ptr; + chip->capt.hw_ready += bytes; + chip->capt.sw_io += bytes; + if (chip->capt.sw_io >= chip->capt.sw_bufsize) + chip->capt.sw_io -= chip->capt.sw_bufsize; + snd_cs46xx_capture_transfer(substream); + return chip->capt.sw_io >> chip->capt.shift; +} + +static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + /*snd_pcm_runtime_t *runtime = substream->runtime;*/ + int result = 0; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); +#else + spin_lock(&chip->reg_lock); +#endif + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + + if (! cpcm->pcm_channel) { + return -ENXIO; + } +#endif + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: +#ifdef CONFIG_SND_CS46XX_NEW_DSP + /* magic value to unmute PCM stream playback volume */ + snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + + SCBVolumeCtrl) << 2, 0x80008000); + + if (cpcm->pcm_channel->unlinked) + cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel); + + if (substream->runtime->periods != CS46XX_FRAGS) + snd_cs46xx_playback_transfer(substream); +#else + if (substream->runtime->periods != CS46XX_FRAGS) + snd_cs46xx_playback_transfer(substream); + { unsigned int tmp; + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + tmp &= 0x0000ffff; + snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp); + } +#endif + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: +#ifdef CONFIG_SND_CS46XX_NEW_DSP + /* magic mute channel */ + snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + + SCBVolumeCtrl) << 2, 0xffffffff); + + if (!cpcm->pcm_channel->unlinked) + cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel); +#else + { unsigned int tmp; + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + tmp &= 0x0000ffff; + snd_cs46xx_poke(chip, BA1_PCTL, tmp); + } +#endif + break; + default: + result = -EINVAL; + break; + } + +#ifndef CONFIG_SND_CS46XX_NEW_DSP + spin_unlock(&chip->reg_lock); +#endif + + return result; +} + +static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + unsigned int tmp; + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + tmp &= 0xffff0000; + snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + tmp &= 0xffff0000; + snd_cs46xx_poke(chip, BA1_CCTL, tmp); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + + return result; +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +static int _cs46xx_adjust_sample_rate (cs46xx_t *chip, cs46xx_pcm_t *cpcm, + int sample_rate) +{ + + /* If PCMReaderSCB and SrcTaskSCB not created yet ... */ + if ( cpcm->pcm_channel == NULL) { + cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, + cpcm, cpcm->hw_addr,cpcm->pcm_channel_id); + if (cpcm->pcm_channel == NULL) { + snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n"); + return -ENOMEM; + } + cpcm->pcm_channel->sample_rate = sample_rate; + } else + /* if sample rate is changed */ + if ((int)cpcm->pcm_channel->sample_rate != sample_rate) { + int unlinked = cpcm->pcm_channel->unlinked; + cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel); + + if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm, + cpcm->hw_addr, + cpcm->pcm_channel_id)) == NULL) { + snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n"); + return -ENOMEM; + } + + if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel); + cpcm->pcm_channel->sample_rate = sample_rate; + } + + return 0; +} +#endif + + +static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_pcm_t *cpcm; + int err; +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_t *chip = snd_pcm_substream_chip(substream); + int sample_rate = params_rate(hw_params); + int period_size = params_period_bytes(hw_params); +#endif + cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_assert (sample_rate != 0, return -ENXIO); + + down (&chip->spos_mutex); + + if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) { + up (&chip->spos_mutex); + return -ENXIO; + } + + snd_assert (cpcm->pcm_channel != NULL); + if (!cpcm->pcm_channel) { + up (&chip->spos_mutex); + return -ENXIO; + } + + + if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) { + up (&chip->spos_mutex); + return -EINVAL; + } + + snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n", + period_size, params_periods(hw_params), + params_buffer_bytes(hw_params)); +#endif + + if (params_periods(hw_params) == CS46XX_FRAGS) { + if (runtime->dma_area != cpcm->hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = cpcm->hw_area; + runtime->dma_addr = cpcm->hw_addr; + runtime->dma_bytes = cpcm->hw_size; + + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) { + substream->ops = &snd_cs46xx_playback_ops; + } else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) { + substream->ops = &snd_cs46xx_playback_rear_ops; + } else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) { + substream->ops = &snd_cs46xx_playback_iec958_ops; + } else { + snd_assert(0); + } +#else + substream->ops = &snd_cs46xx_playback_ops; +#endif + + } else { + if (runtime->dma_area == cpcm->hw_area) { + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) { +#ifdef CONFIG_SND_CS46XX_NEW_DSP + up (&chip->spos_mutex); +#endif + return err; + } + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) { + substream->ops = &snd_cs46xx_playback_indirect_ops; + } else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) { + substream->ops = &snd_cs46xx_playback_indirect_rear_ops; + } else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) { + substream->ops = &snd_cs46xx_playback_indirect_iec958_ops; + } else { + snd_assert(0); + } +#else + substream->ops = &snd_cs46xx_playback_indirect_ops; +#endif + + } + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + up (&chip->spos_mutex); +#endif + + return 0; +} + +static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream) +{ + /*cs46xx_t *chip = snd_pcm_substream_chip(substream);*/ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_pcm_t *cpcm; + + cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + + /* if play_back open fails, then this function + is called and cpcm can actually be NULL here */ + if (!cpcm) return -ENXIO; + + if (runtime->dma_area != cpcm->hw_area) + snd_pcm_lib_free_pages(substream); + + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + + return 0; +} + +static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned int tmp; + unsigned int pfie; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_pcm_t *cpcm; + + cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_assert (cpcm->pcm_channel != NULL, return -ENXIO); + + pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 ); + pfie &= ~0x0000f03f; +#else + /* old dsp */ + pfie = snd_cs46xx_peek(chip, BA1_PFIE); + pfie &= ~0x0000f03f; +#endif + + cpcm->shift = 2; + /* if to convert from stereo to mono */ + if (runtime->channels == 1) { + cpcm->shift--; + pfie |= 0x00002000; + } + /* if to convert from 8 bit to 16 bit */ + if (snd_pcm_format_width(runtime->format) == 8) { + cpcm->shift--; + pfie |= 0x00001000; + } + /* if to convert to unsigned */ + if (snd_pcm_format_unsigned(runtime->format)) + pfie |= 0x00008000; + + /* Never convert byte order when sample stream is 8 bit */ + if (snd_pcm_format_width(runtime->format) != 8) { + /* convert from big endian to little endian */ + if (snd_pcm_format_big_endian(runtime->format)) + pfie |= 0x00004000; + } + + cpcm->sw_bufsize = snd_pcm_lib_buffer_bytes(substream); + cpcm->sw_data = cpcm->sw_io = cpcm->sw_ready = 0; + cpcm->hw_data = cpcm->hw_io = cpcm->hw_ready = 0; + cpcm->appl_ptr = 0; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + + tmp = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2); + tmp &= ~0x000003ff; + tmp |= (4 << cpcm->shift) - 1; + /* playback transaction count register */ + snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2, tmp); + + /* playback format && interrupt enable */ + snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2, pfie | cpcm->pcm_channel->pcm_slot); +#else + snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_addr); + tmp = snd_cs46xx_peek(chip, BA1_PDTC); + tmp &= ~0x000003ff; + tmp |= (4 << cpcm->shift) - 1; + snd_cs46xx_poke(chip, BA1_PDTC, tmp); + snd_cs46xx_poke(chip, BA1_PFIE, pfie); + snd_cs46xx_set_play_sample_rate(chip, runtime->rate); +#endif + + return 0; +} + +static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_dsp_pcm_ostream_set_period (chip, params_period_bytes(hw_params)); +#endif + if (runtime->periods == CS46XX_FRAGS) { + if (runtime->dma_area != chip->capt.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = chip->capt.hw_area; + runtime->dma_addr = chip->capt.hw_addr; + runtime->dma_bytes = chip->capt.hw_size; + substream->ops = &snd_cs46xx_capture_ops; + } else { + if (runtime->dma_area == chip->capt.hw_area) { + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + substream->ops = &snd_cs46xx_capture_indirect_ops; + } + + return 0; +} + +static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (runtime->dma_area != chip->capt.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + + return 0; +} + +static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_addr); + chip->capt.shift = 2; + chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); + chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0; + chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0; + chip->capt.appl_ptr = 0; + snd_cs46xx_set_capture_sample_rate(chip, runtime->rate); + + return 0; +} + +static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, dev_id, return); + u32 status1; +#ifdef CONFIG_SND_CS46XX_NEW_DSP + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + u32 status2; + int i; + cs46xx_pcm_t *cpcm = NULL; +#endif + + /* + * Read the Interrupt Status Register to clear the interrupt + */ + status1 = snd_cs46xx_peekBA0(chip, BA0_HISR); + if ((status1 & 0x7fffffff) == 0) { + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV); + return; + } + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0); + + for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) { + if (i <= 15) { + if ( status1 & (1 << i) ) { + if (i == CS46XX_DSP_CAPTURE_CHANNEL) { + if (chip->capt.substream) + snd_pcm_period_elapsed(chip->capt.substream); + } else { + if (ins->pcm_channels[i].active && + ins->pcm_channels[i].private_data && + !ins->pcm_channels[i].unlinked) { + cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue); + snd_pcm_period_elapsed(cpcm->substream); + } + } + } + } else { + if ( status2 & (1 << (i - 16))) { + if (ins->pcm_channels[i].active && + ins->pcm_channels[i].private_data && + !ins->pcm_channels[i].unlinked) { + cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue); + snd_pcm_period_elapsed(cpcm->substream); + } + } + } + } + +#else + /* old dsp */ + if ((status1 & HISR_VC0) && chip->playback_pcm) { + if (chip->playback_pcm->substream) + snd_pcm_period_elapsed(chip->playback_pcm->substream); + } + if ((status1 & HISR_VC1) && chip->pcm) { + if (chip->capt.substream) + snd_pcm_period_elapsed(chip->capt.substream); + } +#endif + + if ((status1 & HISR_MIDI) && chip->rmidi) { + unsigned char c; + + spin_lock(&chip->reg_lock); + while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) { + c = snd_cs46xx_peekBA0(chip, BA0_MIDRP); + if ((chip->midcr & MIDCR_RIE) == 0) + continue; + snd_rawmidi_receive(chip->midi_input, &c, 1); + } + while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { + if ((chip->midcr & MIDCR_TIE) == 0) + break; + if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { + chip->midcr &= ~MIDCR_TIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + break; + } + snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c); + } + spin_unlock(&chip->reg_lock); + } + /* + * EOI to the PCI part....reenables interrupts + */ + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV); +} + +static snd_pcm_hardware_t snd_cs46xx_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5500, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (256 * 1024), + .period_bytes_min = CS46XX_MIN_PERIOD_SIZE, + .period_bytes_max = CS46XX_MAX_PERIOD_SIZE, + .periods_min = CS46XX_FRAGS, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_cs46xx_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5500, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (256 * 1024), + .period_bytes_min = CS46XX_MIN_PERIOD_SIZE, + .period_bytes_max = CS46XX_MAX_PERIOD_SIZE, + .periods_min = CS46XX_FRAGS, + .periods_max = 1024, + .fifo_size = 0, +}; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + +static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 }; + +#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { + .count = PERIOD_SIZES, + .list = period_sizes, + .mask = 0 +}; + +#endif + +static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + cs46xx_pcm_t * cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return); + + if (cpcm) + snd_magic_kfree(cpcm); +} + +static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pcm_channel_id) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + cs46xx_pcm_t * cpcm; + snd_pcm_runtime_t *runtime = substream->runtime; + + cpcm = snd_magic_kcalloc(cs46xx_pcm_t, 0, GFP_KERNEL); + if (cpcm == NULL) + return -ENOMEM; + cpcm->hw_size = PAGE_SIZE; + if ((cpcm->hw_area = snd_malloc_pci_pages(chip->pci, cpcm->hw_size, &cpcm->hw_addr)) == NULL) { + snd_magic_kfree(cpcm); + return -ENOMEM; + } + + runtime->hw = snd_cs46xx_playback; + runtime->private_data = cpcm; + runtime->private_free = snd_cs46xx_pcm_free_substream; + + cpcm->substream = substream; +#ifdef CONFIG_SND_CS46XX_NEW_DSP + down (&chip->spos_mutex); + cpcm->pcm_channel = NULL; + cpcm->pcm_channel_id = pcm_channel_id; + + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_sizes); + + up (&chip->spos_mutex); +#else + chip->playback_pcm = cpcm; /* HACK */ +#endif + + if (chip->accept_valid) + substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID; + chip->active_ctrl(chip, 1); + + return 0; +} + +static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream) +{ + snd_printdd("open front channel\n"); + return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL); +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +static int snd_cs46xx_playback_open_rear(snd_pcm_substream_t * substream) +{ + snd_printdd("open rear channel\n"); + + return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL); +} + +static int snd_cs46xx_playback_open_iec958(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + snd_printdd("open raw iec958 channel\n"); + + down (&chip->spos_mutex); + cs46xx_iec958_pre_open (chip); + up (&chip->spos_mutex); + + return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL); +} + +static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream); + +static int snd_cs46xx_playback_close_iec958(snd_pcm_substream_t * substream) +{ + int err; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + snd_printdd("close raw iec958 channel\n"); + + err = snd_cs46xx_playback_close(substream); + + down (&chip->spos_mutex); + cs46xx_iec958_post_close (chip); + up (&chip->spos_mutex); + + return err; +} +#endif + +static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + if ((chip->capt.hw_area = snd_malloc_pci_pages(chip->pci, chip->capt.hw_size, &chip->capt.hw_addr)) == NULL) + return -ENOMEM; + chip->capt.substream = substream; + substream->runtime->hw = snd_cs46xx_capture; + + if (chip->accept_valid) + substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID; + + chip->active_ctrl(chip, 1); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_sizes); +#endif + return 0; +} + +static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_pcm_t * cpcm; + + cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + + /* when playback_open fails, then cpcm can be NULL */ + if (!cpcm) return -ENXIO; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + down (&chip->spos_mutex); + if (cpcm->pcm_channel) { + cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel); + cpcm->pcm_channel = NULL; + } + up (&chip->spos_mutex); +#else + chip->playback_pcm = NULL; +#endif + + cpcm->substream = NULL; + snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr); + chip->active_ctrl(chip, -1); + + return 0; +} + +static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + chip->capt.substream = NULL; + snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); + chip->active_ctrl(chip, -1); + + return 0; +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +snd_pcm_ops_t snd_cs46xx_playback_rear_ops = { + .open = snd_cs46xx_playback_open_rear, + .close = snd_cs46xx_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_playback_hw_params, + .hw_free = snd_cs46xx_playback_hw_free, + .prepare = snd_cs46xx_playback_prepare, + .trigger = snd_cs46xx_playback_trigger, + .pointer = snd_cs46xx_playback_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops = { + .open = snd_cs46xx_playback_open_rear, + .close = snd_cs46xx_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_playback_hw_params, + .hw_free = snd_cs46xx_playback_hw_free, + .prepare = snd_cs46xx_playback_prepare, + .trigger = snd_cs46xx_playback_trigger, + .pointer = snd_cs46xx_playback_indirect_pointer, + .ack = snd_cs46xx_playback_transfer, +}; + +snd_pcm_ops_t snd_cs46xx_playback_iec958_ops = { + .open = snd_cs46xx_playback_open_iec958, + .close = snd_cs46xx_playback_close_iec958, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_playback_hw_params, + .hw_free = snd_cs46xx_playback_hw_free, + .prepare = snd_cs46xx_playback_prepare, + .trigger = snd_cs46xx_playback_trigger, + .pointer = snd_cs46xx_playback_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops = { + .open = snd_cs46xx_playback_open_iec958, + .close = snd_cs46xx_playback_close_iec958, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_playback_hw_params, + .hw_free = snd_cs46xx_playback_hw_free, + .prepare = snd_cs46xx_playback_prepare, + .trigger = snd_cs46xx_playback_trigger, + .pointer = snd_cs46xx_playback_indirect_pointer, + .ack = snd_cs46xx_playback_transfer, +}; + +#endif + +snd_pcm_ops_t snd_cs46xx_playback_ops = { + .open = snd_cs46xx_playback_open, + .close = snd_cs46xx_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_playback_hw_params, + .hw_free = snd_cs46xx_playback_hw_free, + .prepare = snd_cs46xx_playback_prepare, + .trigger = snd_cs46xx_playback_trigger, + .pointer = snd_cs46xx_playback_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = { + .open = snd_cs46xx_playback_open, + .close = snd_cs46xx_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_playback_hw_params, + .hw_free = snd_cs46xx_playback_hw_free, + .prepare = snd_cs46xx_playback_prepare, + .trigger = snd_cs46xx_playback_trigger, + .pointer = snd_cs46xx_playback_indirect_pointer, + .ack = snd_cs46xx_playback_transfer, +}; + +snd_pcm_ops_t snd_cs46xx_capture_ops = { + .open = snd_cs46xx_capture_open, + .close = snd_cs46xx_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_capture_hw_params, + .hw_free = snd_cs46xx_capture_hw_free, + .prepare = snd_cs46xx_capture_prepare, + .trigger = snd_cs46xx_capture_trigger, + .pointer = snd_cs46xx_capture_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = { + .open = snd_cs46xx_capture_open, + .close = snd_cs46xx_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs46xx_capture_hw_params, + .hw_free = snd_cs46xx_capture_hw_free, + .prepare = snd_cs46xx_capture_prepare, + .trigger = snd_cs46xx_capture_trigger, + .pointer = snd_cs46xx_capture_indirect_pointer, + .ack = snd_cs46xx_capture_transfer, +}; + +static void snd_cs46xx_pcm_free(snd_pcm_t *pcm) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +static void snd_cs46xx_pcm_rear_free(snd_pcm_t *pcm) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + chip->pcm_rear = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_cs46xx_pcm_iec958_free(snd_pcm_t *pcm) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + chip->pcm_iec958 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +#define MAX_PLAYBACK_CHANNELS (DSP_MAX_PCM_CHANNELS - 1) +#else +#define MAX_PLAYBACK_CHANNELS 1 +#endif + +int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = snd_cs46xx_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "CS46xx"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +int __devinit snd_cs46xx_pcm_rear(cs46xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = snd_cs46xx_pcm_rear_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_rear_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "CS46xx - Rear"); + chip->pcm_rear = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +int __devinit snd_cs46xx_pcm_iec958(cs46xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = snd_cs46xx_pcm_iec958_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_iec958_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "CS46xx - IEC958"); + chip->pcm_rear = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} +#endif + +/* + * Mixer routines + */ +static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + + snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) || + (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]), + return); + + if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) { + chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL; + chip->eapd_switch = NULL; + } + else + chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = NULL; +} + +static int snd_cs46xx_vol_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x7fff; + return 0; +} + +static int snd_cs46xx_vol_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int val = snd_cs46xx_peek(chip, reg); + ucontrol->value.integer.value[0] = 0xffff - (val >> 16); + ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff); + return 0; +} + +static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 | + (0xffff - ucontrol->value.integer.value[1])); + unsigned int old = snd_cs46xx_peek(chip, reg); + int change = (old != val); + + if (change) { + snd_cs46xx_poke(chip, reg, val); + } + + return change; +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + +static int snd_cs46xx_vol_dac_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = chip->dsp_spos_instance->dac_volume_left; + ucontrol->value.integer.value[1] = chip->dsp_spos_instance->dac_volume_right; + + return 0; +} + +static int snd_cs46xx_vol_dac_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int change = 0; + + if (chip->dsp_spos_instance->dac_volume_right != ucontrol->value.integer.value[0] || + chip->dsp_spos_instance->dac_volume_left != ucontrol->value.integer.value[1]) { + cs46xx_dsp_set_dac_volume(chip, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + change = 1; + } + + return change; +} + +static int snd_cs46xx_vol_iec958_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_input_volume_left; + ucontrol->value.integer.value[1] = chip->dsp_spos_instance->spdif_input_volume_right; + return 0; +} + +static int snd_cs46xx_vol_iec958_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int change = 0; + + if (chip->dsp_spos_instance->spdif_input_volume_left != ucontrol->value.integer.value[0] || + chip->dsp_spos_instance->spdif_input_volume_right!= ucontrol->value.integer.value[1]) { + cs46xx_dsp_set_iec958_volume (chip, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + change = 1; + } + + return change; +} + +static int snd_mixer_boolean_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_cs46xx_iec958_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + + if (reg == CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT) + ucontrol->value.integer.value[0] = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED); + else + ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in; + + return 0; +} + +static int snd_cs46xx_iec958_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int change, res; + + switch (kcontrol->private_value) { + case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT: + down (&chip->spos_mutex); + change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED); + if (ucontrol->value.integer.value[0] && !change) + cs46xx_dsp_enable_spdif_out(chip); + else if (change && !ucontrol->value.integer.value[0]) + cs46xx_dsp_disable_spdif_out(chip); + + res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED)); + up (&chip->spos_mutex); + break; + case CS46XX_MIXER_SPDIF_INPUT_ELEMENT: + change = chip->dsp_spos_instance->spdif_status_in; + if (ucontrol->value.integer.value[0] && !change) { + cs46xx_dsp_enable_spdif_in(chip); + /* restore volume */ + } + else if (change && !ucontrol->value.integer.value[0]) + cs46xx_dsp_disable_spdif_in(chip); + + res = (change != chip->dsp_spos_instance->spdif_status_in); + break; + default: + snd_assert(0, return -EINVAL); + } + + return res; +} + +static int snd_cs46xx_adc_capture_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + if (ins->adc_input != NULL) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int snd_cs46xx_adc_capture_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int change = 0; + + if (ucontrol->value.integer.value[0] && !ins->adc_input) { + cs46xx_dsp_enable_adc_capture(chip); + change = 1; + } else if (!ucontrol->value.integer.value[0] && ins->adc_input) { + cs46xx_dsp_disable_adc_capture(chip); + change = 1; + } + return change; +} + +static int snd_cs46xx_pcm_capture_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + if (ins->pcm_input != NULL) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + + +static int snd_cs46xx_pcm_capture_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int change = 0; + + if (ucontrol->value.integer.value[0] && !ins->pcm_input) { + cs46xx_dsp_enable_pcm_capture(chip); + change = 1; + } else if (!ucontrol->value.integer.value[0] && ins->pcm_input) { + cs46xx_dsp_disable_pcm_capture(chip); + change = 1; + } + + return change; +} + +static int snd_herc_spdif_select_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + + int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR); + + if (val1 & EGPIODR_GPOE0) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +/* + * Game Theatre XP card - EGPIO[0] is used to select SPDIF input optical or coaxial. + */ +static int snd_herc_spdif_select_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR); + int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR); + + if (ucontrol->value.integer.value[0]) { + /* optical is default */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, + EGPIODR_GPOE0 | val1); /* enable EGPIO0 output */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, + EGPIOPTR_GPPT0 | val2); /* open-drain on output */ + } else { + /* coaxial */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, val1 & ~EGPIODR_GPOE0); /* disable */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT0); /* disable */ + } + + /* checking diff from the EGPIO direction register + should be enough */ + return (val1 != (int)snd_cs46xx_peekBA0(chip, BA0_EGPIODR)); +} + + +static int snd_cs46xx_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs46xx_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + down (&chip->spos_mutex); + ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_default >> 24) & 0xff); + ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_default >> 16) & 0xff); + ucontrol->value.iec958.status[2] = 0; + ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_default) & 0xff); + up (&chip->spos_mutex); + + return 0; +} + +static int snd_cs46xx_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t * chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + unsigned int val; + int change; + + down (&chip->spos_mutex); + val = _wrap_all_bits(((u32)ucontrol->value.iec958.status[0] << 24)) | + _wrap_all_bits(((u32)ucontrol->value.iec958.status[2] << 16)) | + _wrap_all_bits( (u32)ucontrol->value.iec958.status[3]) | + /* left and right validity bit */ + (1 << 13) | (1 << 12); + + + change = (unsigned int)ins->spdif_csuv_default != val; + ins->spdif_csuv_default = val; + + if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) ) + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val); + + up (&chip->spos_mutex); + + return change; +} + +static int snd_cs46xx_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0x00; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int snd_cs46xx_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + down (&chip->spos_mutex); + ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_stream >> 24) & 0xff); + ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_stream >> 16) & 0xff); + ucontrol->value.iec958.status[2] = 0; + ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_stream) & 0xff); + up (&chip->spos_mutex); + + return 0; +} + +static int snd_cs46xx_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t * chip = snd_kcontrol_chip(kcontrol); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + unsigned int val; + int change; + + down (&chip->spos_mutex); + val = _wrap_all_bits(((u32)ucontrol->value.iec958.status[0] << 24)) | + _wrap_all_bits(((u32)ucontrol->value.iec958.status[1] << 16)) | + _wrap_all_bits( (u32)ucontrol->value.iec958.status[3]) | + /* left and right validity bit */ + (1 << 13) | (1 << 12); + + + change = ins->spdif_csuv_stream != val; + ins->spdif_csuv_stream = val; + + if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN ) + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val); + + up (&chip->spos_mutex); + + return change; +} + +#endif /* CONFIG_SND_CS46XX_NEW_DSP */ + + +#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO +static int snd_cs46xx_egpio_select_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + return 0; +} + +static int snd_cs46xx_egpio_select_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->current_gpio; + + return 0; +} + +static int snd_cs46xx_egpio_select_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int change = (chip->current_gpio != ucontrol->value.integer.value[0]); + chip->current_gpio = ucontrol->value.integer.value[0]; + + return change; +} + + +static int snd_cs46xx_egpio_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + + snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio); + ucontrol->value.integer.value[0] = + (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0; + + return 0; +} + +static int snd_cs46xx_egpio_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + int val = snd_cs46xx_peekBA0(chip, reg); + int oldval = val; + snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio); + + if (ucontrol->value.integer.value[0]) + val |= (1 << chip->current_gpio); + else + val &= ~(1 << chip->current_gpio); + + snd_cs46xx_pokeBA0(chip, reg,val); + snd_printdd ("put: val %08x oldval %08x\n",val,oldval); + + return (oldval != val); +} +#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */ + +static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Volume", + .info = snd_cs46xx_vol_info, +#ifndef CONFIG_SND_CS46XX_NEW_DSP + .get = snd_cs46xx_vol_get, + .put = snd_cs46xx_vol_put, + .private_value = BA1_PVOL, +#else + .get = snd_cs46xx_vol_dac_get, + .put = snd_cs46xx_vol_dac_put, +#endif +}, + +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Volume", + .info = snd_cs46xx_vol_info, + .get = snd_cs46xx_vol_get, + .put = snd_cs46xx_vol_put, +#ifndef CONFIG_SND_CS46XX_NEW_DSP + .private_value = BA1_CVOL, +#else + .private_value = (VARIDECIMATE_SCB_ADDR + 0xE) << 2, +#endif +}, +#ifdef CONFIG_SND_CS46XX_NEW_DSP +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Capture Switch", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_adc_capture_get, + .put = snd_cs46xx_adc_capture_put +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Capture Switch", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_pcm_capture_get, + .put = snd_cs46xx_pcm_capture_put +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Output Switch", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_iec958_get, + .put = snd_cs46xx_iec958_put, + .private_value = CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Input Switch", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_iec958_get, + .put = snd_cs46xx_iec958_put, + .private_value = CS46XX_MIXER_SPDIF_INPUT_ELEMENT, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Input Volume", + .info = snd_cs46xx_vol_info, + .get = snd_cs46xx_vol_iec958_get, + .put = snd_cs46xx_vol_iec958_put, + .private_value = (ASYNCRX_SCB_ADDR + 0xE) << 2, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_cs46xx_spdif_info, + .get = snd_cs46xx_spdif_default_get, + .put = snd_cs46xx_spdif_default_put, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + .info = snd_cs46xx_spdif_info, + .get = snd_cs46xx_spdif_mask_get, + .access = SNDRV_CTL_ELEM_ACCESS_READ +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_cs46xx_spdif_info, + .get = snd_cs46xx_spdif_stream_get, + .put = snd_cs46xx_spdif_stream_put +}, + +#endif +#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EGPIO select", + .info = snd_cs46xx_egpio_select_info, + .get = snd_cs46xx_egpio_select_get, + .put = snd_cs46xx_egpio_select_put, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EGPIO Input/Output", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_egpio_get, + .put = snd_cs46xx_egpio_put, + .private_value = BA0_EGPIODR, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EGPIO CMOS/Open drain", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_egpio_get, + .put = snd_cs46xx_egpio_put, + .private_value = BA0_EGPIOPTR, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EGPIO On/Off", + .info = snd_mixer_boolean_info, + .get = snd_cs46xx_egpio_get, + .put = snd_cs46xx_egpio_put, + .private_value = BA0_EGPIOSR, +}, +#endif +}; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +/* Only available on the Hercules Game Theater XP soundcard */ +static snd_kcontrol_new_t snd_hercules_controls[] __devinitdata = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Optical/Coaxial SPDIF Input Switch", + .info = snd_mixer_boolean_info, + .get = snd_herc_spdif_select_get, + .put = snd_herc_spdif_select_put, +}, +}; + + +static void snd_cs46xx_sec_codec_reset (ac97_t * ac97) +{ + signed long end_time; + int err; + + /* reset to defaults */ + snd_ac97_write(ac97, AC97_RESET, 0); + + /* set codec in extended mode */ + snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3); + + udelay(50); + + /* it's necessary to wait awhile until registers are accessible after RESET */ + /* because the PCM or MASTER volume registers can be modified, */ + /* the REC_GAIN register is used for tests */ + end_time = jiffies + HZ; + do { + unsigned short ext_mid; + + /* use preliminary reads to settle the communication */ + snd_ac97_read(ac97, AC97_RESET); + snd_ac97_read(ac97, AC97_VENDOR_ID1); + snd_ac97_read(ac97, AC97_VENDOR_ID2); + /* modem? */ + ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (ext_mid != 0xffff && (ext_mid & 1) != 0) + return; + + /* test if we can write to the record gain volume register */ + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); + if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) + return; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } while (time_after_eq(end_time, jiffies)); + + snd_printk("CS46xx secondary codec dont respond!\n"); +} +#endif + +int __devinit snd_cs46xx_mixer(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + ac97_t ac97; + snd_ctl_elem_id_t id; + int err; + unsigned int idx; + + /* detect primary codec */ + chip->nr_ac97_codecs = 0; + snd_printdd("snd_cs46xx: detecting primary codec\n"); + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs46xx_ac97_write; + ac97.read = snd_cs46xx_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs46xx_mixer_free_ac97; + + chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = &ac97; + + snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000); + for (idx = 0; idx < 100; ++idx) { + if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000) + goto _ok; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/100); + } + chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL; + return -ENXIO; + + _ok: + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97[CS46XX_PRIMARY_CODEC_INDEX])) < 0) + return err; + snd_printdd("snd_cs46xx: primary codec phase one\n"); + chip->nr_ac97_codecs = 1; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_printdd("snd_cs46xx: detecting seconadry codec\n"); + /* try detect a secondary codec */ + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs46xx_ac97_write; + ac97.read = snd_cs46xx_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs46xx_mixer_free_ac97; + ac97.num = CS46XX_SECONDARY_CODEC_INDEX; + + snd_cs46xx_ac97_write(&ac97, AC97_RESET, 0); + udelay(10); + + if (snd_cs46xx_ac97_read(&ac97, AC97_RESET) & 0x8000) { + snd_printdd("snd_cs46xx: seconadry codec not present\n"); + goto _no_sec_codec; + } + + chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = &ac97; + snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000); + for (idx = 0; idx < 100; ++idx) { + if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000) { + goto _ok2; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/100); + } + + _no_sec_codec: + snd_printdd("snd_cs46xx: secondary codec did not respond ...\n"); + + chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = NULL; + chip->nr_ac97_codecs = 1; + + /* well, one codec only ... */ + goto _end; + _ok2: + /* set secondary codec in extended mode */ + + /* use custom reset to set secondary codec in + extended mode */ + ac97.reset = snd_cs46xx_sec_codec_reset; + + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97[CS46XX_SECONDARY_CODEC_INDEX])) < 0) + return err; + chip->nr_ac97_codecs = 2; + + _end: + +#endif /* CONFIG_SND_CS46XX_NEW_DSP */ + + /* add cs4630 mixer controls */ + for (idx = 0; idx < ARRAY_SIZE(snd_cs46xx_controls); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip); + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + + /* get EAPD mixer switch (for voyetra hack) */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "External Amplifier Power Down"); + chip->eapd_switch = snd_ctl_find_id(chip->card, &id); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + /* do soundcard specific mixer setup */ + if (chip->mixer_init) { + snd_printdd ("calling chip->mixer_init(chip);\n"); + chip->mixer_init(chip); + } +#endif + + /* turn on amplifier */ + chip->amplifier_ctrl(chip, 1); + + return 0; +} + +/* + * RawMIDI interface + */ + +static void snd_cs46xx_midi_reset(cs46xx_t *chip) +{ + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST); + udelay(100); + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); +} + +static int snd_cs46xx_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + chip->active_ctrl(chip, 1); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS46XX_MODE_INPUT; + chip->midcr |= MIDCR_RXE; + chip->midi_input = substream; + if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs46xx_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE); + chip->midi_input = NULL; + if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS46XX_MODE_INPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->active_ctrl(chip, -1); + return 0; +} + +static int snd_cs46xx_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + chip->active_ctrl(chip, 1); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS46XX_MODE_OUTPUT; + chip->midcr |= MIDCR_TXE; + chip->midi_output = substream; + if (!(chip->uartm & CS46XX_MODE_INPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs46xx_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE); + chip->midi_output = NULL; + if (!(chip->uartm & CS46XX_MODE_INPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS46XX_MODE_OUTPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->active_ctrl(chip, -1); + return 0; +} + +static void snd_cs46xx_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & MIDCR_RIE) == 0) { + chip->midcr |= MIDCR_RIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & MIDCR_RIE) { + chip->midcr &= ~MIDCR_RIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & MIDCR_TIE) == 0) { + chip->midcr |= MIDCR_TIE; + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while ((chip->midcr & MIDCR_TIE) && + (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->midcr &= ~MIDCR_TIE; + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte); + } + } + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & MIDCR_TIE) { + chip->midcr &= ~MIDCR_TIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_cs46xx_midi_output = +{ + .open = snd_cs46xx_midi_output_open, + .close = snd_cs46xx_midi_output_close, + .trigger = snd_cs46xx_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_cs46xx_midi_input = +{ + .open = snd_cs46xx_midi_input_open, + .close = snd_cs46xx_midi_input_close, + .trigger = snd_cs46xx_midi_input_trigger, +}; + +int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "CS46XX"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = NULL; + return 0; +} + + +/* + * gameport interface + */ + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + +typedef struct snd_cs46xx_gameport { + struct gameport info; + cs46xx_t *chip; +} cs46xx_gameport_t; + +static void snd_cs46xx_gameport_trigger(struct gameport *gameport) +{ + cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; + cs46xx_t *chip; + snd_assert(gp, return); + chip = snd_magic_cast(cs46xx_t, gp->chip, return); + snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF); +} + +static unsigned char snd_cs46xx_gameport_read(struct gameport *gameport) +{ + cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; + cs46xx_t *chip; + snd_assert(gp, return 0); + chip = snd_magic_cast(cs46xx_t, gp->chip, return 0); + return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io); +} + +static int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; + cs46xx_t *chip; + unsigned js1, js2, jst; + + snd_assert(gp, return 0); + chip = snd_magic_cast(cs46xx_t, gp->chip, return 0); + + js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1); + js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2); + jst = snd_cs46xx_peekBA0(chip, BA0_JSPT); + + *buttons = (~jst >> 4) & 0x0F; + + axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; + axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; + axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; + axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; + + for(jst=0;jst<4;++jst) + if(axes[jst]==0xFFFF) axes[jst] = -1; + return 0; +} + +static int snd_cs46xx_gameport_open(struct gameport *gameport, int mode) +{ + switch (mode) { + case GAMEPORT_MODE_COOKED: + return 0; + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + return 0; +} + +void __devinit snd_cs46xx_gameport(cs46xx_t *chip) +{ + cs46xx_gameport_t *gp; + gp = kmalloc(sizeof(*gp), GFP_KERNEL); + if (! gp) { + snd_printk("cannot allocate gameport area\n"); + return; + } + memset(gp, 0, sizeof(*gp)); + gp->info.open = snd_cs46xx_gameport_open; + gp->info.read = snd_cs46xx_gameport_read; + gp->info.trigger = snd_cs46xx_gameport_trigger; + gp->info.cooked_read = snd_cs46xx_gameport_cooked_read; + gp->chip = chip; + chip->gameport = gp; + + snd_cs46xx_pokeBA0(chip, BA0_JSIO, 0xFF); // ? + snd_cs46xx_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); + gameport_register_port(&gp->info); +} + +#else + +void __devinit snd_cs46xx_gameport(cs46xx_t *chip) +{ +} + +#endif /* CONFIG_GAMEPORT */ + +/* + * proc interface + */ + +static long snd_cs46xx_io_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + snd_cs46xx_region_t *region = (snd_cs46xx_region_t *)entry->private_data; + + size = count; + if (file->f_pos + (size_t)size > region->size) + size = region->size - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = region->remap_addr + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = { + .read = snd_cs46xx_io_read, +}; + +static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) +{ + snd_info_entry_t *entry; + int idx; + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + if (! snd_card_proc_new(card, region->name, &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs46xx_proc_io_ops; + entry->size = region->size; + entry->mode = S_IFREG | S_IRUSR; + } + } +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_dsp_proc_init(card, chip); +#endif + return 0; +} + +static int snd_cs46xx_proc_done(cs46xx_t *chip) +{ +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_dsp_proc_done(chip); +#endif + return 0; +} + +/* + * stop the h/w + */ +static void snd_cs46xx_hw_stop(cs46xx_t *chip) +{ + unsigned int tmp; + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + /* + * Reset the processor. + */ + snd_cs46xx_reset(chip); + + snd_cs46xx_proc_stop(chip); + + /* + * Power down the PLL. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); +} + + +static int snd_cs46xx_free(cs46xx_t *chip) +{ + int idx; + + snd_assert(chip != NULL, return -EINVAL); + + if (chip->active_ctrl) + chip->active_ctrl(chip, 1); + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + if (chip->gameport) { + gameport_unregister_port(&chip->gameport->info); + kfree(chip->gameport); + } +#endif + if (chip->amplifier_ctrl) + chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */ + + snd_cs46xx_proc_done(chip); + + if (chip->region.idx[0].resource) + snd_cs46xx_hw_stop(chip); + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + if (region->remap_addr) + iounmap((void *) region->remap_addr); + if (region->resource) { + release_resource(region->resource); + kfree_nocheck(region->resource); + } + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + if (chip->active_ctrl) + chip->active_ctrl(chip, -chip->amplifier); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_dsp_spos_destroy(chip); +#endif + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs46xx_dev_free(snd_device_t *device) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, device->device_data, return -ENXIO); + return snd_cs46xx_free(chip); +} + +/* + * initialize chip + */ +static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait) +{ + int timeout; + + /* + * First, blast the clock control register to zero so that the PLL starts + * out in a known state, and blast the master serial port control register + * to zero so that the serial ports also start out in a known state. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0); + + /* + * If we are in AC97 mode, then we must set the part to a host controlled + * AC-link. Otherwise, we won't be able to bring up the link. + */ +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0 | + SERACC_TWO_CODECS); /* 2.00 dual codecs */ + /* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */ +#else + snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */ +#endif + + /* + * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS461x that uses the ARST# line + * for a reset. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, 0); +#endif + udelay(50); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_RSTN); +#endif + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_ESYN | ACCTL_RSTN); +#endif + + /* + * Now wait for a short while to allow the AC97 part to start + * generating bit clock (so we don't try to start the PLL without an + * input clock). + */ + mdelay(10); + + /* + * Set the serial port timing configuration, so that + * the clock control circuit gets its clock from the correct place. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97); + + /* + * Write the selected clock control setup to the hardware. Do not turn on + * SWCE yet (if requested), so that the devices clocked by the output of + * PLL are not clocked until the PLL is stable. + */ + snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); + snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a); + snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8); + + /* + * Power up the PLL. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP); + + /* + * Wait until the PLL has stabilized. + */ + mdelay(100); /* FIXME: schedule? */ + + /* + * Turn on clocking of the core so that we can setup the serial ports. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE); + + /* + * Enable FIFO Host Bypass + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBCF, SERBCF_HBP); + + /* + * Fill the serial port FIFOs with silence. + */ + snd_cs46xx_clear_serial_FIFOs(chip); + + /* + * Set the serial port FIFO pointer to the first sample in the FIFO. + */ + /* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */ + + /* + * Write the serial port configuration to the part. The master + * enable bit is not set until all other values have been written. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); + snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); + + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_cs46xx_pokeBA0(chip, BA0_SERC7, SERC7_ASDI2EN); + snd_cs46xx_pokeBA0(chip, BA0_SERC3, 0); + snd_cs46xx_pokeBA0(chip, BA0_SERC4, 0); + snd_cs46xx_pokeBA0(chip, BA0_SERC5, 0); + snd_cs46xx_pokeBA0(chip, BA0_SERC6, 1); +#endif + + mdelay(5); + + + /* + * Wait for the codec ready signal from the AC97 codec. + */ + timeout = 150; + while (timeout-- > 0) { + /* + * Read the AC97 status register to see if we've seen a CODEC READY + * signal from the AC97 codec. + */ + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY) + goto ok1; + if (busywait) + mdelay(10); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ+99)/100); + } + } + + + snd_printk("create - never read codec ready from AC'97\n"); + snd_printk("it is not probably bug, try to use CS4236 driver\n"); + return -EIO; + ok1: +#ifdef CONFIG_SND_CS46XX_NEW_DSP + { + int count; + for (count = 0; count < 150; count++) { + /* First, we want to wait for a short time. */ + udelay(25); + + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY) + break; + } + + /* + * Make sure CODEC is READY. + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY)) + snd_printdd("cs46xx: never read card ready from secondary AC'97\n"); + } +#endif + + /* + * Assert the vaid frame signal so that we can start sending commands + * to the AC97 codec. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); +#endif + + + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the codec is pumping ADC data across the AC-link. + */ + timeout = 150; + while (timeout-- > 0) { + /* + * Read the input slot valid register and see if input slots 3 and + * 4 are valid yet. + */ + if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + goto ok2; + if (busywait) + mdelay(10); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ+99)/100); + } + } + + snd_printk("create - never read ISV3 & ISV4 from AC'97\n"); + return -EIO; + ok2: + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 codec. + */ + + snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4 | + ACOSV_SLV7 | ACOSV_SLV8); + + + /* + * Power down the DAC and ADC. We will power them up (if) when we need + * them. + */ + /* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */ + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + /* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */ + /* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */ + + return 0; +} + +/* + * start and load DSP + */ +int __devinit snd_cs46xx_start_dsp(cs46xx_t *chip) +{ + unsigned int tmp; + /* + * Reset the processor. + */ + snd_cs46xx_reset(chip); + /* + * Download the image to the processor. + */ +#ifdef CONFIG_SND_CS46XX_NEW_DSP +#if 0 + if (cs46xx_dsp_load_module(chip, &cwcemb80_module) < 0) { + snd_printk(KERN_ERR "image download error\n"); + return -EIO; + } +#endif + + if (cs46xx_dsp_load_module(chip, &cwc4630_module) < 0) { + snd_printk(KERN_ERR "image download error [cwc4630]\n"); + return -EIO; + } + + if (cs46xx_dsp_load_module(chip, &cwcasync_module) < 0) { + snd_printk(KERN_ERR "image download error [cwcasync]\n"); + return -EIO; + } + + if (cs46xx_dsp_load_module(chip, &cwcsnoop_module) < 0) { + snd_printk(KERN_ERR "image download error [cwcsnoop]\n"); + return -EIO; + } + + if (cs46xx_dsp_load_module(chip, &cwcbinhack_module) < 0) { + snd_printk(KERN_ERR "image download error [cwcbinhack]\n"); + return -EIO; + } + + if (cs46xx_dsp_load_module(chip, &cwcdma_module) < 0) { + snd_printk(KERN_ERR "image download error [cwcdma]\n"); + return -EIO; + } + + if (cs46xx_dsp_scb_and_task_init(chip) < 0) + return -EIO; +#else + /* old image */ + if (snd_cs46xx_download_image(chip) < 0) { + snd_printk("image download error\n"); + return -EIO; + } + + /* + * Stop playback DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + chip->play_ctl = tmp & 0xffff0000; + snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); +#endif + + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + chip->capt.ctl = tmp & 0x0000ffff; + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + mdelay(5); + + snd_cs46xx_set_play_sample_rate(chip, 8000); + snd_cs46xx_set_capture_sample_rate(chip, 8000); + + snd_cs46xx_proc_start(chip); + + /* + * Enable interrupts on the part. + */ + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + /* set the attenuation to 0dB */ + /* snd_cs46xx_poke(chip, (MASTERMIX_SCB_ADDR + 0xE) << 2, 0x80008000); + snd_cs46xx_poke(chip, (VARIDECIMATE_SCB_ADDR + 0xE) << 2, 0x80008000); */ + + /* + * Initialize cs46xx SPDIF controller + */ + + /* time countdown enable */ + cs46xx_poke_via_dsp (chip,SP_ASER_COUNTDOWN, 0x80000000); + + /* SPDIF input MASTER ENABLE */ + cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x800003ff); +#else + /* set the attenuation to 0dB */ + snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000); + snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000); +#endif + + return 0; +} + + +/* + * AMP control - null AMP + */ + +static void amp_none(cs46xx_t *chip, int change) +{ +} + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +static int voyetra_setup_eapd_slot(cs46xx_t *chip) +{ + + u32 idx, valid_slots,tmp,powerdown = 0; + u16 modem_power,pin_config,logic_type; + + snd_printdd ("cs46xx: cs46xx_setup_eapd_slot()+\n"); + + /* + * See if the devices are powered down. If so, we must power them up first + * or they will not respond. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1); + + if (!(tmp & CLKCR1_SWCE)) { + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE); + powerdown = 1; + } + + /* + * Clear PRA. The Bonzo chip will be used for GPIO not for modem + * stuff. + */ + if(chip->nr_ac97_codecs != 2) { + snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n"); + return -EINVAL; + } + + modem_power = snd_cs46xx_codec_read (chip, + AC97_EXTENDED_MSTATUS, + CS46XX_SECONDARY_CODEC_INDEX); + modem_power &=0xFEFF; + + snd_cs46xx_codec_write(chip, + AC97_EXTENDED_MSTATUS, modem_power, + CS46XX_SECONDARY_CODEC_INDEX); + + /* + * Set GPIO pin's 7 and 8 so that they are configured for output. + */ + pin_config = snd_cs46xx_codec_read (chip, + AC97_GPIO_CFG, + CS46XX_SECONDARY_CODEC_INDEX); + pin_config &=0x27F; + + snd_cs46xx_codec_write(chip, + AC97_GPIO_CFG, pin_config, + CS46XX_SECONDARY_CODEC_INDEX); + + /* + * Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic. + */ + + logic_type = snd_cs46xx_codec_read(chip, AC97_GPIO_POLARITY, + CS46XX_SECONDARY_CODEC_INDEX); + logic_type &=0x27F; + snd_cs46xx_codec_write (chip, AC97_GPIO_POLARITY, logic_type, + CS46XX_SECONDARY_CODEC_INDEX); + + valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV); + valid_slots |= 0x200; + snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots); + + if ( cs46xx_wait_for_fifo(chip,1) ) { + snd_printdd("FIFO is busy\n"); + + return -EINVAL; + } + + /* + * Fill slots 12 with the correct value for the GPIO pins. + */ + for(idx = 0x90; idx <= 0x9F; idx++) { + /* + * Initialize the fifo so that bits 7 and 8 are on. + * + * Remember that the GPIO pins in bonzo are shifted by 4 bits to + * the left. 0x1800 corresponds to bits 7 and 8. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800); + + /* + * Wait for command to complete + */ + if ( cs46xx_wait_for_fifo(chip,200) ) { + snd_printdd("failed waiting for FIFO at addr (%02X)\n",idx); + + return -EINVAL; + } + + /* + * Write the serial port FIFO index. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx); + + /* + * Tell the serial port to load the new value into the FIFO location. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC); + } + + /* wait for last command to complete */ + cs46xx_wait_for_fifo(chip,200); + + /* + * Now, if we powered up the devices, then power them back down again. + * This is kinda ugly, but should never happen. + */ + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); + + return 0; +} +#endif + +/* + * Crystal EAPD mode + */ + +static void amp_voyetra(cs46xx_t *chip, int change) +{ + /* Manage the EAPD bit on the Crystal 4297 + and the Analog AD1885 */ + + int old = chip->amplifier; + int oval, val; + + chip->amplifier += change; + oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN, + CS46XX_PRIMARY_CODEC_INDEX); + val = oval; + if (chip->amplifier) { + /* Turn the EAPD amp on */ + val |= 0x8000; + } else { + /* Turn the EAPD amp off */ + val &= ~0x8000; + } + if (val != oval) { + snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val, + CS46XX_PRIMARY_CODEC_INDEX); + if (chip->eapd_switch) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->eapd_switch->id); + } + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + if (chip->amplifier && !old) { + voyetra_setup_eapd_slot(chip); + } +#endif +} + +static void hercules_init(cs46xx_t *chip) +{ + /* default: AMP off, and SPDIF input optical */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0); + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0); +} + + +/* + * Game Theatre XP card - EGPIO[2] is used to enable the external amp. + */ +static void amp_hercules(cs46xx_t *chip, int change) +{ + int old = chip->amplifier; + int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR); + int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR); + + chip->amplifier += change; + if (chip->amplifier && !old) { + snd_printdd ("Hercules amplifier ON\n"); + + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, + EGPIODR_GPOE2 | val1); /* enable EGPIO2 output */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, + EGPIOPTR_GPPT2 | val2); /* open-drain on output */ + } else if (old && !chip->amplifier) { + snd_printdd ("Hercules amplifier OFF\n"); + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, val1 & ~EGPIODR_GPOE2); /* disable */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */ + } +} + +static void voyetra_mixer_init (cs46xx_t *chip) +{ + snd_printdd ("initializing Voyetra mixer\n"); + + /* Enable SPDIF out */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0); + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0); +} + +static void hercules_mixer_init (cs46xx_t *chip) +{ +#ifdef CONFIG_SND_CS46XX_NEW_DSP + unsigned int idx; + int err; + snd_card_t *card = chip->card; +#endif + + /* set EGPIO to default */ + hercules_init(chip); + + snd_printdd ("initializing Hercules mixer\n"); + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + for (idx = 0 ; idx < ARRAY_SIZE(snd_hercules_controls); idx++) { + snd_kcontrol_t *kctl; + + kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip); + if ((err = snd_ctl_add(card, kctl)) < 0) { + printk (KERN_ERR "cs46xx: failed to initialize Hercules mixer (%d)\n",err); + break; + } + } +#endif +} + + +#if 0 +/* + * Untested + */ + +static void amp_voyetra_4294(cs46xx_t *chip, int change) +{ + chip->amplifier += change; + + if (chip->amplifier) { + /* Switch the GPIO pins 7 and 8 to open drain */ + snd_cs46xx_codec_write(chip, 0x4C, + snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F); + snd_cs46xx_codec_write(chip, 0x4E, + snd_cs46xx_codec_read(chip, 0x4E) | 0x0180); + /* Now wake the AMP (this might be backwards) */ + snd_cs46xx_codec_write(chip, 0x54, + snd_cs46xx_codec_read(chip, 0x54) & ~0x0180); + } else { + snd_cs46xx_codec_write(chip, 0x54, + snd_cs46xx_codec_read(chip, 0x54) | 0x0180); + } +} +#endif + + +/* + * piix4 pci ids + */ +#ifndef PCI_VENDOR_ID_INTEL +#define PCI_VENDOR_ID_INTEL 0x8086 +#endif /* PCI_VENDOR_ID_INTEL */ + +#ifndef PCI_DEVICE_ID_INTEL_82371AB_3 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#endif /* PCI_DEVICE_ID_INTEL_82371AB_3 */ + +/* + * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support + * whenever we need to beat on the chip. + * + * The original idea and code for this hack comes from David Kaiser at + * Linuxcare. Perhaps one day Crystal will document their chips well + * enough to make them useful. + */ + +static void clkrun_hack(cs46xx_t *chip, int change) +{ + u16 control, nval; + + if (chip->acpi_dev == NULL) + return; + + chip->amplifier += change; + + /* Read ACPI port */ + nval = control = inw(chip->acpi_port + 0x10); + + /* Flip CLKRUN off while running */ + if (! chip->amplifier) + nval |= 0x2000; + else + nval &= ~0x2000; + if (nval != control) + outw(nval, chip->acpi_port + 0x10); +} + + +/* + * detect intel piix4 + */ +static void clkrun_init(cs46xx_t *chip) +{ + u8 pp; + + chip->acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if (chip->acpi_dev == NULL) + return; /* Not a thinkpad thats for sure */ + + /* Find the control port */ + pci_read_config_byte(chip->acpi_dev, 0x41, &pp); + chip->acpi_port = pp << 8; +} + + +/* + * Card subid table + */ + +struct cs_card_type +{ + u16 vendor; + u16 id; + char *name; + void (*init)(cs46xx_t *); + void (*amp)(cs46xx_t *, int); + void (*active)(cs46xx_t *, int); + void (*mixer_init)(cs46xx_t *); +}; + +static struct cs_card_type __devinitdata cards[] = { + { + .vendor = 0x1489, + .id = 0x7001, + .name = "Genius Soundmaker 128 value", + /* nothing special */ + }, + { + .vendor = 0x5053, + .id = 0x3357, + .name = "Voyetra", + .amp = amp_voyetra, + .mixer_init = voyetra_mixer_init + }, + { + .vendor = 0x1071, + .id = 0x6003, + .name = "Mitac MI6020/21", + .amp = amp_voyetra + }, + { + .vendor = 0x14AF, + .id = 0x0050, + .name = "Hercules Game Theatre XP", + .amp = amp_hercules, + .mixer_init = hercules_mixer_init + }, + { + .vendor = 0x1681, + .id = 0x0050, + .name = "Hercules Game Theatre XP", + .amp = amp_hercules, + .mixer_init = hercules_mixer_init + }, + { + .vendor = 0x1681, + .id = 0x0051, + .name = "Hercules Game Theatre XP", + .amp = amp_hercules, + .mixer_init = hercules_mixer_init + }, + { + .vendor = 0x1681, + .id = 0x0052, + .name = "Hercules Game Theatre XP", + .amp = amp_hercules, + .mixer_init = hercules_mixer_init + }, + { + .vendor = 0x1681, + .id = 0x0053, + .name = "Hercules Game Theatre XP", + .amp = amp_hercules, + .mixer_init = hercules_mixer_init + }, + { + .vendor = 0x1681, + .id = 0x0054, + .name = "Hercules Game Theatre XP", + .amp = amp_hercules, + .mixer_init = hercules_mixer_init + }, + /* Not sure if the 570 needs the clkrun hack */ + { + .vendor = PCI_VENDOR_ID_IBM, + .id = 0x0132, + .name = "Thinkpad 570", + .init = clkrun_init, + .active = clkrun_hack + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .id = 0x0153, + .name = "Thinkpad 600X/A20/T20", + .init = clkrun_init, + .active = clkrun_hack + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .id = 0x1010, + .name = "Thinkpad 600E (unsupported)" + }, + {} /* terminator */ +}; + + +/* + * APM support + */ +#ifdef CONFIG_PM +void snd_cs46xx_suspend(cs46xx_t *chip) +{ + int amp_saved; + + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + snd_pcm_suspend_all(chip->pcm); + // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); + // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); + amp_saved = chip->amplifier; + /* turn off amp */ + chip->amplifier_ctrl(chip, -chip->amplifier); + snd_cs46xx_hw_stop(chip); + /* disable CLKRUN */ + chip->active_ctrl(chip, -chip->amplifier); + chip->amplifier = amp_saved; /* restore the status */ + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +void snd_cs46xx_resume(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + int amp_saved; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + pci_enable_device(chip->pci); + amp_saved = chip->amplifier; + chip->amplifier = 0; + chip->active_ctrl(chip, 1); /* force to on */ + + snd_cs46xx_chip_init(chip, 1); + +#if 0 + snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, + chip->ac97_general_purpose); + snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL, + chip->ac97_powerdown); + mdelay(10); + snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN, + chip->ac97_powerdown); + mdelay(5); +#endif + + snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); + + if (amp_saved) + chip->amplifier_ctrl(chip, 1); /* turn amp on */ + else + chip->active_ctrl(chip, -1); /* disable CLKRUN */ + chip->amplifier = amp_saved; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_cs46xx_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_cs46xx_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif /* CONFIG_PM */ + + +/* + */ + +int __devinit snd_cs46xx_create(snd_card_t * card, + struct pci_dev * pci, + int external_amp, int thinkpad, + cs46xx_t ** rchip) +{ + cs46xx_t *chip; + int err, idx; + snd_cs46xx_region_t *region; + struct cs_card_type *cp; + u16 ss_card, ss_vendor; + static snd_device_ops_t ops = { + .dev_free = snd_cs46xx_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(cs46xx_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + init_MUTEX(&chip->spos_mutex); +#endif + chip->card = card; + chip->pci = pci; + chip->capt.hw_size = PAGE_SIZE; + chip->irq = -1; + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + if (chip->ba0_addr == 0 || chip->ba0_addr == (unsigned long)~0 || + chip->ba1_addr == 0 || chip->ba1_addr == (unsigned long)~0) { + snd_cs46xx_free(chip); + snd_printk("wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n", chip->ba0_addr, chip->ba1_addr); + return -ENOMEM; + } + + region = &chip->region.name.ba0; + strcpy(region->name, "CS46xx_BA0"); + region->base = chip->ba0_addr; + region->size = CS46XX_BA0_SIZE; + + region = &chip->region.name.data0; + strcpy(region->name, "CS46xx_BA1_data0"); + region->base = chip->ba1_addr + BA1_SP_DMEM0; + region->size = CS46XX_BA1_DATA0_SIZE; + + region = &chip->region.name.data1; + strcpy(region->name, "CS46xx_BA1_data1"); + region->base = chip->ba1_addr + BA1_SP_DMEM1; + region->size = CS46XX_BA1_DATA1_SIZE; + + region = &chip->region.name.pmem; + strcpy(region->name, "CS46xx_BA1_pmem"); + region->base = chip->ba1_addr + BA1_SP_PMEM; + region->size = CS46XX_BA1_PRG_SIZE; + + region = &chip->region.name.reg; + strcpy(region->name, "CS46xx_BA1_reg"); + region->base = chip->ba1_addr + BA1_SP_REG; + region->size = CS46XX_BA1_REG_SIZE; + + /* set up amp and clkrun hack */ + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card); + + for (cp = &cards[0]; cp->name; cp++) { + if (cp->vendor == ss_vendor && cp->id == ss_card) { + snd_printd ("hack for %s enabled\n", cp->name); + chip->amplifier_ctrl = cp->amp; + chip->active_ctrl = cp->active; + chip->mixer_init = cp->mixer_init; + if (cp->init) + cp->init(chip); + break; + } + } + + if (external_amp) { + snd_printk("Crystal EAPD support forced on.\n"); + chip->amplifier_ctrl = amp_voyetra; + } + + if (thinkpad) { + snd_printk("Activating CLKRUN hack for Thinkpad.\n"); + chip->active_ctrl = clkrun_hack; + clkrun_init(chip); + } + + if (chip->amplifier_ctrl == NULL) + chip->amplifier_ctrl = amp_none; + if (chip->active_ctrl == NULL) + chip->active_ctrl = amp_none; + + chip->active_ctrl(chip, 1); /* enable CLKRUN */ + + pci_set_master(pci); + + for (idx = 0; idx < 5; idx++) { + region = &chip->region.idx[idx]; + if ((region->resource = request_mem_region(region->base, region->size, region->name)) == NULL) { + snd_printk("unable to request memory region 0x%lx-0x%lx\n", region->base, region->base + region->size - 1); + snd_cs46xx_free(chip); + return -EBUSY; + } + region->remap_addr = (unsigned long) ioremap_nocache(region->base, region->size); + if (region->remap_addr == 0) { + snd_printk("%s ioremap problem\n", region->name); + snd_cs46xx_free(chip); + return -ENOMEM; + } + } + if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS46XX", (void *) chip)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + snd_cs46xx_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + chip->dsp_spos_instance = cs46xx_dsp_spos_create(chip); + if (chip->dsp_spos_instance == NULL) { + snd_cs46xx_free(chip); + return -ENOMEM; + } +#endif + + err = snd_cs46xx_chip_init(chip, 0); + if (err < 0) { + snd_cs46xx_free(chip); + return err; + } + + snd_cs46xx_proc_init(card, chip); + +#ifdef CONFIG_PM + card->set_power_state = snd_cs46xx_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs46xx_free(chip); + return err; + } + + chip->active_ctrl(chip, -1); /* disable CLKRUN */ + + *rchip = chip; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx_lib.h linux/sound/pci/cs46xx/cs46xx_lib.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/cs46xx_lib.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/cs46xx_lib.h 2003-03-03 04:33:01.000000000 -0700 @@ -0,0 +1,222 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __CS46XX_LIB_H__ +#define __CS46XX_LIB_H__ + +#define chip_t cs46xx_t + +/* + * constants + */ + +#define CS46XX_BA0_SIZE 0x1000 +#define CS46XX_BA1_DATA0_SIZE 0x3000 +#define CS46XX_BA1_DATA1_SIZE 0x3800 +#define CS46XX_BA1_PRG_SIZE 0x7000 +#define CS46XX_BA1_REG_SIZE 0x0100 + + + +#ifdef CONFIG_SND_CS46XX_NEW_DSP +#define CS46XX_MIN_PERIOD_SIZE 1 +#define CS46XX_MAX_PERIOD_SIZE 1024*1024 +#else +#define CS46XX_MIN_PERIOD_SIZE 2048 +#define CS46XX_MAX_PERIOD_SIZE 2048 +#endif + +#define CS46XX_FRAGS 2 +/* #define CS46XX_BUFFER_SIZE CS46XX_MAX_PERIOD_SIZE * CS46XX_FRAGS */ + +#define SCB_NO_PARENT 0 +#define SCB_ON_PARENT_NEXT_SCB 1 +#define SCB_ON_PARENT_SUBLIST_SCB 2 + +/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */ +#define BA1_DWORD_SIZE (13 * 1024 + 512) +#define BA1_MEMORY_COUNT 3 + +extern snd_pcm_ops_t snd_cs46xx_playback_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_indirect_ops; +extern snd_pcm_ops_t snd_cs46xx_capture_ops; +extern snd_pcm_ops_t snd_cs46xx_capture_indirect_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_rear_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_iec958_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops; + + +/* + * common I/O routines + */ + +static inline void snd_cs46xx_poke(cs46xx_t *chip, unsigned long reg, unsigned int val) +{ + unsigned int bank = reg >> 16; + unsigned int offset = reg & 0xffff; + + /*if (bank == 0) printk("snd_cs46xx_poke: %04X - %08X\n",reg >> 2,val); */ + writel(val, chip->region.idx[bank+1].remap_addr + offset); +} + +static inline unsigned int snd_cs46xx_peek(cs46xx_t *chip, unsigned long reg) +{ + unsigned int bank = reg >> 16; + unsigned int offset = reg & 0xffff; + return readl(chip->region.idx[bank+1].remap_addr + offset); +} + +static inline void snd_cs46xx_pokeBA0(cs46xx_t *chip, unsigned long offset, unsigned int val) +{ + writel(val, chip->region.name.ba0.remap_addr + offset); +} + +static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offset) +{ + return readl(chip->region.name.ba0.remap_addr + offset); +} + +dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip); +void cs46xx_dsp_spos_destroy (cs46xx_t * chip); +int cs46xx_dsp_load_module (cs46xx_t * chip,dsp_module_desc_t * module); +symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip,char * symbol_name,int symbol_type); +symbol_entry_t * cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip,u32 address,int symbol_type); +int cs46xx_dsp_proc_init (snd_card_t * card, cs46xx_t *chip); +int cs46xx_dsp_proc_done (cs46xx_t *chip); +int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip); +int cs46xx_dsp_async_init (cs46xx_t *chip,dsp_scb_descriptor_t * fg_entry); +int snd_cs46xx_download (cs46xx_t *chip,u32 *src,unsigned long offset, + unsigned long len); +int snd_cs46xx_clear_BA1(cs46xx_t *chip,unsigned long offset,unsigned long len); +int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip); +int cs46xx_dsp_enable_spdif_hw (cs46xx_t *chip); +int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip); +int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip); +int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip); +int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip); +int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip); +int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip); +int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip); +int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data); +dsp_scb_descriptor_t * cs46xx_dsp_create_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest); +void cs46xx_dsp_proc_free_scb_desc (dsp_scb_descriptor_t * scb); +void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * scb); +dsp_task_descriptor_t * cs46xx_dsp_create_task_tree (cs46xx_t *chip,char * name, + u32 * task_data,u32 dest,int size); +dsp_scb_descriptor_t * cs46xx_dsp_create_timing_master_scb (cs46xx_t *chip); +dsp_scb_descriptor_t * cs46xx_dsp_create_codec_out_scb(cs46xx_t * chip,char * codec_name, + u16 channel_disp,u16 fifo_addr, + u16 child_scb_addr, + u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_codec_in_scb(cs46xx_t * chip,char * codec_name, + u16 channel_disp,u16 fifo_addr, + u16 sample_buffer_addr, + u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +void cs46xx_dsp_remove_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb); +dsp_scb_descriptor_t * cs46xx_dsp_create_generic_scb (cs46xx_t *chip,char * name, + u32 * scb_data,u32 dest, + char * task_entry_name, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_codec_in_scb(cs46xx_t * chip,char * codec_name, + u16 channel_disp,u16 fifo_addr, + u16 sample_buffer_addr, + u32 dest,dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_pcm_reader_scb(cs46xx_t * chip,char * scb_name, + u16 sample_buffer_addr,u32 dest, + int virtual_channel,u32 playback_hw_addr, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_src_task_scb(cs46xx_t * chip,char * scb_name, + int sample_rate, + u16 src_buffer_addr, + u16 src_delay_buffer_addr,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type, + int pass_through); +dsp_scb_descriptor_t * cs46xx_dsp_create_mix_only_scb(cs46xx_t * chip,char * scb_name, + u16 mix_buffer_addr,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); + +dsp_scb_descriptor_t * cs46xx_dsp_create_vari_decimate_scb(cs46xx_t * chip,char * scb_name, + u16 vari_buffer_addr0, + u16 vari_buffer_addr1, + u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_pcm_serial_input_scb(cs46xx_t * chip,char * scb_name,u32 dest, + dsp_scb_descriptor_t * input_scb, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_asynch_fg_tx_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 hfg_scb_address, + u16 asynch_buffer_address, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_asynch_fg_rx_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 hfg_scb_address, + u16 asynch_buffer_address, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_spio_write_scb(cs46xx_t * chip,char * scb_name,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_mix_to_ostream_scb(cs46xx_t * chip,char * scb_name, + u16 mix_buffer_addr,u16 writeback_spb,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_output_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 snoop_buffer_address, + dsp_scb_descriptor_t * snoop_scb, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +dsp_scb_descriptor_t * cs46xx_dsp_create_magic_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 snoop_buffer_address, + dsp_scb_descriptor_t * snoop_scb, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type); +pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr, + int pcm_channel_id); +void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip, + pcm_channel_descriptor_t * pcm_channel); +int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel); +int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel); +dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source, + u16 addr,char * scb_name); +int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src); +int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src); +int cs46xx_iec958_pre_open (cs46xx_t *chip); +int cs46xx_iec958_post_close (cs46xx_t *chip); +int cs46xx_dsp_pcm_channel_set_period (cs46xx_t * chip, + pcm_channel_descriptor_t * pcm_channel, + int period_size); +int cs46xx_dsp_pcm_ostream_set_period (cs46xx_t * chip, + int period_size); +int cs46xx_dsp_set_dac_volume (cs46xx_t * chip,u16 left,u16 right); +int cs46xx_dsp_set_iec958_volume (cs46xx_t * chip,u16 left,u16 right); +#endif /* __CS46XX_LIB_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/dsp_spos.c linux/sound/pci/cs46xx/dsp_spos.c --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/dsp_spos.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/dsp_spos.c 2003-03-03 04:33:01.000000000 -0700 @@ -0,0 +1,1819 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * 2002-07 Benny Sjostrand benny@hostmobility.com + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs46xx_lib.h" +#include "dsp_spos.h" + +static wide_opcode_t wide_opcodes[] = { + WIDE_FOR_BEGIN_LOOP, + WIDE_FOR_BEGIN_LOOP2, + WIDE_COND_GOTO_ADDR, + WIDE_COND_GOTO_CALL, + WIDE_TBEQ_COND_GOTO_ADDR, + WIDE_TBEQ_COND_CALL_ADDR, + WIDE_TBEQ_NCOND_GOTO_ADDR, + WIDE_TBEQ_NCOND_CALL_ADDR, + WIDE_TBEQ_COND_GOTO1_ADDR, + WIDE_TBEQ_COND_CALL1_ADDR, + WIDE_TBEQ_NCOND_GOTOI_ADDR, + WIDE_TBEQ_NCOND_CALL1_ADDR +}; + +static int shadow_and_reallocate_code (cs46xx_t * chip,u32 * data,u32 size, u32 overlay_begin_address) +{ + unsigned int i = 0, j, nreallocated = 0; + u32 hival,loval,address; + u32 mop_operands,mop_type,wide_op; + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert( ((size % 2) == 0), return -EINVAL); + + while (i < size) { + loval = data[i++]; + hival = data[i++]; + + if (ins->code.offset > 0) { + mop_operands = (hival >> 6) & 0x03fff; + mop_type = mop_operands >> 10; + + /* check for wide type instruction */ + if (mop_type == 0 && + (mop_operands & WIDE_LADD_INSTR_MASK) == 0 && + (mop_operands & WIDE_INSTR_MASK) != 0) { + wide_op = loval & 0x7f; + for (j = 0;j < sizeof(wide_opcodes) / sizeof(wide_opcode_t); ++j) { + if (wide_opcodes[j] == wide_op) { + /* need to reallocate instruction */ + address = (hival & 0x00FFF) << 5; + address |= loval >> 15; + + snd_printdd("handle_wideop[1]: %05x:%05x addr %04x\n",hival,loval,address); + + if ( !(address & 0x8000) ) { + address += (ins->code.offset / 2) - overlay_begin_address; + } else { + snd_printdd("handle_wideop[1]: ROM symbol not reallocated\n"); + } + + hival &= 0xFF000; + loval &= 0x07FFF; + + hival |= ( (address >> 5) & 0x00FFF); + loval |= ( (address << 15) & 0xF8000); + + address = (hival & 0x00FFF) << 5; + address |= loval >> 15; + + snd_printdd("handle_wideop:[2] %05x:%05x addr %04x\n",hival,loval,address); + nreallocated ++; + } /* wide_opcodes[j] == wide_op */ + } /* for */ + } /* mod_type == 0 ... */ + } /* ins->code.offset > 0 */ + + ins->code.data[ins->code.size++] = loval; + ins->code.data[ins->code.size++] = hival; + } + + snd_printdd("dsp_spos: %d instructions reallocated\n",nreallocated); + return nreallocated; +} + +static segment_desc_t * get_segment_desc (dsp_module_desc_t * module, int seg_type) +{ + int i; + for (i = 0;i < module->nsegments; ++i) { + if (module->segments[i].segment_type == seg_type) { + return (module->segments + i); + } + } + + return NULL; +}; + +static int find_free_symbol_index (dsp_spos_instance_t * ins) +{ + int index = ins->symbol_table.nsymbols,i; + + for (i = ins->symbol_table.highest_frag_index; i < ins->symbol_table.nsymbols; ++i) { + if (ins->symbol_table.symbols[i].deleted) { + index = i; + break; + } + } + + return index; +} + +static int add_symbols (cs46xx_t * chip, dsp_module_desc_t * module) +{ + int i; + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + if (module->symbol_table.nsymbols > 0) { + if (!strcmp(module->symbol_table.symbols[0].symbol_name, "OVERLAYBEGINADDRESS") && + module->symbol_table.symbols[0].symbol_type == SYMBOL_CONSTANT ) { + module->overlay_begin_address = module->symbol_table.symbols[0].address; + } + } + + for (i = 0;i < module->symbol_table.nsymbols; ++i) { + if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) { + snd_printk(KERN_ERR "dsp_spos: symbol table is full\n"); + return -ENOMEM; + } + + + if (cs46xx_dsp_lookup_symbol(chip, + module->symbol_table.symbols[i].symbol_name, + module->symbol_table.symbols[i].symbol_type) == NULL) { + + ins->symbol_table.symbols[ins->symbol_table.nsymbols] = module->symbol_table.symbols[i]; + ins->symbol_table.symbols[ins->symbol_table.nsymbols].address += ((ins->code.offset / 2) - module->overlay_begin_address); + ins->symbol_table.symbols[ins->symbol_table.nsymbols].module = module; + ins->symbol_table.symbols[ins->symbol_table.nsymbols].deleted = 0; + + if (ins->symbol_table.nsymbols > ins->symbol_table.highest_frag_index) + ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols; + + ins->symbol_table.nsymbols++; + } else { + /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n", + module->symbol_table.symbols[i].symbol_name); */ + } + } + + return 0; +} + +static symbol_entry_t * add_symbol (cs46xx_t * chip, char * symbol_name, u32 address, int type) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + symbol_entry_t * symbol = NULL; + int index; + + if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) { + snd_printk(KERN_ERR "dsp_spos: symbol table is full\n"); + return NULL; + } + + if (cs46xx_dsp_lookup_symbol(chip, + symbol_name, + type) != NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol <%s> duplicated\n", symbol_name); + return NULL; + } + + index = find_free_symbol_index (ins); + + strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); + ins->symbol_table.symbols[index].address = address; + ins->symbol_table.symbols[index].symbol_type = type; + ins->symbol_table.symbols[index].module = NULL; + ins->symbol_table.symbols[index].deleted = 0; + symbol = (ins->symbol_table.symbols + index); + + if (index > ins->symbol_table.highest_frag_index) + ins->symbol_table.highest_frag_index = index; + + if (index == ins->symbol_table.nsymbols) + ins->symbol_table.nsymbols++; /* no frag. in list */ + + return symbol; +} + +dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip) +{ + dsp_spos_instance_t * ins = kmalloc(sizeof(dsp_spos_instance_t), GFP_KERNEL); + + if (ins == NULL) + return NULL; + memset(ins, 0, sizeof(*ins)); + + /* better to use vmalloc for this big table */ + ins->symbol_table.nsymbols = 0; + ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS); + ins->symbol_table.highest_frag_index = 0; + + if (ins->symbol_table.symbols == NULL) { + cs46xx_dsp_spos_destroy(chip); + return NULL; + } + + ins->code.offset = 0; + ins->code.size = 0; + ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL); + + if (ins->code.data == NULL) { + cs46xx_dsp_spos_destroy(chip); + return NULL; + } + + ins->nscb = 0; + ins->ntask = 0; + + ins->nmodules = 0; + ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL); + + if (ins->modules == NULL) { + cs46xx_dsp_spos_destroy(chip); + return NULL; + } + + /* default SPDIF input sample rate + to 48000 khz */ + ins->spdif_in_sample_rate = 48000; + + /* maximize volume */ + ins->dac_volume_right = 0x8000; + ins->dac_volume_left = 0x8000; + ins->spdif_input_volume_right = 0x8000; + ins->spdif_input_volume_left = 0x8000; + + /* set left and right validity bits and + default channel status */ + ins->spdif_csuv_default = + ins->spdif_csuv_stream = + /* byte 0 */ (_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF & 0xff)) << 24) | + /* byte 1 */ (_wrap_all_bits( ((SNDRV_PCM_DEFAULT_CON_SPDIF >> 16) & 0xff)) << 16) | + /* byte 3 */ _wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF >> 24) & 0xff) | + /* left and right validity bits */ (1 << 13) | (1 << 12); + + return ins; +} + +void cs46xx_dsp_spos_destroy (cs46xx_t * chip) +{ + int i; + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert(ins != NULL, return); + + down(&chip->spos_mutex); + for (i = 0; i < ins->nscb; ++i) { + if (ins->scbs[i].deleted) continue; + + cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) ); + } + + if (ins->code.data) + kfree(ins->code.data); + if (ins->symbol_table.symbols) + vfree(ins->symbol_table.symbols); + if (ins->modules) + kfree(ins->modules); + kfree(ins); + up(&chip->spos_mutex); +} + +int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + segment_desc_t * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM); + segment_desc_t * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER); + segment_desc_t * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE); + u32 doffset, dsize; + + if (ins->nmodules == DSP_MAX_MODULES - 1) { + snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n"); + return -ENOMEM; + } + + snd_printdd("dsp_spos: loading module %s into DSP\n", module->module_name); + + if (ins->nmodules == 0) { + snd_printdd("dsp_spos: clearing parameter area\n"); + snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE); + } + + if (parameter == NULL) { + snd_printdd("dsp_spos: module got no parameter segment\n"); + } else { + if (ins->nmodules > 0) { + snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n"); + } + + doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET); + dsize = parameter->size * 4; + + snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n", + doffset,doffset + dsize); + + if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) { + snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n"); + return -EINVAL; + } + } + + if (ins->nmodules == 0) { + snd_printdd("dsp_spos: clearing sample area\n"); + snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE); + } + + if (sample == NULL) { + snd_printdd("dsp_spos: module got no sample segment\n"); + } else { + if (ins->nmodules > 0) { + snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n"); + } + + doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET); + dsize = sample->size * 4; + + snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n", + doffset,doffset + dsize); + + if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) { + snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n"); + return -EINVAL; + } + } + + + if (ins->nmodules == 0) { + snd_printdd("dsp_spos: clearing code area\n"); + snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE); + } + + if (code == NULL) { + snd_printdd("dsp_spos: module got no code segment\n"); + } else { + if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) { + snd_printk(KERN_ERR "dsp_spos: no space available in DSP\n"); + return -ENOMEM; + } + + module->load_address = ins->code.offset; + module->overlay_begin_address = 0x000; + + /* if module has a code segment it must have + symbol table */ + snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM); + if (add_symbols(chip,module)) { + snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n"); + return -ENOMEM; + } + + doffset = (code->offset * 4 + ins->code.offset * 4 + DSP_CODE_BYTE_OFFSET); + dsize = code->size * 4; + snd_printdd("dsp_spos: downloading code to chip (%08x-%08x)\n", + doffset,doffset + dsize); + + module->nfixups = shadow_and_reallocate_code(chip,code->data,code->size,module->overlay_begin_address); + + if (snd_cs46xx_download (chip,(ins->code.data + ins->code.offset),doffset,dsize)) { + snd_printk(KERN_ERR "dsp_spos: failed to download code to DSP\n"); + return -EINVAL; + } + + ins->code.offset += code->size; + } + + /* NOTE: module segments and symbol table must be + statically allocated. Case that module data is + not generated by the ospparser */ + ins->modules[ins->nmodules] = *module; + ins->nmodules++; + + return 0; +} + +symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip, char * symbol_name, int symbol_type) +{ + int i; + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) { + + if (ins->symbol_table.symbols[i].deleted) + continue; + + if (!strcmp(ins->symbol_table.symbols[i].symbol_name,symbol_name) && + ins->symbol_table.symbols[i].symbol_type == symbol_type) { + return (ins->symbol_table.symbols + i); + } + } + +#if 0 + printk ("dsp_spos: symbol <%s> type %02x not found\n", + symbol_name,symbol_type); +#endif + + return NULL; +} + + +symbol_entry_t * cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip, u32 address, int symbol_type) +{ + int i; + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) { + + if (ins->symbol_table.symbols[i].deleted) + continue; + + if (ins->symbol_table.symbols[i].address == address && + ins->symbol_table.symbols[i].symbol_type == symbol_type) { + return (ins->symbol_table.symbols + i); + } + } + + + return NULL; +} + + +static void cs46xx_dsp_proc_symbol_table_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int i; + + snd_iprintf(buffer, "SYMBOLS:\n"); + for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) { + char *module_str = "system"; + + if (ins->symbol_table.symbols[i].deleted) + continue; + + if (ins->symbol_table.symbols[i].module != NULL) { + module_str = ins->symbol_table.symbols[i].module->module_name; + } + + + snd_iprintf(buffer, "%04X <%02X> %s [%s]\n", + ins->symbol_table.symbols[i].address, + ins->symbol_table.symbols[i].symbol_type, + ins->symbol_table.symbols[i].symbol_name, + module_str); + } +} + + +static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int i,j; + + down(&chip->spos_mutex); + snd_iprintf(buffer, "MODULES:\n"); + for ( i = 0; i < ins->nmodules; ++i ) { + snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name); + snd_iprintf(buffer, " %d symbols\n", ins->modules[i].symbol_table.nsymbols); + snd_iprintf(buffer, " %d fixups\n", ins->modules[i].nfixups); + + for (j = 0; j < ins->modules[i].nsegments; ++ j) { + segment_desc_t * desc = (ins->modules[i].segments + j); + snd_iprintf(buffer, " segment %02x offset %08x size %08x\n", + desc->segment_type,desc->offset, desc->size); + } + } + up(&chip->spos_mutex); +} + +static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int i,j,col; + unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; + + down(&chip->spos_mutex); + snd_iprintf(buffer, "TASK TREES:\n"); + for ( i = 0; i < ins->ntask; ++i) { + snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name); + + for (col = 0,j = 0;j < ins->tasks[i].size; j++,col++) { + u32 val; + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + val = readl(dst + (ins->tasks[i].address + j) * sizeof(u32)); + snd_iprintf(buffer,"%08x ",val); + } + } + + snd_iprintf(buffer,"\n"); + up(&chip->spos_mutex); +} + +static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int i; + + down(&chip->spos_mutex); + snd_iprintf(buffer, "SCB's:\n"); + for ( i = 0; i < ins->nscb; ++i) { + if (ins->scbs[i].deleted) + continue; + snd_iprintf(buffer,"\n%04x %s:\n\n",ins->scbs[i].address,ins->scbs[i].scb_name); + + if (ins->scbs[i].parent_scb_ptr != NULL) { + snd_iprintf(buffer,"parent [%s:%04x] ", + ins->scbs[i].parent_scb_ptr->scb_name, + ins->scbs[i].parent_scb_ptr->address); + } else snd_iprintf(buffer,"parent [none] "); + + snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x] task_entry [%s:%04x]\n", + ins->scbs[i].sub_list_ptr->scb_name, + ins->scbs[i].sub_list_ptr->address, + ins->scbs[i].next_scb_ptr->scb_name, + ins->scbs[i].next_scb_ptr->address, + ins->scbs[i].task_entry->symbol_name, + ins->scbs[i].task_entry->address); + } + + snd_iprintf(buffer,"\n"); + up(&chip->spos_mutex); +} + +static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + /*dsp_spos_instance_t * ins = chip->dsp_spos_instance; */ + unsigned int i,col = 0; + unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; + symbol_entry_t * symbol; + + for (i = 0;i < DSP_PARAMETER_BYTE_SIZE; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if ( (symbol = cs46xx_dsp_lookup_symbol_addr (chip,i / sizeof(u32), SYMBOL_PARAMETER)) != NULL) { + col = 0; + snd_iprintf (buffer,"\n%s:\n",symbol->symbol_name); + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ", i / (unsigned int)sizeof(u32)); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } +} + +static void cs46xx_dsp_proc_sample_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + int i,col = 0; + unsigned long dst = chip->region.idx[2].remap_addr; + + snd_iprintf(buffer,"PCMREADER:\n"); + for (i = PCM_READER_BUF1;i < PCM_READER_BUF1 + 0x30; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + + snd_iprintf(buffer,"\nMIX_SAMPLE_BUF1:\n"); + + col = 0; + for (i = MIX_SAMPLE_BUF1;i < MIX_SAMPLE_BUF1 + 0x40; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + + snd_iprintf(buffer,"\nSRC_TASK_SCB1:\n"); + col = 0; + for (i = 0x2580 ; i < 0x2580 + 0x40 ; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + + + snd_iprintf(buffer,"\nSPDIFO_BUFFER:\n"); + col = 0; + for (i = SPDIFO_IP_OUTPUT_BUFFER1;i < SPDIFO_IP_OUTPUT_BUFFER1 + 0x30; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + + snd_iprintf(buffer,"\n...\n"); + col = 0; + + for (i = SPDIFO_IP_OUTPUT_BUFFER1+0xD0;i < SPDIFO_IP_OUTPUT_BUFFER1 + 0x110; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + + + snd_iprintf(buffer,"\nOUTPUT_SNOOP:\n"); + col = 0; + for (i = OUTPUT_SNOOP_BUFFER;i < OUTPUT_SNOOP_BUFFER + 0x40; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + + snd_iprintf(buffer,"\nCODEC_INPUT_BUF1: \n"); + col = 0; + for (i = CODEC_INPUT_BUF1;i < CODEC_INPUT_BUF1 + 0x40; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } +#if 0 + snd_iprintf(buffer,"\nWRITE_BACK_BUF1: \n"); + col = 0; + for (i = WRITE_BACK_BUF1;i < WRITE_BACK_BUF1 + 0x40; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } +#endif + + snd_iprintf(buffer,"\nSPDIFI_IP_OUTPUT_BUFFER1: \n"); + col = 0; + for (i = SPDIFI_IP_OUTPUT_BUFFER1;i < SPDIFI_IP_OUTPUT_BUFFER1 + 0x80; i += sizeof(u32),col ++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + + if (col == 0) { + snd_iprintf(buffer, "%04X ",i); + } + + snd_iprintf(buffer,"%08X ",readl(dst + i)); + } + snd_iprintf(buffer,"\n"); +} + +int cs46xx_dsp_proc_init (snd_card_t * card, cs46xx_t *chip) +{ + snd_info_entry_t *entry; + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int i; + + ins->snd_card = card; + + if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->c.text.read_size = 512; + + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + + ins->proc_dsp_dir = entry; + + if (!ins->proc_dsp_dir) + return -ENOMEM; + + if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = cs46xx_dsp_proc_symbol_table_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ins->proc_sym_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = cs46xx_dsp_proc_modules_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ins->proc_modules_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ins->proc_parameter_dump_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = cs46xx_dsp_proc_sample_dump_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ins->proc_sample_dump_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = cs46xx_dsp_proc_task_tree_read;; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ins->proc_task_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 1024; + entry->c.text.read = cs46xx_dsp_proc_scb_read;; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ins->proc_scb_info_entry = entry; + + down(&chip->spos_mutex); + /* register/update SCB's entries on proc */ + for (i = 0; i < ins->nscb; ++i) { + if (ins->scbs[i].deleted) continue; + + cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i)); + } + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_dsp_proc_done (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int i; + + if (ins->proc_sym_info_entry) { + snd_info_unregister(ins->proc_sym_info_entry); + ins->proc_sym_info_entry = NULL; + } + + if (ins->proc_modules_info_entry) { + snd_info_unregister(ins->proc_modules_info_entry); + ins->proc_modules_info_entry = NULL; + } + + if (ins->proc_parameter_dump_info_entry) { + snd_info_unregister(ins->proc_parameter_dump_info_entry); + ins->proc_parameter_dump_info_entry = NULL; + } + + if (ins->proc_sample_dump_info_entry) { + snd_info_unregister(ins->proc_sample_dump_info_entry); + ins->proc_sample_dump_info_entry = NULL; + } + + if (ins->proc_scb_info_entry) { + snd_info_unregister(ins->proc_scb_info_entry); + ins->proc_scb_info_entry = NULL; + } + + if (ins->proc_task_info_entry) { + snd_info_unregister(ins->proc_task_info_entry); + ins->proc_task_info_entry = NULL; + } + + down(&chip->spos_mutex); + for (i = 0; i < ins->nscb; ++i) { + if (ins->scbs[i].deleted) continue; + cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) ); + } + up(&chip->spos_mutex); + + if (ins->proc_dsp_dir) { + snd_info_unregister (ins->proc_dsp_dir); + ins->proc_dsp_dir = NULL; + } + + return 0; +} + +static int debug_tree = 0; +static void _dsp_create_task_tree (cs46xx_t *chip,u32 * task_data, u32 dest, int size) +{ + unsigned long spdst = chip->region.idx[1].remap_addr + + DSP_PARAMETER_BYTE_OFFSET + dest * sizeof(u32); + int i; + + for (i = 0; i < size; ++i) { + if (debug_tree) printk ("addr %08x, val %08x\n",(int)spdst,task_data[i]); + writel(task_data[i],spdst); + spdst += sizeof(u32); + } +} + +static int debug_scb = 0; +static void _dsp_create_scb (cs46xx_t *chip,u32 * scb_data, u32 dest) +{ + unsigned long spdst = chip->region.idx[1].remap_addr + + DSP_PARAMETER_BYTE_OFFSET + dest * sizeof(u32); + int i; + + for (i = 0; i < 0x10; ++i) { + if (debug_scb) printk ("addr %08x, val %08x\n",(int)spdst,scb_data[i]); + writel(scb_data[i],spdst); + spdst += sizeof(u32); + } +} + +static int find_free_scb_index (dsp_spos_instance_t * ins) +{ + int index = ins->nscb, i; + + for (i = ins->scb_highest_frag_index; i < ins->nscb; ++i) { + if (ins->scbs[i].deleted) { + index = i; + break; + } + } + + return index; +} + +static dsp_scb_descriptor_t * _map_scb (cs46xx_t *chip,char * name,u32 dest) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * desc = NULL; + int index; + + if (ins->nscb == DSP_MAX_SCB_DESC - 1) { + snd_printk(KERN_ERR "dsp_spos: got no place for other SCB\n"); + return NULL; + } + + index = find_free_scb_index (ins); + + strcpy(ins->scbs[index].scb_name, name); + ins->scbs[index].address = dest; + ins->scbs[index].index = index; + ins->scbs[index].proc_info = NULL; + ins->scbs[index].ref_count = 1; + ins->scbs[index].deleted = 0; + spin_lock_init(&ins->scbs[index].lock); + + desc = (ins->scbs + index); + ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER); + + if (index > ins->scb_highest_frag_index) + ins->scb_highest_frag_index = index; + + if (index == ins->nscb) + ins->nscb++; + + return desc; +} + +static dsp_task_descriptor_t * _map_task_tree (cs46xx_t *chip,char * name,u32 dest,u32 size) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_task_descriptor_t * desc = NULL; + + if (ins->ntask == DSP_MAX_TASK_DESC - 1) { + snd_printk(KERN_ERR "dsp_spos: got no place for other TASK\n"); + return NULL; + } + + strcpy(ins->tasks[ins->ntask].task_name,name); + ins->tasks[ins->ntask].address = dest; + ins->tasks[ins->ntask].size = size; + + /* quick find in list */ + ins->tasks[ins->ntask].index = ins->ntask; + desc = (ins->tasks + ins->ntask); + ins->ntask++; + + add_symbol (chip,name,dest,SYMBOL_PARAMETER); + return desc; +} + +dsp_scb_descriptor_t * cs46xx_dsp_create_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest) +{ + dsp_scb_descriptor_t * desc; + + desc = _map_scb (chip,name,dest); + if (desc) { + _dsp_create_scb(chip,scb_data,dest); + } else { + snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n"); + } + + return desc; +} + + +dsp_task_descriptor_t * cs46xx_dsp_create_task_tree (cs46xx_t *chip,char * name, u32 * task_data,u32 dest,int size) +{ + dsp_task_descriptor_t * desc; + + desc = _map_task_tree (chip,name,dest,size); + if (desc) { + _dsp_create_task_tree(chip,task_data,dest,size); + } else { + snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n"); + } + + return desc; +} + + +int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + symbol_entry_t * fg_task_tree_header_code; + symbol_entry_t * task_tree_header_code; + symbol_entry_t * task_tree_thread; + symbol_entry_t * null_algorithm; + symbol_entry_t * magic_snoop_task; + + dsp_scb_descriptor_t * timing_master_scb; + dsp_scb_descriptor_t * codec_out_scb; + dsp_scb_descriptor_t * codec_in_scb; + dsp_scb_descriptor_t * src_task_scb; + dsp_scb_descriptor_t * master_mix_scb; + dsp_scb_descriptor_t * rear_mix_scb; + dsp_scb_descriptor_t * record_mix_scb; + dsp_scb_descriptor_t * write_back_scb; + dsp_scb_descriptor_t * vari_decimate_scb; + dsp_scb_descriptor_t * sec_codec_out_scb; + dsp_scb_descriptor_t * magic_snoop_scb; + + spos_control_block_t sposcb = { + /* 0 */ HFG_TREE_SCB,HFG_STACK, + /* 1 */ SPOSCB_ADDR,BG_TREE_SCB_ADDR, + /* 2 */ DSP_SPOS_DC,0, + /* 3 */ DSP_SPOS_DC,DSP_SPOS_DC, + /* 4 */ 0,0, + /* 5 */ DSP_SPOS_UU,0, + /* 6 */ FG_TASK_HEADER_ADDR,0, + /* 7 */ 0,0, + /* 8 */ DSP_SPOS_UU,DSP_SPOS_DC, + /* 9 */ 0, + /* A */ 0,HFG_FIRST_EXECUTE_MODE, + /* B */ DSP_SPOS_UU,DSP_SPOS_UU, + /* C */ DSP_SPOS_DC_DC, + /* D */ DSP_SPOS_DC_DC, + /* E */ DSP_SPOS_DC_DC, + /* F */ DSP_SPOS_DC_DC + }; + + cs46xx_dsp_create_task_tree(chip, "sposCB", (u32 *)&sposcb, SPOSCB_ADDR, 0x10); + + null_algorithm = cs46xx_dsp_lookup_symbol(chip, "NULLALGORITHM", SYMBOL_CODE); + if (null_algorithm == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n"); + return -EIO; + } + + fg_task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "FGTASKTREEHEADERCODE", SYMBOL_CODE); + if (fg_task_tree_header_code == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n"); + return -EIO; + } + + task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "TASKTREEHEADERCODE", SYMBOL_CODE); + if (task_tree_header_code == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol TASKTREEHEADERCODE not found\n"); + return -EIO; + } + + task_tree_thread = cs46xx_dsp_lookup_symbol(chip, "TASKTREETHREAD", SYMBOL_CODE); + if (task_tree_thread == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol TASKTREETHREAD not found\n"); + return -EIO; + } + + magic_snoop_task = cs46xx_dsp_lookup_symbol(chip, "MAGICSNOOPTASK", SYMBOL_CODE); + if (magic_snoop_task == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol MAGICSNOOPTASK not found\n"); + return -EIO; + } + + { + /* create the null SCB */ + generic_scb_t null_scb = { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + NULL_SCB_ADDR, NULL_SCB_ADDR, + null_algorithm->address, 0, + 0,0,0, + { + 0,0, + 0,0, + } + }; + + ins->the_null_scb = cs46xx_dsp_create_scb(chip, "nullSCB", (u32 *)&null_scb, NULL_SCB_ADDR); + ins->the_null_scb->task_entry = null_algorithm; + ins->the_null_scb->sub_list_ptr = ins->the_null_scb; + ins->the_null_scb->next_scb_ptr = ins->the_null_scb; + ins->the_null_scb->parent_scb_ptr = NULL; + cs46xx_dsp_proc_register_scb_desc (chip,ins->the_null_scb); + } + + { + /* setup foreground task tree */ + task_tree_control_block_t fg_task_tree_hdr = { + { FG_TASK_HEADER_ADDR | (DSP_SPOS_DC << 0x10), + DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + 0x0000,DSP_SPOS_DC, + DSP_SPOS_DC, DSP_SPOS_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC,DSP_SPOS_DC }, + + { + BG_TREE_SCB_ADDR,TIMINGMASTER_SCB_ADDR, + fg_task_tree_header_code->address, + FG_TASK_HEADER_ADDR + TCBData, + }, + + { + 4,0, + 1,0, + 2,SPOSCB_ADDR + HFGFlags, + 0,0, + FG_TASK_HEADER_ADDR + TCBContextBlk,FG_STACK + }, + + { + DSP_SPOS_DC,task_tree_thread->address, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DCDC, + DSP_SPOS_UU,1, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC + }, + { + FG_INTERVAL_TIMER_PERIOD,DSP_SPOS_UU, + 0,0 + } + }; + + cs46xx_dsp_create_task_tree(chip,"FGtaskTreeHdr",(u32 *)&fg_task_tree_hdr,FG_TASK_HEADER_ADDR,0x35); + } + + + { + /* setup foreground task tree */ + task_tree_control_block_t bg_task_tree_hdr = { + { DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC, DSP_SPOS_DC, + DSP_SPOS_DC, DSP_SPOS_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC_DC, + DSP_SPOS_DC,DSP_SPOS_DC }, + + { + NULL_SCB_ADDR,NULL_SCB_ADDR, /* Set up the background to do nothing */ + task_tree_header_code->address, + BG_TREE_SCB_ADDR + TCBData, + }, + + { + 9999,0, + 0,1, + 0,SPOSCB_ADDR + HFGFlags, + 0,0, + BG_TREE_SCB_ADDR + TCBContextBlk,BG_STACK + }, + + { + DSP_SPOS_DC,task_tree_thread->address, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DC,DSP_SPOS_DC, + DSP_SPOS_DCDC, + DSP_SPOS_UU,1, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC, + DSP_SPOS_DCDC + }, + { + BG_INTERVAL_TIMER_PERIOD,DSP_SPOS_UU, + 0,0 + } + }; + cs46xx_dsp_create_task_tree(chip,"BGtaskTreeHdr",(u32 *)&bg_task_tree_hdr,BG_TREE_SCB_ADDR,0x35); + } + + /* create timing master SCB */ + timing_master_scb = cs46xx_dsp_create_timing_master_scb(chip); + + /* create the CODEC output task */ + codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_I",0x0010,0x0000, + MASTERMIX_SCB_ADDR, + CODECOUT_SCB_ADDR,timing_master_scb, + SCB_ON_PARENT_SUBLIST_SCB); + + if (!codec_out_scb) goto _fail_end; + /* create the master mix SCB */ + master_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"MasterMixSCB", + MIX_SAMPLE_BUF1,MASTERMIX_SCB_ADDR, + codec_out_scb, + SCB_ON_PARENT_SUBLIST_SCB); + ins->master_mix_scb = master_mix_scb; + + if (!master_mix_scb) goto _fail_end; + + /* create codec in */ + codec_in_scb = cs46xx_dsp_create_codec_in_scb(chip,"CodecInSCB",0x0010,0x00A0, + CODEC_INPUT_BUF1, + CODECIN_SCB_ADDR,codec_out_scb, + SCB_ON_PARENT_NEXT_SCB); + if (!codec_in_scb) goto _fail_end; + ins->codec_in_scb = codec_in_scb; + + /* create write back scb */ + write_back_scb = cs46xx_dsp_create_mix_to_ostream_scb(chip,"WriteBackSCB", + WRITE_BACK_BUF1,WRITE_BACK_SPB, + WRITEBACK_SCB_ADDR, + timing_master_scb, + SCB_ON_PARENT_NEXT_SCB); + if (!write_back_scb) goto _fail_end; + + { + mix2_ostream_spb_t mix2_ostream_spb = { + 0x00020000, + 0x0000ffff + }; + + /* dirty hack ... */ + _dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2); + } + + /* input sample converter */ + vari_decimate_scb = cs46xx_dsp_create_vari_decimate_scb(chip,"VariDecimateSCB", + VARI_DECIMATE_BUF0, + VARI_DECIMATE_BUF1, + VARIDECIMATE_SCB_ADDR, + write_back_scb, + SCB_ON_PARENT_SUBLIST_SCB); + if (!vari_decimate_scb) goto _fail_end; + + /* create the record mixer SCB */ + record_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RecordMixerSCB", + MIX_SAMPLE_BUF2, + RECORD_MIXER_SCB_ADDR, + vari_decimate_scb, + SCB_ON_PARENT_SUBLIST_SCB); + ins->record_mixer_scb = record_mix_scb; + + if (!record_mix_scb) goto _fail_end; + + /* create secondary CODEC output */ + sec_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_II",0x0010,0x0040, + REAR_MIXER_SCB_ADDR, + SEC_CODECOUT_SCB_ADDR,codec_in_scb, + SCB_ON_PARENT_NEXT_SCB); + if (!sec_codec_out_scb) goto _fail_end; + + + /* create the rear PCM channel mixer SCB */ + rear_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RearMixerSCB", + MIX_SAMPLE_BUF3, + REAR_MIXER_SCB_ADDR, + sec_codec_out_scb, + SCB_ON_PARENT_SUBLIST_SCB); + ins->rear_mix_scb = rear_mix_scb; + if (!rear_mix_scb) goto _fail_end; + + /* the magic snooper */ + magic_snoop_scb = cs46xx_dsp_create_magic_snoop_scb (chip,"MagicSnoopSCB_I",OUTPUTSNOOP_SCB_ADDR, + OUTPUT_SNOOP_BUFFER, + codec_out_scb, + sec_codec_out_scb, + SCB_ON_PARENT_NEXT_SCB); + + + if (!magic_snoop_scb) goto _fail_end; + ins->ref_snoop_scb = magic_snoop_scb; + + /* SP IO access */ + if (!cs46xx_dsp_create_spio_write_scb(chip,"SPIOWriteSCB",SPIOWRITE_SCB_ADDR, + magic_snoop_scb, + SCB_ON_PARENT_NEXT_SCB)) + goto _fail_end; + + /* SPDIF input sampel rate converter */ + src_task_scb = cs46xx_dsp_create_src_task_scb(chip,"SrcTaskSCB_SPDIFI", + ins->spdif_in_sample_rate, + SRC_OUTPUT_BUF1, + SRC_DELAY_BUF1,SRCTASK_SCB_ADDR, + master_mix_scb, + SCB_ON_PARENT_SUBLIST_SCB,0); + + if (!src_task_scb) goto _fail_end; + + cs46xx_src_unlink(chip,src_task_scb); + + /* NOTE: when we now how to detect the SPDIF input + sample rate we will use this SRC to adjust it */ + ins->spdif_in_src = src_task_scb; + + cs46xx_dsp_async_init(chip,timing_master_scb); + return 0; + + _fail_end: + snd_printk(KERN_ERR "dsp_spos: failed to setup SCB's in DSP\n"); + return -EINVAL; +} + +int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + symbol_entry_t * s16_async_codec_input_task; + symbol_entry_t * spdifo_task; + symbol_entry_t * spdifi_task; + dsp_scb_descriptor_t * spdifi_scb_desc,* spdifo_scb_desc,* async_codec_scb_desc; + + s16_async_codec_input_task = cs46xx_dsp_lookup_symbol(chip, "S16_ASYNCCODECINPUTTASK", SYMBOL_CODE); + if (s16_async_codec_input_task == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n"); + return -EIO; + } + spdifo_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFOTASK", SYMBOL_CODE); + if (spdifo_task == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol SPDIFOTASK not found\n"); + return -EIO; + } + + spdifi_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFITASK", SYMBOL_CODE); + if (spdifi_task == NULL) { + snd_printk(KERN_ERR "dsp_spos: symbol SPDIFITASK not found\n"); + return -EIO; + } + + { + /* 0xBC0 */ + spdifoscb_t spdifo_scb = { + /* 0 */ DSP_SPOS_UUUU, + { + /* 1 */ 0xb0, + /* 2 */ 0, + /* 3 */ 0, + /* 4 */ 0, + }, + /* NOTE: the SPDIF output task read samples in mono + format, the AsynchFGTxSCB task writes to buffer + in stereo format + */ + /* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_256, + /* 6 */ ( SPDIFO_IP_OUTPUT_BUFFER1 << 0x10 ) | 0xFFFC, + /* 7 */ 0,0, + /* 8 */ 0, + /* 9 */ FG_TASK_HEADER_ADDR, NULL_SCB_ADDR, + /* A */ spdifo_task->address, + SPDIFO_SCB_INST + SPDIFOFIFOPointer, + { + /* B */ 0x0040, /*DSP_SPOS_UUUU,*/ + /* C */ 0x20ff, /*DSP_SPOS_UUUU,*/ + }, + /* D */ 0x804c,0, /* SPDIFOFIFOPointer:SPDIFOStatRegAddr; */ + /* E */ 0x0108,0x0001, /* SPDIFOStMoFormat:SPDIFOFIFOBaseAddr; */ + /* F */ DSP_SPOS_UUUU /* SPDIFOFree; */ + }; + + /* 0xBB0 */ + spdifiscb_t spdifi_scb = { + /* 0 */ DSP_SPOS_UULO,DSP_SPOS_UUHI, + /* 1 */ 0, + /* 2 */ 0, + /* 3 */ 1,4000, + /* 4 */ DSP_SPOS_UUUU, + /* 5 */ DSP_SPOS_UULO,DSP_SPOS_UUHI, + /* 6 */ DSP_SPOS_UUUU, + /* 7 */ DSP_SPOS_UU,DSP_SPOS_DC, + /* 8 */ DSP_SPOS_UUUU, + /* 9 */ SPDIFO_SCB_INST, NULL_SCB_ADDR, + /* A */ spdifi_task->address, + SPDIFI_SCB_INST + SPDIFIFIFOPointer, + /* NOTE: The SPDIF input task write the sample in mono + format from the HW FIFO, the AsynchFGRxSCB task reads + them in stereo + */ + /* B */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_128, + /* C */ (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC, + /* D */ 0x8048,0, + /* E */ 0x01f0,0x0001, + /* F */ DSP_SPOS_UUUU + }; + + /* 0xBA0 */ + async_codec_input_scb_t async_codec_input_scb = { + /* 0 */ DSP_SPOS_UUUU, + /* 1 */ 0, + /* 2 */ 0, + /* 3 */ 1,4000, + /* 4 */ 0x0118,0x0001, + /* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_64, + /* 6 */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC, + /* 7 */ DSP_SPOS_UU,0x3, + /* 8 */ DSP_SPOS_UUUU, + /* 9 */ SPDIFI_SCB_INST,NULL_SCB_ADDR, + /* A */ s16_async_codec_input_task->address, + HFG_TREE_SCB + AsyncCIOFIFOPointer, + + /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64, + /* C */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10), /*(ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,*/ + +#ifdef UseASER1Input + /* short AsyncCIFIFOPointer:AsyncCIStatRegAddr; + Init. 0000:8042: for ASER1 + 0000:8044: for ASER2 */ + /* D */ 0x8042,0, + + /* short AsyncCIStMoFormat:AsyncCIFIFOBaseAddr; + Init 1 stero:8050 ASER1 + Init 0 mono:8070 ASER2 + Init 1 Stereo : 0100 ASER1 (Set by script) */ + /* E */ 0x0100,0x0001, + +#endif + +#ifdef UseASER2Input + /* short AsyncCIFIFOPointer:AsyncCIStatRegAddr; + Init. 0000:8042: for ASER1 + 0000:8044: for ASER2 */ + /* D */ 0x8044,0, + + /* short AsyncCIStMoFormat:AsyncCIFIFOBaseAddr; + Init 1 stero:8050 ASER1 + Init 0 mono:8070 ASER2 + Init 1 Stereo : 0100 ASER1 (Set by script) */ + /* E */ 0x0110,0x0001, + +#endif + + /* short AsyncCIOutputBufModulo:AsyncCIFree; + AsyncCIOutputBufModulo: The modulo size for + the output buffer of this task */ + /* F */ 0, /* DSP_SPOS_UUUU */ + }; + + spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST); + + snd_assert(spdifo_scb_desc, return -EIO); + spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST); + snd_assert(spdifi_scb_desc, return -EIO); + async_codec_scb_desc = cs46xx_dsp_create_scb(chip,"AsynCodecInputSCB",(u32 *)&async_codec_input_scb, HFG_TREE_SCB); + snd_assert(async_codec_scb_desc, return -EIO); + + async_codec_scb_desc->parent_scb_ptr = NULL; + async_codec_scb_desc->next_scb_ptr = spdifi_scb_desc; + async_codec_scb_desc->sub_list_ptr = ins->the_null_scb; + async_codec_scb_desc->task_entry = s16_async_codec_input_task; + + spdifi_scb_desc->parent_scb_ptr = async_codec_scb_desc; + spdifi_scb_desc->next_scb_ptr = spdifo_scb_desc; + spdifi_scb_desc->sub_list_ptr = ins->the_null_scb; + spdifi_scb_desc->task_entry = spdifi_task; + + spdifo_scb_desc->parent_scb_ptr = spdifi_scb_desc; + spdifo_scb_desc->next_scb_ptr = fg_entry; + spdifo_scb_desc->sub_list_ptr = ins->the_null_scb; + spdifo_scb_desc->task_entry = spdifo_task; + + /* this one is faked, as the parnet of SPDIFO task + is the FG task tree */ + fg_entry->parent_scb_ptr = spdifo_scb_desc; + + /* for proc fs */ + cs46xx_dsp_proc_register_scb_desc (chip,spdifo_scb_desc); + cs46xx_dsp_proc_register_scb_desc (chip,spdifi_scb_desc); + cs46xx_dsp_proc_register_scb_desc (chip,async_codec_scb_desc); + + /* Async MASTER ENABLE, affects both SPDIF input and output */ + snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 ); + } + + return 0; +} + + +static void cs46xx_dsp_disable_spdif_hw (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + /* set SPDIF output FIFO slot */ + snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, 0); + + /* SPDIF output MASTER ENABLE */ + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0); + + /* right and left validate bit */ + /*cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);*/ + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, 0x0); + + /* monitor state */ + ins->spdif_status_out &= ~DSP_SPDIF_STATUS_HW_ENABLED; +} + +int cs46xx_dsp_enable_spdif_hw (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + /* if hw-ctrl already enabled, turn off to reset logic ... */ + cs46xx_dsp_disable_spdif_hw (chip); + udelay(50); + + /* set SPDIF output FIFO slot */ + snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, ( 0x8000 | ((SP_SPDOUT_FIFO >> 4) << 4) )); + + /* SPDIF output MASTER ENABLE */ + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x80000000); + + /* right and left validate bit */ + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default); + + /* monitor state */ + ins->spdif_status_out |= DSP_SPDIF_STATUS_HW_ENABLED; + + return 0; +} + +int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + /* turn on amplifier */ + chip->active_ctrl(chip, 1); + chip->amplifier_ctrl(chip, 1); + + snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL); + snd_assert (ins->spdif_in_src != NULL,return -EINVAL); + + down(&chip->spos_mutex); + /* create and start the asynchronous receiver SCB */ + ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB", + ASYNCRX_SCB_ADDR, + SPDIFI_SCB_INST, + SPDIFI_IP_OUTPUT_BUFFER1, + ins->spdif_in_src, + SCB_ON_PARENT_SUBLIST_SCB); + + spin_lock_irq(&chip->reg_lock); + + /* reset SPDIF input sample buffer pointer */ + snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2, + (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC); + + /* reset FIFO ptr */ + cs46xx_poke_via_dsp (chip,SP_SPDIN_FIFOPTR, 0x0); + cs46xx_src_link(chip,ins->spdif_in_src); + + /* unmute SRC volume */ + cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,0x7fff,0x7fff); + + spin_unlock_irq(&chip->reg_lock); + + /* set SPDIF input sample rate and unmute + NOTE: only 48khz support for SPDIF input this time */ + /* cs46xx_dsp_set_src_sample_rate(chip,ins->spdif_in_src,48000); */ + + /* monitor state */ + ins->spdif_status_in = 1; + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL); + snd_assert (ins->spdif_in_src != NULL,return -EINVAL); + + down(&chip->spos_mutex); + /* Remove the asynchronous receiver SCB */ + cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb); + ins->asynch_rx_scb = NULL; + + cs46xx_src_unlink(chip,ins->spdif_in_src); + + /* monitor state */ + ins->spdif_status_in = 0; + up(&chip->spos_mutex); + + /* restore amplifier */ + chip->active_ctrl(chip, -1); + chip->amplifier_ctrl(chip, -1); + + return 0; +} + +int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert (ins->pcm_input == NULL,return -EINVAL); + snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL); + + down(&chip->spos_mutex); + ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR, + "PCMSerialInput_Wave"); + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert (ins->pcm_input != NULL,return -EINVAL); + + down(&chip->spos_mutex); + cs46xx_dsp_remove_scb (chip,ins->pcm_input); + ins->pcm_input = NULL; + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert (ins->adc_input == NULL,return -EINVAL); + snd_assert (ins->codec_in_scb != NULL,return -EINVAL); + + down(&chip->spos_mutex); + ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR, + "PCMSerialInput_ADC"); + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert (ins->adc_input != NULL,return -EINVAL); + + down(&chip->spos_mutex); + cs46xx_dsp_remove_scb (chip,ins->adc_input); + ins->adc_input = NULL; + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data) +{ + u32 temp; + int i; + + /* santiy check the parameters. (These numbers are not 100% correct. They are + a rough guess from looking at the controller spec.) */ + if (address < 0x8000 || address >= 0x9000) + return -EINVAL; + + /* initialize the SP_IO_WRITE SCB with the data. */ + temp = ( address << 16 ) | ( address & 0x0000FFFF); /* offset 0 <-- address2 : address1 */ + + snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR << 2), temp); + snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */ + snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2 */ + + /* Poke this location to tell the task to start */ + snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10); + + /* Verify that the task ran */ + for (i=0; i<25; i++) { + udelay(125); + + temp = snd_cs46xx_peek(chip,((SPIOWRITE_SCB_ADDR + 6) << 2)); + if (temp == 0x00000000) + break; + } + + if (i == 25) { + snd_printk(KERN_ERR "dsp_spos: SPIOWriteTask not responding\n"); + return -EBUSY; + } + + return 0; +} + +int cs46xx_dsp_set_dac_volume (cs46xx_t * chip,u16 left,u16 right) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * scb; + + down(&chip->spos_mutex); + + /* main output */ + scb = ins->master_mix_scb->sub_list_ptr; + while (scb != ins->the_null_scb) { + cs46xx_dsp_scb_set_volume (chip,scb,left,right); + scb = scb->next_scb_ptr; + } + + /* rear output */ + scb = ins->rear_mix_scb->sub_list_ptr; + while (scb != ins->the_null_scb) { + cs46xx_dsp_scb_set_volume (chip,scb,left,right); + scb = scb->next_scb_ptr; + } + + ins->dac_volume_left = left; + ins->dac_volume_right = right; + + up(&chip->spos_mutex); + + return 0; +} + +int cs46xx_dsp_set_iec958_volume (cs46xx_t * chip,u16 left,u16 right) { + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + down(&chip->spos_mutex); + + if (ins->asynch_rx_scb != NULL) + cs46xx_dsp_scb_set_volume (chip,ins->asynch_rx_scb, + left,right); + + ins->spdif_input_volume_left = left; + ins->spdif_input_volume_right = right; + + up(&chip->spos_mutex); + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/dsp_spos.h linux/sound/pci/cs46xx/dsp_spos.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/dsp_spos.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/dsp_spos.h 2002-12-10 08:18:29.000000000 -0700 @@ -0,0 +1,224 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * 2002-07 Benny Sjostrand benny@hostmobility.com + */ + +#ifdef CONFIG_SND_CS46XX_NEW_DSP /* hack ... */ +#ifndef __DSP_SPOS_H__ +#define __DSP_SPOS_H__ + +#define DSP_MAX_SYMBOLS 1024 +#define DSP_MAX_MODULES 64 + +#define DSP_CODE_BYTE_SIZE 0x00007000UL +#define DSP_PARAMETER_BYTE_SIZE 0x00003000UL +#define DSP_SAMPLE_BYTE_SIZE 0x00003800UL +#define DSP_PARAMETER_BYTE_OFFSET 0x00000000UL +#define DSP_SAMPLE_BYTE_OFFSET 0x00010000UL +#define DSP_CODE_BYTE_OFFSET 0x00020000UL + +#define WIDE_INSTR_MASK 0x0040 +#define WIDE_LADD_INSTR_MASK 0x0380 + +/* this instruction types + needs to be reallocated when load + code into DSP */ +typedef enum { + WIDE_FOR_BEGIN_LOOP = 0x20, + WIDE_FOR_BEGIN_LOOP2, + + WIDE_COND_GOTO_ADDR = 0x30, + WIDE_COND_GOTO_CALL, + + WIDE_TBEQ_COND_GOTO_ADDR = 0x70, + WIDE_TBEQ_COND_CALL_ADDR, + WIDE_TBEQ_NCOND_GOTO_ADDR, + WIDE_TBEQ_NCOND_CALL_ADDR, + WIDE_TBEQ_COND_GOTO1_ADDR, + WIDE_TBEQ_COND_CALL1_ADDR, + WIDE_TBEQ_NCOND_GOTOI_ADDR, + WIDE_TBEQ_NCOND_CALL1_ADDR, +} wide_opcode_t; + +/* SAMPLE segment */ +#define VARI_DECIMATE_BUF1 0x0000 +#define WRITE_BACK_BUF1 0x0400 +#define CODEC_INPUT_BUF1 0x0500 +#define PCM_READER_BUF1 0x0600 +#define SRC_DELAY_BUF1 0x0680 +#define VARI_DECIMATE_BUF0 0x0780 +#define SRC_OUTPUT_BUF1 0x07A0 +#define ASYNC_IP_OUTPUT_BUFFER1 0x0A00 +#define OUTPUT_SNOOP_BUFFER 0x0B00 +#define SPDIFI_IP_OUTPUT_BUFFER1 0x0E00 +#define SPDIFO_IP_OUTPUT_BUFFER1 0x1000 +#define MIX_SAMPLE_BUF1 0x1400 +#define MIX_SAMPLE_BUF2 0x2D00 +#define MIX_SAMPLE_BUF3 0x2E00 +#define MIX_SAMPLE_BUF4 0x2F00 +#define MIX_SAMPLE_BUF5 0x3000 + +/* Task stack address */ +#define HFG_STACK 0x066A +#define FG_STACK 0x066E +#define BG_STACK 0x068E + +/* SCB's addresses */ +#define SPOSCB_ADDR 0x070 +#define BG_TREE_SCB_ADDR 0x635 +#define NULL_SCB_ADDR 0x000 +#define TIMINGMASTER_SCB_ADDR 0x010 +#define CODECOUT_SCB_ADDR 0x020 +#define PCMREADER_SCB_ADDR 0x030 +#define WRITEBACK_SCB_ADDR 0x040 +#define CODECIN_SCB_ADDR 0x080 +#define MASTERMIX_SCB_ADDR 0x090 +#define SRCTASK_SCB_ADDR 0x0A0 +#define VARIDECIMATE_SCB_ADDR 0x0B0 +#define PCMSERIALIN_SCB_ADDR 0x0C0 +#define FG_TASK_HEADER_ADDR 0x600 +#define ASYNCTX_SCB_ADDR 0x0E0 +#define ASYNCRX_SCB_ADDR 0x0F0 +#define SRCTASKII_SCB_ADDR 0x100 +#define OUTPUTSNOOP_SCB_ADDR 0x110 +#define PCMSERIALINII_SCB_ADDR 0x120 +#define SPIOWRITE_SCB_ADDR 0x130 +#define SEC_CODECOUT_SCB_ADDR 0x140 +#define OUTPUTSNOOPII_SCB_ADDR 0x150 +#define PCMSERIALIN_PCM_SCB_ADDR 0x160 +#define RECORD_MIXER_SCB_ADDR 0x170 +#define REAR_MIXER_SCB_ADDR 0x180 +#define SPDIF_MIXER_SCB_ADDR 0x190 + +/* hyperforground SCB's*/ +#define HFG_TREE_SCB 0xBA0 +#define SPDIFI_SCB_INST 0xBB0 +#define SPDIFO_SCB_INST 0xBC0 +#define WRITE_BACK_SPB 0x0D0 + +/* offsets */ +#define AsyncCIOFIFOPointer 0xd +#define SPDIFOFIFOPointer 0xd +#define SPDIFIFIFOPointer 0xd +#define TCBData 0xb +#define HFGFlags 0xa +#define TCBContextBlk 0x10 +#define AFGTxAccumPhi 0x4 +#define SCBsubListPtr 0x9 +#define SCBfuncEntryPtr 0xA +#define SRCCorPerGof 0x2 +#define SRCPhiIncr6Int26Frac 0xd +#define SCBVolumeCtrl 0xe + +/* conf */ +#define UseASER1Input 1 + + + +/* + * The following defines are for the flags in the rsConfig01/23 registers of + * the SP. + */ + +#define RSCONFIG_MODULO_SIZE_MASK 0x0000000FL +#define RSCONFIG_MODULO_16 0x00000001L +#define RSCONFIG_MODULO_32 0x00000002L +#define RSCONFIG_MODULO_64 0x00000003L +#define RSCONFIG_MODULO_128 0x00000004L +#define RSCONFIG_MODULO_256 0x00000005L +#define RSCONFIG_MODULO_512 0x00000006L +#define RSCONFIG_MODULO_1024 0x00000007L +#define RSCONFIG_MODULO_4 0x00000008L +#define RSCONFIG_MODULO_8 0x00000009L +#define RSCONFIG_SAMPLE_SIZE_MASK 0x000000C0L +#define RSCONFIG_SAMPLE_8MONO 0x00000000L +#define RSCONFIG_SAMPLE_8STEREO 0x00000040L +#define RSCONFIG_SAMPLE_16MONO 0x00000080L +#define RSCONFIG_SAMPLE_16STEREO 0x000000C0L +#define RSCONFIG_UNDERRUN_ZERO 0x00004000L +#define RSCONFIG_DMA_TO_HOST 0x00008000L +#define RSCONFIG_STREAM_NUM_MASK 0x00FF0000L +#define RSCONFIG_MAX_DMA_SIZE_MASK 0x1F000000L +#define RSCONFIG_DMA_ENABLE 0x20000000L +#define RSCONFIG_PRIORITY_MASK 0xC0000000L +#define RSCONFIG_PRIORITY_HIGH 0x00000000L +#define RSCONFIG_PRIORITY_MEDIUM_HIGH 0x40000000L +#define RSCONFIG_PRIORITY_MEDIUM_LOW 0x80000000L +#define RSCONFIG_PRIORITY_LOW 0xC0000000L +#define RSCONFIG_STREAM_NUM_SHIFT 16L +#define RSCONFIG_MAX_DMA_SIZE_SHIFT 24L + +/* SP constants */ +#define FG_INTERVAL_TIMER_PERIOD 0x0051 +#define BG_INTERVAL_TIMER_PERIOD 0x0100 + + +/* Only SP accesible registers */ +#define SP_ASER_COUNTDOWN 0x8040 +#define SP_SPDOUT_FIFO 0x0108 +#define SP_SPDIN_MI_FIFO 0x01E0 +#define SP_SPDIN_D_FIFO 0x01F0 +#define SP_SPDIN_STATUS 0x8048 +#define SP_SPDIN_CONTROL 0x8049 +#define SP_SPDIN_FIFOPTR 0x804A +#define SP_SPDOUT_STATUS 0x804C +#define SP_SPDOUT_CONTROL 0x804D +#define SP_SPDOUT_CSUV 0x808E + +static inline u8 _wrap_all_bits (u8 val) { + u8 wrapped; + + /* wrap all 8 bits */ + wrapped = + ((val & 0x1 ) << 7) | + ((val & 0x2 ) << 5) | + ((val & 0x4 ) << 3) | + ((val & 0x8 ) << 1) | + ((val & 0x10) >> 1) | + ((val & 0x20) >> 3) | + ((val & 0x40) >> 5) | + ((val & 0x80) >> 7); + + return wrapped; + +} + + +static inline void cs46xx_dsp_spos_update_scb (cs46xx_t * chip,dsp_scb_descriptor_t * scb) +{ + /* update nextSCB and subListPtr in SCB */ + snd_cs46xx_poke(chip, + (scb->address + SCBsubListPtr) << 2, + (scb->sub_list_ptr->address << 0x10) | + (scb->next_scb_ptr->address)); +} + +static inline void cs46xx_dsp_scb_set_volume (cs46xx_t * chip,dsp_scb_descriptor_t * scb, + u16 left,u16 right) { + unsigned int val = ((0xffff - left) << 16 | (0xffff - right)); + + snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val); + snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val); +} +#endif /* __DSP_SPOS_H__ */ +#endif /* CONFIG_SND_CS46XX_NEW_DSP */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/dsp_spos_scb_lib.c linux/sound/pci/cs46xx/dsp_spos_scb_lib.c --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/dsp_spos_scb_lib.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/dsp_spos_scb_lib.c 2003-03-03 04:33:01.000000000 -0700 @@ -0,0 +1,1677 @@ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * 2002-07 Benny Sjostrand benny@hostmobility.com + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs46xx_lib.h" +#include "dsp_spos.h" + +typedef struct _proc_scb_info_t { + dsp_scb_descriptor_t * scb_desc; + cs46xx_t *chip; +} proc_scb_info_t; + +static void remove_symbol (cs46xx_t * chip,symbol_entry_t * symbol) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + int symbol_index = (int)(symbol - ins->symbol_table.symbols); + + snd_assert(ins->symbol_table.nsymbols > 0,return); + snd_assert(symbol_index >= 0 && symbol_index < ins->symbol_table.nsymbols, return); + + ins->symbol_table.symbols[symbol_index].deleted = 1; + + if (symbol_index < ins->symbol_table.highest_frag_index) { + ins->symbol_table.highest_frag_index = symbol_index; + } + + if (symbol_index == ins->symbol_table.nsymbols - 1) + ins->symbol_table.nsymbols --; + + if (ins->symbol_table.highest_frag_index > ins->symbol_table.nsymbols) { + ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols; + } + +} + +static void cs46xx_dsp_proc_scb_info_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + proc_scb_info_t * scb_info = (proc_scb_info_t *)entry->private_data; + dsp_scb_descriptor_t * scb = scb_info->scb_desc; + dsp_spos_instance_t * ins; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, scb_info->chip, return); + int j,col; + unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; + + ins = chip->dsp_spos_instance; + + down(&chip->spos_mutex); + snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name); + + for (col = 0,j = 0;j < 0x10; j++,col++) { + if (col == 4) { + snd_iprintf(buffer,"\n"); + col = 0; + } + snd_iprintf(buffer,"%08x ",readl(dst + (scb->address + j) * sizeof(u32))); + } + + snd_iprintf(buffer,"\n"); + + if (scb->parent_scb_ptr != NULL) { + snd_iprintf(buffer,"parent [%s:%04x] ", + scb->parent_scb_ptr->scb_name, + scb->parent_scb_ptr->address); + } else snd_iprintf(buffer,"parent [none] "); + + snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x] task_entry [%s:%04x]\n", + scb->sub_list_ptr->scb_name, + scb->sub_list_ptr->address, + scb->next_scb_ptr->scb_name, + scb->next_scb_ptr->address, + scb->task_entry->symbol_name, + scb->task_entry->address); + + snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count); + up(&chip->spos_mutex); +} + +static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + unsigned long flags; + + if ( scb->parent_scb_ptr ) { + /* unlink parent SCB */ + snd_assert ((scb->parent_scb_ptr->sub_list_ptr == scb || + scb->parent_scb_ptr->next_scb_ptr == scb),return); + + if (scb->parent_scb_ptr->sub_list_ptr == scb) { + + if (scb->next_scb_ptr == ins->the_null_scb) { + /* last and only node in parent sublist */ + scb->parent_scb_ptr->sub_list_ptr = scb->sub_list_ptr; + + if (scb->sub_list_ptr != ins->the_null_scb) { + scb->sub_list_ptr->parent_scb_ptr = scb->parent_scb_ptr; + } + scb->sub_list_ptr = ins->the_null_scb; + } else { + /* first node in parent sublist */ + scb->parent_scb_ptr->sub_list_ptr = scb->next_scb_ptr; + + if (scb->next_scb_ptr != ins->the_null_scb) { + /* update next node parent ptr. */ + scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr; + } + scb->next_scb_ptr = ins->the_null_scb; + } + } else { + /* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */ + scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr; + + if (scb->next_scb_ptr != ins->the_null_scb) { + /* update next node parent ptr. */ + scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr; + } + scb->next_scb_ptr = ins->the_null_scb; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + + /* update parent first entry in DSP RAM */ + cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr); + + /* then update entry in DSP RAM */ + cs46xx_dsp_spos_update_scb(chip,scb); + + scb->parent_scb_ptr = NULL; + spin_unlock_irqrestore(&chip->reg_lock, flags); + } +} + +static void _dsp_clear_sample_buffer (cs46xx_t *chip, u32 sample_buffer_addr, int dword_count) +{ + unsigned long dst = chip->region.idx[2].remap_addr + sample_buffer_addr; + int i; + + for (i = 0; i < dword_count ; ++i ) { + writel(0, dst); + dst += 4; + } +} + +void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + /* check integrety */ + snd_assert ( (scb->index >= 0 && + scb->index < ins->nscb && + (ins->scbs + scb->index) == scb), return ); + +#if 0 + /* can't remove a SCB with childs before + removing childs first */ + snd_assert ( (scb->sub_list_ptr == ins->the_null_scb && + scb->next_scb_ptr == ins->the_null_scb), + goto _end); +#endif + + spin_lock(&scb->lock); + _dsp_unlink_scb (chip,scb); + spin_unlock(&scb->lock); + + cs46xx_dsp_proc_free_scb_desc(scb); + snd_assert (scb->scb_symbol != NULL, return ); + remove_symbol (chip,scb->scb_symbol); + + ins->scbs[scb->index].deleted = 1; + + if (scb->index < ins->scb_highest_frag_index) + ins->scb_highest_frag_index = scb->index; + + if (scb->index == ins->nscb - 1) { + ins->nscb --; + } + + if (ins->scb_highest_frag_index > ins->nscb) { + ins->scb_highest_frag_index = ins->nscb; + } + +#if 0 + /* !!!! THIS IS A PIECE OF SHIT MADE BY ME !!! */ + for(i = scb->index + 1;i < ins->nscb; ++i) { + ins->scbs[i - 1].index = i - 1; + } +#endif +} + + +void cs46xx_dsp_proc_free_scb_desc (dsp_scb_descriptor_t * scb) +{ + if (scb->proc_info) { + proc_scb_info_t * scb_info = (proc_scb_info_t *)scb->proc_info->private_data; + + snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name); + + snd_info_unregister(scb->proc_info); + scb->proc_info = NULL; + + snd_assert (scb_info != NULL, return); + kfree (scb_info); + } +} + +void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * scb) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + snd_info_entry_t * entry; + proc_scb_info_t * scb_info; + + /* register to proc */ + if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL && + scb->proc_info == NULL) { + + if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name, + ins->proc_dsp_dir)) != NULL) { + scb_info = kmalloc(sizeof(proc_scb_info_t), GFP_KERNEL); + scb_info->chip = chip; + scb_info->scb_desc = scb; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = scb_info; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + + entry->c.text.read_size = 512; + entry->c.text.read = cs46xx_dsp_proc_scb_info_read; + + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + kfree (scb_info); + entry = NULL; + } + } + + scb->proc_info = entry; + } +} + +static dsp_scb_descriptor_t * +_dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest, + symbol_entry_t * task_entry, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * scb; + + unsigned long flags; + + snd_assert (ins->the_null_scb != NULL,return NULL); + + /* fill the data that will be wroten to DSP */ + scb_data[SCBsubListPtr] = + (ins->the_null_scb->address << 0x10) | ins->the_null_scb->address; + + scb_data[SCBfuncEntryPtr] &= 0xFFFF0000; + scb_data[SCBfuncEntryPtr] |= task_entry->address; + + snd_printdd("dsp_spos: creating SCB <%s>\n",name); + + scb = cs46xx_dsp_create_scb(chip,name,scb_data,dest); + + + scb->sub_list_ptr = ins->the_null_scb; + scb->next_scb_ptr = ins->the_null_scb; + + scb->parent_scb_ptr = parent_scb; + scb->task_entry = task_entry; + + + /* update parent SCB */ + if (scb->parent_scb_ptr) { +#if 0 + printk ("scb->parent_scb_ptr = %s\n",scb->parent_scb_ptr->scb_name); + printk ("scb->parent_scb_ptr->next_scb_ptr = %s\n",scb->parent_scb_ptr->next_scb_ptr->scb_name); + printk ("scb->parent_scb_ptr->sub_list_ptr = %s\n",scb->parent_scb_ptr->sub_list_ptr->scb_name); +#endif + /* link to parent SCB */ + if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) { + snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb), + return NULL); + + scb->parent_scb_ptr->next_scb_ptr = scb; + + } else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) { + snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb), + return NULL); + + scb->parent_scb_ptr->sub_list_ptr = scb; + } else { + snd_assert (0,return NULL); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + + /* update entry in DSP RAM */ + cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr); + + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + + + cs46xx_dsp_proc_register_scb_desc (chip,scb); + + return scb; +} + +dsp_scb_descriptor_t * +cs46xx_dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest, + char * task_entry_name, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + symbol_entry_t * task_entry; + + task_entry = cs46xx_dsp_lookup_symbol (chip,task_entry_name, + SYMBOL_CODE); + + if (task_entry == NULL) { + snd_printk (KERN_ERR "dsp_spos: symbol %s not found\n",task_entry_name); + return NULL; + } + + return _dsp_create_generic_scb (chip,name,scb_data,dest,task_entry, + parent_scb,scb_child_type); +} + +dsp_scb_descriptor_t * +cs46xx_dsp_create_timing_master_scb (cs46xx_t *chip) +{ + dsp_scb_descriptor_t * scb; + + timing_master_scb_t timing_master_scb = { + { 0, + 0, + 0, + 0 + }, + { 0, + 0, + 0, + 0, + 0 + }, + 0,0, + 0,NULL_SCB_ADDR, + 0,0, /* extraSampleAccum:TMreserved */ + 0,0, /* codecFIFOptr:codecFIFOsyncd */ + 0x0001,0x8000, /* fracSampAccumQm1:TMfrmsLeftInGroup */ + 0x0001,0x0000, /* fracSampCorrectionQm1:TMfrmGroupLength */ + 0x00060000 /* nSampPerFrmQ15 */ + }; + + scb = cs46xx_dsp_create_generic_scb(chip,"TimingMasterSCBInst",(u32 *)&timing_master_scb, + TIMINGMASTER_SCB_ADDR, + "TIMINGMASTER",NULL,SCB_NO_PARENT); + + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_codec_out_scb(cs46xx_t * chip,char * codec_name, + u16 channel_disp,u16 fifo_addr, + u16 child_scb_addr, + u32 dest,dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_scb_descriptor_t * scb; + + codec_output_scb_t codec_out_scb = { + { 0, + 0, + 0, + 0 + }, + { + 0, + 0, + 0, + 0, + 0 + }, + 0,0, + 0,NULL_SCB_ADDR, + 0, /* COstrmRsConfig */ + 0, /* COstrmBufPtr */ + channel_disp,fifo_addr, /* leftChanBaseIOaddr:rightChanIOdisp */ + 0x0000,0x0080, /* (!AC97!) COexpVolChangeRate:COscaleShiftCount */ + 0,child_scb_addr /* COreserved - need child scb to work with rom code */ + }; + + + scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_out_scb, + dest,"S16_CODECOUTPUTTASK",parent_scb, + scb_child_type); + + return scb; +} + +dsp_scb_descriptor_t * +cs46xx_dsp_create_codec_in_scb(cs46xx_t * chip,char * codec_name, + u16 channel_disp,u16 fifo_addr, + u16 sample_buffer_addr, + u32 dest,dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + + dsp_scb_descriptor_t * scb; + codec_input_scb_t codec_input_scb = { + { 0, + 0, + 0, + 0 + }, + { + 0, + 0, + 0, + 0, + 0 + }, + +#if 0 /* cs4620 */ + SyncIOSCB,NULL_SCB_ADDR +#else + 0 , 0, +#endif + 0,0, + + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64, /* strmRsConfig */ + sample_buffer_addr << 0x10, /* strmBufPtr; defined as a dword ptr, used as a byte ptr */ + channel_disp,fifo_addr, /* (!AC97!) leftChanBaseINaddr=AC97primary + link input slot 3 :rightChanINdisp=""slot 4 */ + 0x0000,0x0000, /* (!AC97!) ????:scaleShiftCount; no shift needed + because AC97 is already 20 bits */ + 0x80008000 /* ??clw cwcgame.scb has 0 */ + }; + + scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_input_scb, + dest,"S16_CODECINPUTTASK",parent_scb, + scb_child_type); + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_pcm_reader_scb(cs46xx_t * chip,char * scb_name, + u16 sample_buffer_addr,u32 dest, + int virtual_channel, u32 playback_hw_addr, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * scb; + + generic_scb_t pcm_reader_scb = { + + /* + Play DMA Task xfers data from host buffer to SP buffer + init/runtime variables: + PlayAC: Play Audio Data Conversion - SCB loc: 2nd dword, mask: 0x0000F000L + DATA_FMT_16BIT_ST_LTLEND(0x00000000L) from 16-bit stereo, little-endian + DATA_FMT_8_BIT_ST_SIGNED(0x00001000L) from 8-bit stereo, signed + DATA_FMT_16BIT_MN_LTLEND(0x00002000L) from 16-bit mono, little-endian + DATA_FMT_8_BIT_MN_SIGNED(0x00003000L) from 8-bit mono, signed + DATA_FMT_16BIT_ST_BIGEND(0x00004000L) from 16-bit stereo, big-endian + DATA_FMT_16BIT_MN_BIGEND(0x00006000L) from 16-bit mono, big-endian + DATA_FMT_8_BIT_ST_UNSIGNED(0x00009000L) from 8-bit stereo, unsigned + DATA_FMT_8_BIT_MN_UNSIGNED(0x0000b000L) from 8-bit mono, unsigned + ? Other combinations possible from: + DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000L + DMA_RQ_C2_AC_NONE 0x00000000L + DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000L + DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000L + DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000L + DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000L + + HostBuffAddr: Host Buffer Physical Byte Address - SCB loc:3rd dword, Mask: 0xFFFFFFFFL + aligned to dword boundary + */ + /* Basic (non scatter/gather) DMA requestor (4 ints) */ + { DMA_RQ_C1_SOURCE_ON_HOST + /* source buffer is on the host */ + DMA_RQ_C1_SOURCE_MOD1024 + /* source buffer is 1024 dwords (4096 bytes) */ + DMA_RQ_C1_DEST_MOD32 + /* dest buffer(PCMreaderBuf) is 32 dwords*/ + DMA_RQ_C1_WRITEBACK_SRC_FLAG + /* ?? */ + DMA_RQ_C1_WRITEBACK_DEST_FLAG + /* ?? */ + 15, /* DwordCount-1: picked 16 for DwordCount because Jim */ + /* Barnette said that is what we should use since */ + /* we are not running in optimized mode? */ + DMA_RQ_C2_AC_NONE + + DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG + /* set play interrupt (bit0) in HISR when source */ + /* buffer (on host) crosses half-way point */ + virtual_channel, /* Play DMA channel arbitrarily set to 0 */ + playback_hw_addr, /* HostBuffAddr (source) */ + DMA_RQ_SD_SP_SAMPLE_ADDR + /* destination buffer is in SP Sample Memory */ + sample_buffer_addr /* SP Buffer Address (destination) */ + }, + /* Scatter/gather DMA requestor extension (5 ints) */ + { + 0, + 0, + 0, + 0, + 0 + }, + /* Sublist pointer & next stream control block (SCB) link. */ + NULL_SCB_ADDR,NULL_SCB_ADDR, + /* Pointer to this tasks parameter block & stream function pointer */ + 0,NULL_SCB_ADDR, + /* rsConfig register for stream buffer (rsDMA reg. is loaded from basicReq.daw */ + /* for incoming streams, or basicReq.saw, for outgoing streams) */ + RSCONFIG_DMA_ENABLE + /* enable DMA */ + (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + /* MAX_DMA_SIZE picked to be 19 since SPUD */ + /* uses it for some reason */ + ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) + /* stream number = SCBaddr/16 */ + RSCONFIG_SAMPLE_16STEREO + + RSCONFIG_MODULO_32, /* dest buffer(PCMreaderBuf) is 32 dwords (256 bytes) */ + /* Stream sample pointer & MAC-unit mode for this stream */ + (sample_buffer_addr << 0x10), + /* Fractional increment per output sample in the input sample buffer */ + 0, + { + /* Standard stereo volume control + default muted */ + 0xffff,0xffff, + 0xffff,0xffff + } + }; + + if (ins->null_algorithm == NULL) { + ins->null_algorithm = cs46xx_dsp_lookup_symbol (chip,"NULLALGORITHM", + SYMBOL_CODE); + + if (ins->null_algorithm == NULL) { + snd_printk (KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n"); + return NULL; + } + } + + scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_reader_scb, + dest,ins->null_algorithm,parent_scb, + scb_child_type); + + return scb; +} + +#define GOF_PER_SEC 200 + +dsp_scb_descriptor_t * +cs46xx_dsp_create_src_task_scb(cs46xx_t * chip,char * scb_name, + int rate, + u16 src_buffer_addr, + u16 src_delay_buffer_addr,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type, + int pass_through) +{ + + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * scb; + unsigned int tmp1, tmp2; + unsigned int phiIncr; + unsigned int correctionPerGOF, correctionPerSec; + + snd_printdd( "dsp_spos: setting %s rate to %u\n",scb_name,rate); + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M + * GOF_PER_SEC * correctionPerGOF + * + * i.e. + * + * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + */ + tmp1 = rate << 16; + phiIncr = tmp1 / 48000; + tmp1 -= phiIncr * 48000; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / 48000; + phiIncr += tmp2; + tmp1 -= tmp2 * 48000; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + + { + src_task_scb_t src_task_scb = { + 0x0028,0x00c8, + 0x5555,0x0000, + 0x0000,0x0000, + src_buffer_addr,1, + correctionPerGOF,correctionPerSec, + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32, + 0x0000,src_delay_buffer_addr, + 0x0, + 0x080,(src_delay_buffer_addr + (24 * 4)), + 0,0, /* next_scb, sub_list_ptr */ + 0,0, /* entry, this_spb */ + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8, + src_buffer_addr << 0x10, + phiIncr, + { + 0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left, + 0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left + } + }; + + if (ins->s16_up == NULL) { + ins->s16_up = cs46xx_dsp_lookup_symbol (chip,"S16_UPSRC", + SYMBOL_CODE); + + if (ins->s16_up == NULL) { + snd_printk (KERN_ERR "dsp_spos: symbol S16_UPSRC not found\n"); + return NULL; + } + } + + /* clear buffers */ + _dsp_clear_sample_buffer (chip,src_buffer_addr,8); + _dsp_clear_sample_buffer (chip,src_delay_buffer_addr,32); + + if (pass_through) { + /* wont work with any other rate than + the native DSP rate */ + snd_assert (rate = 48000); + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb, + dest,"DMAREADER",parent_scb, + scb_child_type); + } else { + scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb, + dest,ins->s16_up,parent_scb, + scb_child_type); + } + + + } + + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_mix_only_scb(cs46xx_t * chip,char * scb_name, + u16 mix_buffer_addr,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_scb_descriptor_t * scb; + + mix_only_scb_t master_mix_scb = { + /* 0 */ { 0, + /* 1 */ 0, + /* 2 */ mix_buffer_addr, + /* 3 */ 0 + /* */ }, + { + /* 4 */ 0, + /* 5 */ 0, + /* 6 */ 0, + /* 7 */ 0, + /* 8 */ 0x00000080 + }, + /* 9 */ 0,0, + /* A */ 0,0, + /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64, + /* C */ (mix_buffer_addr + (32 * 4)) << 0x10, + /* D */ 0, + { + /* E */ 0x8000,0x8000, + /* F */ 0x8000,0x8000 + } + }; + + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&master_mix_scb, + dest,"S16_MIX",parent_scb, + scb_child_type); + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_mix_to_ostream_scb(cs46xx_t * chip,char * scb_name, + u16 mix_buffer_addr,u16 writeback_spb,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_scb_descriptor_t * scb; + + mix2_ostream_scb_t mix2_ostream_scb = { + /* Basic (non scatter/gather) DMA requestor (4 ints) */ + { + DMA_RQ_C1_SOURCE_MOD64 + + DMA_RQ_C1_DEST_ON_HOST + + DMA_RQ_C1_DEST_MOD1024 + + DMA_RQ_C1_WRITEBACK_SRC_FLAG + + DMA_RQ_C1_WRITEBACK_DEST_FLAG + + 15, + + DMA_RQ_C2_AC_NONE + + DMA_RQ_C2_SIGNAL_DEST_PINGPONG + + + CS46XX_DSP_CAPTURE_CHANNEL, + DMA_RQ_SD_SP_SAMPLE_ADDR + + mix_buffer_addr, + 0x0 + }, + + { 0, 0, 0, 0, 0, }, + 0,0, + 0,writeback_spb, + + RSCONFIG_DMA_ENABLE + + (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + + + ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) + + RSCONFIG_DMA_TO_HOST + + RSCONFIG_SAMPLE_16STEREO + + RSCONFIG_MODULO_64, + (mix_buffer_addr + (32 * 4)) << 0x10, + 1,0, + 0x0001,0x0080, + 0xFFFF,0 + }; + + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&mix2_ostream_scb, + dest,"S16_MIX_TO_OSTREAM",parent_scb, + scb_child_type); + + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_vari_decimate_scb(cs46xx_t * chip,char * scb_name, + u16 vari_buffer_addr0, + u16 vari_buffer_addr1, + u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + + dsp_scb_descriptor_t * scb; + + vari_decimate_scb_t vari_decimate_scb = { + 0x0028,0x00c8, + 0x5555,0x0000, + 0x0000,0x0000, + vari_buffer_addr0,vari_buffer_addr1, + + 0x0028,0x00c8, + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, + + 0xFF800000, + 0, + 0x0080,vari_buffer_addr1 + (25 * 4), + + 0,0, + 0,0, + + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8, + vari_buffer_addr0 << 0x10, + 0x04000000, + { + 0x8000,0x8000, + 0xFFFF,0xFFFF + } + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&vari_decimate_scb, + dest,"VARIDECIMATE",parent_scb, + scb_child_type); + + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_pcm_serial_input_scb(cs46xx_t * chip,char * scb_name,u32 dest, + dsp_scb_descriptor_t * input_scb, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + + dsp_scb_descriptor_t * scb; + + + pcm_serial_input_scb_t pcm_serial_input_scb = { + { 0, + 0, + 0, + 0 + }, + { + 0, + 0, + 0, + 0, + 0 + }, + + 0,0, + 0,0, + + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_16, + 0, + /* 0xD */ 0,input_scb->address, + { + /* 0xE */ 0x8000,0x8000, + /* 0xF */ 0x8000,0x8000 + } + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_serial_input_scb, + dest,"PCMSERIALINPUTTASK",parent_scb, + scb_child_type); + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_asynch_fg_tx_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 hfg_scb_address, + u16 asynch_buffer_address, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + + dsp_scb_descriptor_t * scb; + + asynch_fg_tx_scb_t asynch_fg_tx_scb = { + 0xfc00,0x03ff, /* Prototype sample buffer size of 256 dwords */ + 0x0058,0x0028, /* Min Delta 7 dwords == 28 bytes */ + /* : Max delta 25 dwords == 100 bytes */ + 0,hfg_scb_address, /* Point to HFG task SCB */ + 0,0, /* Initialize current Delta and Consumer ptr adjustment count */ + 0, /* Initialize accumulated Phi to 0 */ + 0,0x2aab, /* Const 1/3 */ + + { + 0, /* Define the unused elements */ + 0, + 0 + }, + + 0,0, + 0,dest + AFGTxAccumPhi, + + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, /* Stereo, 256 dword */ + (asynch_buffer_address) << 0x10, /* This should be automagically synchronized + to the producer pointer */ + + /* There is no correct initial value, it will depend upon the detected + rate etc */ + 0x18000000, /* Phi increment for approx 32k operation */ + 0x8000,0x8000, /* Volume controls are unused at this time */ + 0x8000,0x8000 + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_tx_scb, + dest,"ASYNCHFGTXCODE",parent_scb, + scb_child_type); + + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_asynch_fg_rx_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 hfg_scb_address, + u16 asynch_buffer_address, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * scb; + + asynch_fg_rx_scb_t asynch_fg_rx_scb = { + 0xfe00,0x01ff, /* Prototype sample buffer size of 128 dwords */ + 0x0064,0x001c, /* Min Delta 7 dwords == 28 bytes */ + /* : Max delta 25 dwords == 100 bytes */ + 0,hfg_scb_address, /* Point to HFG task SCB */ + 0,0, /* Initialize current Delta and Consumer ptr adjustment count */ + { + 0, /* Define the unused elements */ + 0, + 0, + 0, + 0 + }, + + 0,0, + 0,dest, + + RSCONFIG_MODULO_128 | + RSCONFIG_SAMPLE_16STEREO, /* Stereo, 128 dword */ + ( (asynch_buffer_address + (16 * 4)) << 0x10), /* This should be automagically + synchrinized to the producer pointer */ + + /* There is no correct initial value, it will depend upon the detected + rate etc */ + 0x18000000, + + /* Set IEC958 input volume */ + 0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left, + 0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left, + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_rx_scb, + dest,"ASYNCHFGRXCODE",parent_scb, + scb_child_type); + + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_output_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 snoop_buffer_address, + dsp_scb_descriptor_t * snoop_scb, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + + dsp_scb_descriptor_t * scb; + + output_snoop_scb_t output_snoop_scb = { + { 0, /* not used. Zero */ + 0, + 0, + 0, + }, + { + 0, /* not used. Zero */ + 0, + 0, + 0, + 0 + }, + + 0,0, + 0,0, + + RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64, + snoop_buffer_address << 0x10, + 0,0, + 0, + 0,snoop_scb->address + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&output_snoop_scb, + dest,"OUTPUTSNOOP",parent_scb, + scb_child_type); + return scb; +} + + +dsp_scb_descriptor_t * +cs46xx_dsp_create_spio_write_scb(cs46xx_t * chip,char * scb_name,u32 dest, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_scb_descriptor_t * scb; + + spio_write_scb_t spio_write_scb = { + 0,0, /* SPIOWAddress2:SPIOWAddress1; */ + 0, /* SPIOWData1; */ + 0, /* SPIOWData2; */ + 0,0, /* SPIOWAddress4:SPIOWAddress3; */ + 0, /* SPIOWData3; */ + 0, /* SPIOWData4; */ + 0,0, /* SPIOWDataPtr:Unused1; */ + { 0,0 }, /* Unused2[2]; */ + + 0,0, /* SPIOWChildPtr:SPIOWSiblingPtr; */ + 0,0, /* SPIOWThisPtr:SPIOWEntryPoint; */ + + { + 0, + 0, + 0, + 0, + 0 /* Unused3[5]; */ + } + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&spio_write_scb, + dest,"SPIOWRITE",parent_scb, + scb_child_type); + + return scb; +} + +dsp_scb_descriptor_t * cs46xx_dsp_create_magic_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest, + u16 snoop_buffer_address, + dsp_scb_descriptor_t * snoop_scb, + dsp_scb_descriptor_t * parent_scb, + int scb_child_type) +{ + dsp_scb_descriptor_t * scb; + + magic_snoop_task_t magic_snoop_scb = { + /* 0 */ 0, /* i0 */ + /* 1 */ 0, /* i1 */ + /* 2 */ snoop_buffer_address << 0x10, + /* 3 */ 0,snoop_scb->address, + /* 4 */ 0, /* i3 */ + /* 5 */ 0, /* i4 */ + /* 6 */ 0, /* i5 */ + /* 7 */ 0, /* i6 */ + /* 8 */ 0, /* i7 */ + /* 9 */ 0,0, /* next_scb, sub_list_ptr */ + /* A */ 0,0, /* entry_point, this_ptr */ + /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64, + /* C */ snoop_buffer_address << 0x10, + /* D */ 0, + /* E */ { 0x8000,0x8000, + /* F */ 0xffff,0xffff + } + }; + + scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&magic_snoop_scb, + dest,"MAGICSNOOPTASK",parent_scb, + scb_child_type); + + return scb; +} + +static dsp_scb_descriptor_t * find_next_free_scb (cs46xx_t * chip,dsp_scb_descriptor_t * from) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * scb = from; + + while (scb->next_scb_ptr != ins->the_null_scb) { + snd_assert (scb->next_scb_ptr != NULL, return NULL); + + scb = scb->next_scb_ptr; + } + + return scb; +} + +static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = { + 0x0600, /* 1 */ + 0x1500, /* 2 */ + 0x1580, /* 3 */ + 0x1600, /* 4 */ + 0x1680, /* 5 */ + 0x1700, /* 6 */ + 0x1780, /* 7 */ + 0x1800, /* 8 */ + 0x1880, /* 9 */ + 0x1900, /* 10 */ + 0x1980, /* 11 */ + 0x1A00, /* 12 */ + 0x1A80, /* 13 */ + 0x1B00, /* 14 */ + 0x1B80, /* 15 */ + 0x1C00, /* 16 */ + 0x1C80, /* 17 */ + 0x1D00, /* 18 */ + 0x1D80, /* 19 */ + 0x1E00, /* 20 */ + 0x1E80, /* 21 */ + 0x1F00, /* 22 */ + 0x1F80, /* 23 */ + 0x2000, /* 24 */ + 0x2080, /* 25 */ + 0x2100, /* 26 */ + 0x2180, /* 27 */ + 0x2200, /* 28 */ + 0x2280, /* 29 */ + 0x2300, /* 30 */ + 0x2380, /* 31 */ + 0x2400, /* 32 */ +}; + +static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = { + 0x2580, + 0x2680, + 0x2780, + 0x2980, + 0x2A80, + 0x2B80, +}; + +static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = { + 0x2600, + 0x2700, + 0x2800, + 0x2900, + 0x2A00, + 0x2B00, +}; + + +pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip, + u32 sample_rate, void * private_data, + u32 hw_dma_addr, + int pcm_channel_id) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * src_scb = NULL,* pcm_scb, * mixer_scb = NULL; + dsp_scb_descriptor_t * src_parent_scb = NULL; + + /* dsp_scb_descriptor_t * pcm_parent_scb; */ + char scb_name[DSP_MAX_SCB_NAME]; + int i,pcm_index = -1, insert_point, src_index = -1,pass_through = 0; + unsigned long flags; + + switch (pcm_channel_id) { + case DSP_PCM_MAIN_CHANNEL: + mixer_scb = ins->master_mix_scb; + break; + case DSP_PCM_REAR_CHANNEL: + mixer_scb = ins->rear_mix_scb; + break; + case DSP_PCM_CENTER_CHANNEL: + /* TODO */ + snd_assert(0); + break; + case DSP_PCM_LFE_CHANNEL: + /* TODO */ + snd_assert(0); + break; + case DSP_IEC958_CHANNEL: + snd_assert (ins->asynch_tx_scb != NULL, return NULL); + mixer_scb = ins->asynch_tx_scb; + + /* if sample rate is set to 48khz we pass + the Sample Rate Converted (which could + alter the raw data stream ...) */ + if (sample_rate == 48000) { + snd_printdd ("IEC958 pass through\n"); + /* Hack to bypass creating a new SRC */ + pass_through = 1; + } + break; + default: + snd_assert (0); + return NULL; + } + /* default sample rate is 44100 */ + if (!sample_rate) sample_rate = 44100; + + /* search for a already created SRC SCB with the same sample rate */ + for (i = 0; i < DSP_MAX_PCM_CHANNELS && + (pcm_index == -1 || src_scb == NULL); ++i) { + + /* virtual channel reserved + for capture */ + if (i == CS46XX_DSP_CAPTURE_CHANNEL) continue; + + if (ins->pcm_channels[i].active) { + if (!src_scb && + ins->pcm_channels[i].sample_rate == sample_rate && + ins->pcm_channels[i].mixer_scb == mixer_scb) { + src_scb = ins->pcm_channels[i].src_scb; + ins->pcm_channels[i].src_scb->ref_count ++; + src_index = ins->pcm_channels[i].src_slot; + } + } else if (pcm_index == -1) { + pcm_index = i; + } + } + + if (pcm_index == -1) { + snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n"); + return NULL; + } + + if (src_scb == NULL) { + if (ins->nsrc_scb >= DSP_MAX_SRC_NR) { + snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!"); + return NULL; + } + + /* find a free slot */ + for (i = 0; i < DSP_MAX_SRC_NR; ++i) { + if (ins->src_scb_slots[i] == 0) { + src_index = i; + ins->src_scb_slots[i] = 1; + break; + } + } + snd_assert (src_index != -1,return NULL); + + /* we need to create a new SRC SCB */ + if (mixer_scb->sub_list_ptr == ins->the_null_scb) { + src_parent_scb = mixer_scb; + insert_point = SCB_ON_PARENT_SUBLIST_SCB; + } else { + src_parent_scb = find_next_free_scb(chip,mixer_scb->sub_list_ptr); + insert_point = SCB_ON_PARENT_NEXT_SCB; + } + + snprintf (scb_name,DSP_MAX_SCB_NAME,"SrcTask_SCB%d",src_index); + + snd_printdd( "dsp_spos: creating SRC \"%s\"\n",scb_name); + src_scb = cs46xx_dsp_create_src_task_scb(chip,scb_name, + sample_rate, + src_output_buffer_addr[src_index], + src_delay_buffer_addr[src_index], + /* 0x400 - 0x600 source SCBs */ + 0x400 + (src_index * 0x10) , + src_parent_scb, + insert_point, + pass_through); + + if (!src_scb) { + snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n"); + return NULL; + } + + /* cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate); */ + + ins->nsrc_scb ++; + } + + + snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index); + + snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name, + pcm_channel_id); + + pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name, + pcm_reader_buffer_addr[pcm_index], + /* 0x200 - 400 PCMreader SCBs */ + (pcm_index * 0x10) + 0x200, + pcm_index, /* virtual channel 0-31 */ + hw_dma_addr, /* pcm hw addr */ + NULL, /* parent SCB ptr */ + 0 /* insert point */ + ); + + if (!pcm_scb) { + snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n"); + return NULL; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + ins->pcm_channels[pcm_index].sample_rate = sample_rate; + ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb; + ins->pcm_channels[pcm_index].src_scb = src_scb; + ins->pcm_channels[pcm_index].unlinked = 1; + ins->pcm_channels[pcm_index].private_data = private_data; + ins->pcm_channels[pcm_index].src_slot = src_index; + ins->pcm_channels[pcm_index].active = 1; + ins->pcm_channels[pcm_index].pcm_slot = pcm_index; + ins->pcm_channels[pcm_index].mixer_scb = mixer_scb; + ins->npcm_channels ++; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return (ins->pcm_channels + pcm_index); +} + +int cs46xx_dsp_pcm_channel_set_period (cs46xx_t * chip, + pcm_channel_descriptor_t * pcm_channel, + int period_size) +{ + u32 temp = snd_cs46xx_peek (chip,pcm_channel->pcm_reader_scb->address << 2); + temp &= ~DMA_RQ_C1_SOURCE_SIZE_MASK; + + switch (period_size) { + case 2048: + temp |= DMA_RQ_C1_SOURCE_MOD1024; + break; + case 1024: + temp |= DMA_RQ_C1_SOURCE_MOD512; + break; + case 512: + temp |= DMA_RQ_C1_SOURCE_MOD256; + break; + case 256: + temp |= DMA_RQ_C1_SOURCE_MOD128; + break; + case 128: + temp |= DMA_RQ_C1_SOURCE_MOD64; + break; + case 64: + temp |= DMA_RQ_C1_SOURCE_MOD32; + break; + case 32: + temp |= DMA_RQ_C1_SOURCE_MOD16; + break; + default: + snd_printdd ("period size (%d) not supported by HW\n"); + return -EINVAL; + } + + snd_cs46xx_poke (chip,pcm_channel->pcm_reader_scb->address << 2,temp); + + return 0; +} + +int cs46xx_dsp_pcm_ostream_set_period (cs46xx_t * chip, + int period_size) +{ + u32 temp = snd_cs46xx_peek (chip,WRITEBACK_SCB_ADDR << 2); + temp &= ~DMA_RQ_C1_DEST_SIZE_MASK; + + switch (period_size) { + case 2048: + temp |= DMA_RQ_C1_DEST_MOD1024; + break; + case 1024: + temp |= DMA_RQ_C1_DEST_MOD512; + break; + case 512: + temp |= DMA_RQ_C1_DEST_MOD256; + break; + case 256: + temp |= DMA_RQ_C1_DEST_MOD128; + break; + case 128: + temp |= DMA_RQ_C1_DEST_MOD64; + break; + case 64: + temp |= DMA_RQ_C1_DEST_MOD32; + break; + case 32: + temp |= DMA_RQ_C1_DEST_MOD16; + break; + default: + snd_printdd ("period size (%d) not supported by HW\n"); + return -EINVAL; + } + + snd_cs46xx_poke (chip,WRITEBACK_SCB_ADDR << 2,temp); + + return 0; +} + +void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + unsigned long flags; + + snd_assert(pcm_channel->active, return ); + snd_assert(ins->npcm_channels > 0, return ); + snd_assert(pcm_channel->src_scb->ref_count > 0, return ); + + spin_lock_irqsave(&chip->reg_lock, flags); + pcm_channel->unlinked = 1; + pcm_channel->active = 0; + pcm_channel->private_data = NULL; + pcm_channel->src_scb->ref_count --; + ins->npcm_channels --; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + cs46xx_dsp_remove_scb(chip,pcm_channel->pcm_reader_scb); + + if (!pcm_channel->src_scb->ref_count) { + cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb); + + snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR, + return ); + + ins->src_scb_slots[pcm_channel->src_slot] = 0; + ins->nsrc_scb --; + } +} + +int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + unsigned long flags; + + snd_assert(pcm_channel->active,return -EIO); + snd_assert(ins->npcm_channels > 0,return -EIO); + + spin_lock(&pcm_channel->src_scb->lock); + + if (pcm_channel->unlinked) { + spin_unlock(&pcm_channel->src_scb->lock); + return -EIO; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + pcm_channel->unlinked = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + _dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb); + + spin_unlock(&pcm_channel->src_scb->lock); + return 0; +} + +int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * parent_scb; + dsp_scb_descriptor_t * src_scb = pcm_channel->src_scb; + unsigned long flags; + + spin_lock(&pcm_channel->src_scb->lock); + + if (pcm_channel->unlinked == 0) { + spin_unlock(&pcm_channel->src_scb->lock); + return -EIO; + } + + parent_scb = src_scb; + + if (src_scb->sub_list_ptr != ins->the_null_scb) { + src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb; + pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr; + } + + src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb; + + snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; ); + pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb; + + spin_lock_irqsave(&chip->reg_lock, flags); + + /* update SCB entry in DSP RAM */ + cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb); + + /* update parent SCB entry */ + cs46xx_dsp_spos_update_scb(chip,parent_scb); + + pcm_channel->unlinked = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + spin_unlock(&pcm_channel->src_scb->lock); + return 0; +} + +dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source, + u16 addr,char * scb_name) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * parent; + dsp_scb_descriptor_t * pcm_input; + int insert_point; + + snd_assert (ins->record_mixer_scb != NULL,return NULL); + + if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) { + parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr); + insert_point = SCB_ON_PARENT_NEXT_SCB; + } else { + parent = ins->record_mixer_scb; + insert_point = SCB_ON_PARENT_SUBLIST_SCB; + } + + pcm_input = cs46xx_dsp_create_pcm_serial_input_scb(chip,scb_name,addr, + source, parent, + insert_point); + + return pcm_input; +} + +int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src) +{ + snd_assert (src->parent_scb_ptr != NULL, return -EINVAL ); + + /* mute SCB */ + cs46xx_dsp_scb_set_volume (chip,src,0,0); + + _dsp_unlink_scb (chip,src); + + return 0; +} + +int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + dsp_scb_descriptor_t * parent_scb; + + snd_assert (src->parent_scb_ptr == NULL, return -EINVAL ); + snd_assert(ins->master_mix_scb !=NULL, return -EINVAL ); + + if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) { + parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr); + parent_scb->next_scb_ptr = src; + } else { + parent_scb = ins->master_mix_scb; + parent_scb->sub_list_ptr = src; + } + + src->parent_scb_ptr = parent_scb; + + /* update entry in DSP RAM */ + cs46xx_dsp_spos_update_scb(chip,parent_scb); + + return 0; +} + +int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) { + cs46xx_dsp_enable_spdif_hw (chip); + } + + /* dont touch anything if SPDIF is open */ + if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) { + /* when cs46xx_iec958_post_close(...) is called it + will call this function if necessary depending on + this bit */ + ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED; + + return -EBUSY; + } + + snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL); + snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL); + + /* reset output snooper sample buffer pointer */ + snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2, + (OUTPUT_SNOOP_BUFFER + 0x10) << 0x10 ); + + /* The asynch. transfer task */ + ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR, + SPDIFO_SCB_INST, + SPDIFO_IP_OUTPUT_BUFFER1, + ins->master_mix_scb, + SCB_ON_PARENT_NEXT_SCB); + if (!ins->asynch_tx_scb) return -ENOMEM; + + ins->spdif_pcm_input_scb = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput_II", + PCMSERIALINII_SCB_ADDR, + ins->ref_snoop_scb, + ins->asynch_tx_scb, + SCB_ON_PARENT_SUBLIST_SCB); + + + if (!ins->spdif_pcm_input_scb) return -ENOMEM; + + /* monitor state */ + ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED; + + return 0; +} + +int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + /* dont touch anything if SPDIF is open */ + if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) { + ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED; + return -EBUSY; + } + + /* check integrety */ + snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL); + snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL); + snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL); + snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL); + + cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb); + cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb); + + ins->spdif_pcm_input_scb = NULL; + ins->asynch_tx_scb = NULL; + + /* clear buffer to prevent any undesired noise */ + _dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256); + + /* monitor state */ + ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED; + + + return 0; +} + +int cs46xx_iec958_pre_open (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) { + /* remove AsynchFGTxSCB and and PCMSerialInput_II */ + cs46xx_dsp_disable_spdif_out (chip); + + /* save state */ + ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED; + } + + /* if not enabled already */ + if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) { + cs46xx_dsp_enable_spdif_hw (chip); + } + + /* Create the asynch. transfer task for playback */ + ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR, + SPDIFO_SCB_INST, + SPDIFO_IP_OUTPUT_BUFFER1, + ins->master_mix_scb, + SCB_ON_PARENT_NEXT_SCB); + + + /* set spdif channel status value for streaming */ + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_stream); + + ins->spdif_status_out |= DSP_SPDIF_STATUS_PLAYBACK_OPEN; + + return 0; +} + +int cs46xx_iec958_post_close (cs46xx_t *chip) +{ + dsp_spos_instance_t * ins = chip->dsp_spos_instance; + + snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL); + + ins->spdif_status_out &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN; + + /* restore settings */ + cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default); + + /* deallocate stuff */ + if (ins->spdif_pcm_input_scb != NULL) { + cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb); + ins->spdif_pcm_input_scb = NULL; + } + + cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb); + ins->asynch_tx_scb = NULL; + + /* clear buffer to prevent any undesired noise */ + _dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256); + + /* restore state */ + if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) { + cs46xx_dsp_enable_spdif_out (chip); + } + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwc4630.h linux/sound/pci/cs46xx/imgs/cwc4630.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwc4630.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwc4630.h 2002-08-01 03:26:33.000000000 -0600 @@ -0,0 +1,320 @@ +/* generated from cwc4630.osp DO NOT MODIFY */ + +#ifndef __HEADER_cwc4630_H__ +#define __HEADER_cwc4630_H__ + +static symbol_entry_t cwc4630_symbols[] = { + { 0x0000, "BEGINADDRESS",0x00 }, + { 0x8000, "EXECCHILD",0x03 }, + { 0x8001, "EXECCHILD_98",0x03 }, + { 0x8003, "EXECCHILD_PUSH1IND",0x03 }, + { 0x8008, "EXECSIBLING",0x03 }, + { 0x800a, "EXECSIBLING_298",0x03 }, + { 0x800b, "EXECSIBLING_2IND1",0x03 }, + { 0x8010, "TIMINGMASTER",0x03 }, + { 0x804f, "S16_CODECINPUTTASK",0x03 }, + { 0x805e, "PCMSERIALINPUTTASK",0x03 }, + { 0x806d, "S16_MIX_TO_OSTREAM",0x03 }, + { 0x809a, "S16_MIX",0x03 }, + { 0x80bb, "S16_UPSRC",0x03 }, + { 0x813b, "MIX3_EXP",0x03 }, + { 0x8164, "DECIMATEBYPOW2",0x03 }, + { 0x8197, "VARIDECIMATE",0x03 }, + { 0x81f2, "_3DINPUTTASK",0x03 }, + { 0x820a, "_3DPRLGCINPTASK",0x03 }, + { 0x8227, "_3DSTEREOINPUTTASK",0x03 }, + { 0x8242, "_3DOUTPUTTASK",0x03 }, + { 0x82c4, "HRTF_MORPH_TASK",0x03 }, + { 0x82c6, "WAIT4DATA",0x03 }, + { 0x82fa, "PROLOGIC",0x03 }, + { 0x8496, "DECORRELATOR",0x03 }, + { 0x84a4, "STEREO2MONO",0x03 }, + { 0x0070, "SPOSCB",0x02 }, + { 0x0107, "TASKTREETHREAD",0x03 }, + { 0x013c, "TASKTREEHEADERCODE",0x03 }, + { 0x0145, "FGTASKTREEHEADERCODE",0x03 }, + { 0x0169, "NULLALGORITHM",0x03 }, + { 0x016d, "HFGEXECCHILD",0x03 }, + { 0x016e, "HFGEXECCHILD_98",0x03 }, + { 0x0170, "HFGEXECCHILD_PUSH1IND",0x03 }, + { 0x0173, "HFGEXECSIBLING",0x03 }, + { 0x0175, "HFGEXECSIBLING_298",0x03 }, + { 0x0176, "HFGEXECSIBLING_2IND1",0x03 }, + { 0x0179, "S16_CODECOUTPUTTASK",0x03 }, + { 0x0194, "#CODE_END",0x00 }, +}; /* cwc4630 symbols */ + +static u32 cwc4630_code[] = { +/* BEGINADDRESS */ +/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003, +/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003, +/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003, +/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003, +/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003, +/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003, +/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003, +/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003, +/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003, +/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040, +/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003, +/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003, +/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003, +/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003, +/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003, +/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003, +/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003, +/* 0040 */ 0x0001a730,0x00001008,0x000e2730,0x00001002, +/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002, +/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002, +/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002, +/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003, +/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000, +/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000, +/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000, +/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000, +/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000, +/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000, +/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003, +/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003, +/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000, +/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003, +/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000, +/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000, +/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000, +/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000, +/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140, +/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40, +/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00, +/* 007C */ 0x0000002e,0x0009d6c0,0x0002ef8a,0x00000000, +/* 007E */ 0x00040630,0x00001004,0x0004ef0a,0x000eb785, +/* 0080 */ 0x0003fc8a,0x00000000,0x00000000,0x000c70e0, +/* 0082 */ 0x0007d182,0x0002c640,0x00008630,0x00001004, +/* 0084 */ 0x000799b8,0x0002c6c0,0x00031705,0x00092240, +/* 0086 */ 0x00039f05,0x000932c0,0x0003520a,0x00000000, +/* 0088 */ 0x00070731,0x0000100b,0x00010705,0x000b20c0, +/* 008A */ 0x00000000,0x000eba44,0x00032108,0x000c60c4, +/* 008C */ 0x00065208,0x000c2917,0x000486b0,0x00001007, +/* 008E */ 0x00012f05,0x00036880,0x0002818e,0x000c0000, +/* 0090 */ 0x0004410a,0x00000000,0x00048630,0x00001007, +/* 0092 */ 0x00029705,0x000c0000,0x00000000,0x00000000, +/* 0094 */ 0x00003fc1,0x0003fc40,0x000037c1,0x00091b40, +/* 0096 */ 0x00003fc1,0x000911c0,0x000037c1,0x000957c0, +/* 0098 */ 0x00003fc1,0x000951c0,0x000037c1,0x00000000, +/* 009A */ 0x00003fc1,0x000991c0,0x000037c1,0x00000000, +/* 009C */ 0x00003fc1,0x0009d1c0,0x000037c1,0x00000000, +/* 009E */ 0x0001ccc1,0x000915c0,0x0001c441,0x0009d800, +/* 00A0 */ 0x0009cdc1,0x00091240,0x0001c541,0x00091d00, +/* 00A2 */ 0x0009cfc1,0x00095240,0x0001c741,0x00095c80, +/* 00A4 */ 0x000e8ca9,0x00099240,0x000e85ad,0x00095640, +/* 00A6 */ 0x00069ca9,0x00099d80,0x000e952d,0x00099640, +/* 00A8 */ 0x000eaca9,0x0009d6c0,0x000ea5ad,0x00091a40, +/* 00AA */ 0x0006bca9,0x0009de80,0x000eb52d,0x00095a40, +/* 00AC */ 0x000ecca9,0x00099ac0,0x000ec5ad,0x0009da40, +/* 00AE */ 0x000edca9,0x0009d300,0x000a6e0a,0x00001000, +/* 00B0 */ 0x000ed52d,0x00091e40,0x000eeca9,0x00095ec0, +/* 00B2 */ 0x000ee5ad,0x00099e40,0x0006fca9,0x00002500, +/* 00B4 */ 0x000fb208,0x000c59a0,0x000ef52d,0x0009de40, +/* 00B6 */ 0x00068ca9,0x000912c1,0x000683ad,0x00095241, +/* 00B8 */ 0x00020f05,0x000991c1,0x00000000,0x00000000, +/* 00BA */ 0x00086f88,0x00001000,0x0009cf81,0x000b5340, +/* 00BC */ 0x0009c701,0x000b92c0,0x0009de81,0x000bd300, +/* 00BE */ 0x0009d601,0x000b1700,0x0001fd81,0x000b9d80, +/* 00C0 */ 0x0009f501,0x000b57c0,0x000a0f81,0x000bd740, +/* 00C2 */ 0x00020701,0x000b5c80,0x000a1681,0x000b97c0, +/* 00C4 */ 0x00021601,0x00002500,0x000a0701,0x000b9b40, +/* 00C6 */ 0x000a0f81,0x000b1bc0,0x00021681,0x00002d00, +/* 00C8 */ 0x00020f81,0x000bd800,0x000a0701,0x000b5bc0, +/* 00CA */ 0x00021601,0x00003500,0x000a0f81,0x000b5f40, +/* 00CC */ 0x000a0701,0x000bdbc0,0x00021681,0x00003d00, +/* 00CE */ 0x00020f81,0x000b1d00,0x000a0701,0x000b1fc0, +/* 00D0 */ 0x00021601,0x00020500,0x00020f81,0x000b1341, +/* 00D2 */ 0x000a0701,0x000b9fc0,0x00021681,0x00020d00, +/* 00D4 */ 0x00020f81,0x000bde80,0x000a0701,0x000bdfc0, +/* 00D6 */ 0x00021601,0x00021500,0x00020f81,0x000b9341, +/* 00D8 */ 0x00020701,0x000b53c1,0x00021681,0x00021d00, +/* 00DA */ 0x000a0f81,0x000d0380,0x0000b601,0x000b15c0, +/* 00DC */ 0x00007b01,0x00000000,0x00007b81,0x000bd1c0, +/* 00DE */ 0x00007b01,0x00000000,0x00007b81,0x000b91c0, +/* 00E0 */ 0x00007b01,0x000b57c0,0x00007b81,0x000b51c0, +/* 00E2 */ 0x00007b01,0x000b1b40,0x00007b81,0x000b11c0, +/* 00E4 */ 0x00087b01,0x000c3dc0,0x0007e488,0x000d7e45, +/* 00E6 */ 0x00000000,0x000d7a44,0x0007e48a,0x00000000, +/* 00E8 */ 0x00011f05,0x00084080,0x00000000,0x00000000, +/* 00EA */ 0x00001705,0x000b3540,0x00008a01,0x000bf040, +/* 00EC */ 0x00007081,0x000bb5c0,0x00055488,0x00000000, +/* 00EE */ 0x0000d482,0x0003fc40,0x0003fc88,0x00000000, +/* 00F0 */ 0x0001e401,0x000b3a00,0x0001ec81,0x000bd6c0, +/* 00F2 */ 0x0002ef88,0x000e7784,0x00056f08,0x00000000, +/* 00F4 */ 0x000d86b0,0x00001007,0x00008281,0x000bb240, +/* 00F6 */ 0x0000b801,0x000b7140,0x00007888,0x00000000, +/* 00F8 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000, +/* 00FA */ 0x00000000,0x00000000,0x00055288,0x000c555c, +/* 00FC */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +/* 00FE */ 0x0000fa88,0x00000000,0x00000032,0x00001000, +/* 0100 */ 0x0000073d,0x00001000,0x0007f188,0x000c0000, +/* 0102 */ 0x00000000,0x00000000,0x0008c01c,0x00001003, +/* 0104 */ 0x00002705,0x00001008,0x0008b201,0x000c1392, +/* 0106 */ 0x0000ba01,0x00000000, +/* TASKTREETHREAD */ +/* 0107 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4, +/* 0109 */ 0x00057488,0x00000000,0x000a6388,0x00001001, +/* 010B */ 0x0008b334,0x000bc141,0x0003020e,0x00000000, +/* 010D */ 0x000986b0,0x00001008,0x00003625,0x000c5dfa, +/* 010F */ 0x000a638a,0x00001001,0x0008020e,0x00001002, +/* 0111 */ 0x0009a6b0,0x00001008,0x0007f301,0x00000000, +/* 0113 */ 0x00000000,0x00000000,0x00002725,0x000a8c40, +/* 0115 */ 0x000000ae,0x00000000,0x000e8630,0x00001008, +/* 0117 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640, +/* 0119 */ 0x000b8630,0x00001008,0x000799b8,0x0002d6c0, +/* 011B */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000, +/* 011D */ 0x00062208,0x000c4117,0x000a0630,0x00001009, +/* 011F */ 0x00000000,0x000c0000,0x0001022e,0x00000000, +/* 0121 */ 0x0006a630,0x00001009,0x00000032,0x00001000, +/* 0123 */ 0x000ca21c,0x00001003,0x00005a02,0x00000000, +/* 0125 */ 0x0001a630,0x00001009,0x00000000,0x000c0000, +/* 0127 */ 0x00000036,0x00001000,0x00000000,0x00000000, +/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 012B */ 0x00000000,0x00000000,0x0003a730,0x00001008, +/* 012D */ 0x0007f801,0x000c0000,0x00000037,0x00001000, +/* 012F */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0131 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0133 */ 0x0003a730,0x00001008,0x00000033,0x00001000, +/* 0135 */ 0x0003a705,0x00001008,0x00007a01,0x000c0000, +/* 0137 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000, +/* 0139 */ 0x00090730,0x0000100a,0x00000000,0x000c0000, +/* 013B */ 0x00000000,0x00000000, +/* TASKTREEHEADERCODE */ +/* 013C */ 0x0007aab0,0x00034880,0x000a8fb0,0x0000100b, +/* 013E */ 0x00057488,0x00000000,0x00033b94,0x00081140, +/* 0140 */ 0x000183ae,0x00000000,0x000a86b0,0x0000100b, +/* 0142 */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +/* 0144 */ 0x00042731,0x00001003, +/* FGTASKTREEHEADERCODE */ +/* 0145 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100a, +/* 0147 */ 0x00057488,0x00000000,0x00033b94,0x00081140, +/* 0149 */ 0x000183ae,0x00000000,0x000b06b0,0x0000100b, +/* 014B */ 0x00022f05,0x00000000,0x00007401,0x00091140, +/* 014D */ 0x00048f05,0x000951c0,0x00042731,0x00001003, +/* 014F */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47, +/* 0151 */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003, +/* 0153 */ 0x00000000,0x00000000,0x0008e19c,0x00001003, +/* 0155 */ 0x000083c1,0x00093040,0x00000f41,0x00097140, +/* 0157 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +/* 0159 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +/* 015B */ 0x00000000,0x000fdc44,0x00055208,0x00000000, +/* 015D */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00, +/* 015F */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000, +/* 0161 */ 0x00012f05,0x00036880,0x00065308,0x000c2997, +/* 0163 */ 0x000086b0,0x0000100b,0x0004410a,0x000d40c7, +/* 0165 */ 0x00000000,0x00000000,0x00088730,0x00001004, +/* 0167 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000, +/* NULLALGORITHM */ +/* 0169 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47, +/* 016B */ 0x00080000,0x000bffc7,0x0000273d,0x00001000, +/* HFGEXECCHILD */ +/* 016D */ 0x00000000,0x000eba44, +/* HFGEXECCHILD_98 */ +/* 016E */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +/* HFGEXECCHILD_PUSH1IND */ +/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880, +/* 0172 */ 0x00006a88,0x000c75c4, +/* HFGEXECSIBLING */ +/* 0173 */ 0x00000000,0x000e5084,0x00000000,0x000eba44, +/* HFGEXECSIBLING_298 */ +/* 0175 */ 0x00087401,0x000e4782, +/* HFGEXECSIBLING_2IND1 */ +/* 0176 */ 0x00000734,0x00001000,0x00010705,0x000a6880, +/* 0178 */ 0x00006a88,0x000c75c4, +/* S16_CODECOUTPUTTASK */ +/* 0179 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40, +/* 017B */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80, +/* 017D */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0, +/* 017F */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0, +/* 0181 */ 0x00073fb0,0x00074c80,0x000583a0,0x0000100c, +/* 0183 */ 0x000ee388,0x00042970,0x00008301,0x00021ef2, +/* 0185 */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b, +/* 0187 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916, +/* 0189 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000, +/* 018B */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b, +/* 018D */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956, +/* 018F */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40, +/* 0191 */ 0x00058730,0x00001400,0x000d7488,0x000c3a00, +/* 0193 */ 0x00048f05,0x00000000 +}; +/* #CODE_END */ + +static u32 cwc4630_parameter[] = { +/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000 +}; /* #PARAMETER_END */ + + +static segment_desc_t cwc4630_segments[] = { + { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000328, cwc4630_code }, + { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000080, cwc4630_parameter }, +}; + +static dsp_module_desc_t cwc4630_module = { + "cwc4630", + { + 38, + cwc4630_symbols + }, + 2, + cwc4630_segments, +}; + +#endif /* __HEADER_cwc4630_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcasync.h linux/sound/pci/cs46xx/imgs/cwcasync.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcasync.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwcasync.h 2002-08-01 03:26:33.000000000 -0600 @@ -0,0 +1,176 @@ +/* generated from cwcasync.osp DO NOT MODIFY */ + +#ifndef __HEADER_cwcasync_H__ +#define __HEADER_cwcasync_H__ + +static symbol_entry_t cwcasync_symbols[] = { + { 0x8000, "EXECCHILD",0x03 }, + { 0x8001, "EXECCHILD_98",0x03 }, + { 0x8003, "EXECCHILD_PUSH1IND",0x03 }, + { 0x8008, "EXECSIBLING",0x03 }, + { 0x800a, "EXECSIBLING_298",0x03 }, + { 0x800b, "EXECSIBLING_2IND1",0x03 }, + { 0x8010, "TIMINGMASTER",0x03 }, + { 0x804f, "S16_CODECINPUTTASK",0x03 }, + { 0x805e, "PCMSERIALINPUTTASK",0x03 }, + { 0x806d, "S16_MIX_TO_OSTREAM",0x03 }, + { 0x809a, "S16_MIX",0x03 }, + { 0x80bb, "S16_UPSRC",0x03 }, + { 0x813b, "MIX3_EXP",0x03 }, + { 0x8164, "DECIMATEBYPOW2",0x03 }, + { 0x8197, "VARIDECIMATE",0x03 }, + { 0x81f2, "_3DINPUTTASK",0x03 }, + { 0x820a, "_3DPRLGCINPTASK",0x03 }, + { 0x8227, "_3DSTEREOINPUTTASK",0x03 }, + { 0x8242, "_3DOUTPUTTASK",0x03 }, + { 0x82c4, "HRTF_MORPH_TASK",0x03 }, + { 0x82c6, "WAIT4DATA",0x03 }, + { 0x82fa, "PROLOGIC",0x03 }, + { 0x8496, "DECORRELATOR",0x03 }, + { 0x84a4, "STEREO2MONO",0x03 }, + { 0x0000, "OVERLAYBEGINADDRESS",0x00 }, + { 0x0000, "SPIOWRITE",0x03 }, + { 0x000d, "S16_ASYNCCODECINPUTTASK",0x03 }, + { 0x0043, "SPDIFITASK",0x03 }, + { 0x007b, "SPDIFOTASK",0x03 }, + { 0x0097, "ASYNCHFGTXCODE",0x03 }, + { 0x00be, "ASYNCHFGRXCODE",0x03 }, + { 0x00db, "#CODE_END",0x00 }, +}; /* cwcasync symbols */ + +static u32 cwcasync_code[] = { +/* OVERLAYBEGINADDRESS */ +/* 0000 */ 0x00002731,0x00001400,0x00003725,0x000a8440, +/* 0002 */ 0x000000ae,0x00000000,0x00060630,0x00001000, +/* 0004 */ 0x00000000,0x000c7560,0x00075282,0x0002d640, +/* 0006 */ 0x00021705,0x00000000,0x00072ab8,0x0002d6c0, +/* 0008 */ 0x00020630,0x00001000,0x000c74c2,0x000d4b82, +/* 000A */ 0x000475c2,0x00000000,0x0003430a,0x000c0000, +/* 000C */ 0x00042730,0x00001400, +/* S16_ASYNCCODECINPUTTASK */ +/* 000D */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x00000000, +/* 000F */ 0x000fa418,0x0000101f,0x0005d402,0x0001c500, +/* 0011 */ 0x000f0630,0x00001000,0x00004418,0x00001380, +/* 0013 */ 0x000e243d,0x000d394a,0x00049705,0x00000000, +/* 0015 */ 0x0007d530,0x000b4240,0x000e00f2,0x00001000, +/* 0017 */ 0x00009134,0x000ca20a,0x00004c90,0x00001000, +/* 0019 */ 0x0005d705,0x00000000,0x00004f25,0x00098240, +/* 001B */ 0x00004725,0x00000000,0x0000e48a,0x00000000, +/* 001D */ 0x00027295,0x0009c2c0,0x0003df25,0x00000000, +/* 001F */ 0x000e8030,0x00001001,0x0005f718,0x000ac600, +/* 0021 */ 0x0007cf30,0x000c2a01,0x00082630,0x00001001, +/* 0023 */ 0x000504a0,0x00001001,0x00029314,0x000bcb80, +/* 0025 */ 0x0003cf25,0x000b0e00,0x0004f5c0,0x00000000, +/* 0027 */ 0x00049118,0x000d888a,0x0007dd02,0x000c6efa, +/* 0029 */ 0x00000000,0x00000000,0x0004f5c0,0x00069c80, +/* 002B */ 0x0000d402,0x00000000,0x000e8630,0x00001001, +/* 002D */ 0x00079130,0x00000000,0x00049118,0x00090e00, +/* 002F */ 0x0006c10a,0x00000000,0x00000000,0x000c0000, +/* 0031 */ 0x0007cf30,0x00030580,0x00005725,0x00000000, +/* 0033 */ 0x000d84a0,0x00001001,0x00029314,0x000b4780, +/* 0035 */ 0x0003cf25,0x000b8600,0x00000000,0x00000000, +/* 0037 */ 0x00000000,0x000c0000,0x00000000,0x00042c80, +/* 0039 */ 0x0001dec1,0x000e488c,0x00031114,0x00000000, +/* 003B */ 0x0004f5c2,0x00000000,0x0003640a,0x00000000, +/* 003D */ 0x00000000,0x000e5084,0x00000000,0x000eb844, +/* 003F */ 0x00007001,0x00000000,0x00000734,0x00001000, +/* 0041 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4, +/* SPDIFITASK */ +/* 0043 */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x000d5384, +/* 0045 */ 0x0007e48a,0x00000000,0x00067718,0x00001000, +/* 0047 */ 0x0007a418,0x00001000,0x0007221a,0x00000000, +/* 0049 */ 0x0005d402,0x00014500,0x000b8630,0x00001002, +/* 004B */ 0x00004418,0x00001780,0x000e243d,0x000d394a, +/* 004D */ 0x00049705,0x00000000,0x0007d530,0x000b4240, +/* 004F */ 0x000ac0f2,0x00001002,0x00014414,0x00000000, +/* 0051 */ 0x00004c90,0x00001000,0x0005d705,0x00000000, +/* 0053 */ 0x00004f25,0x00098240,0x00004725,0x00000000, +/* 0055 */ 0x0000e48a,0x00000000,0x00027295,0x0009c2c0, +/* 0057 */ 0x0007df25,0x00000000,0x000ac030,0x00001003, +/* 0059 */ 0x0005f718,0x000fe798,0x00029314,0x000bcb80, +/* 005B */ 0x00000930,0x000b0e00,0x0004f5c0,0x000de204, +/* 005D */ 0x000884a0,0x00001003,0x0007cf25,0x000e3560, +/* 005F */ 0x00049118,0x00000000,0x00049118,0x000d888a, +/* 0061 */ 0x0007dd02,0x000c6efa,0x0000c434,0x00030040, +/* 0063 */ 0x000fda82,0x000c2312,0x000fdc0e,0x00001001, +/* 0065 */ 0x00083402,0x000c2b92,0x000706b0,0x00001003, +/* 0067 */ 0x00075a82,0x00000000,0x0000d625,0x000b0940, +/* 0069 */ 0x0000840e,0x00001002,0x0000aabc,0x000c511e, +/* 006B */ 0x00078730,0x00001003,0x0000aaf4,0x000e910a, +/* 006D */ 0x0004628a,0x00000000,0x00006aca,0x00000000, +/* 006F */ 0x00000930,0x00000000,0x0004f5c0,0x00069c80, +/* 0071 */ 0x00046ac0,0x00000000,0x0003c40a,0x000fc898, +/* 0073 */ 0x00049118,0x00090e00,0x0006c10a,0x00000000, +/* 0075 */ 0x00000000,0x000e5084,0x00000000,0x000eb844, +/* 0077 */ 0x00007001,0x00000000,0x00000734,0x00001000, +/* 0079 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4, +/* SPDIFOTASK */ +/* 007B */ 0x0006a108,0x000c0000,0x0004f4c0,0x000c3245, +/* 007D */ 0x0000a418,0x00001000,0x0003a20a,0x00000000, +/* 007F */ 0x00004418,0x00001380,0x000e243d,0x000d394a, +/* 0081 */ 0x000c9705,0x000def92,0x0008c030,0x00001004, +/* 0083 */ 0x0005f718,0x000fe798,0x00000000,0x000c0000, +/* 0085 */ 0x00005725,0x00000000,0x000704a0,0x00001004, +/* 0087 */ 0x00029314,0x000b4780,0x0003cf25,0x000b8600, +/* 0089 */ 0x00000000,0x00000000,0x00000000,0x000c0000, +/* 008B */ 0x00000000,0x00042c80,0x0001dec1,0x000e488c, +/* 008D */ 0x00031114,0x00000000,0x0004f5c2,0x00000000, +/* 008F */ 0x0004a918,0x00098600,0x0006c28a,0x00000000, +/* 0091 */ 0x00000000,0x000e5084,0x00000000,0x000eb844, +/* 0093 */ 0x00007001,0x00000000,0x00000734,0x00001000, +/* 0095 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4, +/* ASYNCHFGTXCODE */ +/* 0097 */ 0x0002a880,0x000b4e40,0x00042214,0x000e5548, +/* 0099 */ 0x000542bf,0x00000000,0x00000000,0x000481c0, +/* 009B */ 0x00000000,0x00000000,0x00000000,0x00000030, +/* 009D */ 0x0000072d,0x000fbf8a,0x00077f94,0x000ea7df, +/* 009F */ 0x0002ac95,0x000d3145,0x00002731,0x00001400, +/* 00A1 */ 0x00006288,0x000c71c4,0x00014108,0x000e6044, +/* 00A3 */ 0x00035408,0x00000000,0x00025418,0x000a0ec0, +/* 00A5 */ 0x0001443d,0x000ca21e,0x00046595,0x000d730c, +/* 00A7 */ 0x0006538e,0x00000000,0x00064630,0x00001005, +/* 00A9 */ 0x000e7b0e,0x000df782,0x000746b0,0x00001005, +/* 00AB */ 0x00036f05,0x000c0000,0x00043695,0x000d598c, +/* 00AD */ 0x0005331a,0x000f2185,0x00000000,0x00000000, +/* 00AF */ 0x000007ae,0x000bdb00,0x00040630,0x00001400, +/* 00B1 */ 0x0005e708,0x000c0000,0x0007ef30,0x000b1c00, +/* 00B3 */ 0x000d86a0,0x00001005,0x00066408,0x000c0000, +/* 00B5 */ 0x00000000,0x00000000,0x00021843,0x00000000, +/* 00B7 */ 0x00000cac,0x00062c00,0x00001dac,0x00063400, +/* 00B9 */ 0x00002cac,0x0006cc80,0x000db943,0x000e5ca1, +/* 00BB */ 0x00000000,0x00000000,0x0006680a,0x000f3205, +/* 00BD */ 0x00042730,0x00001400, +/* ASYNCHFGRXCODE */ +/* 00BE */ 0x00014108,0x000f2204,0x00025418,0x000a2ec0, +/* 00C0 */ 0x00015dbd,0x00038100,0x00015dbc,0x00000000, +/* 00C2 */ 0x0005e415,0x00034880,0x0001258a,0x000d730c, +/* 00C4 */ 0x0006538e,0x000baa40,0x00060630,0x00001006, +/* 00C6 */ 0x00067b0e,0x000ac380,0x0003ef05,0x00000000, +/* 00C8 */ 0x0000f734,0x0001c300,0x000586b0,0x00001400, +/* 00CA */ 0x000b6f05,0x000c3a00,0x00048f05,0x00000000, +/* 00CC */ 0x0005b695,0x0008c380,0x0002058e,0x00000000, +/* 00CE */ 0x000500b0,0x00001400,0x0002b318,0x000e998d, +/* 00D0 */ 0x0006430a,0x00000000,0x00000000,0x000ef384, +/* 00D2 */ 0x00004725,0x000c0000,0x00000000,0x000f3204, +/* 00D4 */ 0x00004f25,0x000c0000,0x00080000,0x000e5ca1, +/* 00D6 */ 0x000cb943,0x000e5ca1,0x0004b943,0x00000000, +/* 00D8 */ 0x00040730,0x00001400,0x000cb943,0x000e5ca1, +/* 00DA */ 0x0004b943,0x00000000 +}; +/* #CODE_END */ + +static segment_desc_t cwcasync_segments[] = { + { SEGTYPE_SP_PROGRAM, 0x00000000, 0x000001b6, cwcasync_code }, +}; + +static dsp_module_desc_t cwcasync_module = { + "cwcasync", + { + 32, + cwcasync_symbols + }, + 1, + cwcasync_segments, +}; + +#endif /* __HEADER_cwcasync_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcbinhack.h linux/sound/pci/cs46xx/imgs/cwcbinhack.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcbinhack.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwcbinhack.h 2002-08-01 03:26:33.000000000 -0600 @@ -0,0 +1,48 @@ +/* generated by Benny + MODIFY ON YOUR OWN RISK */ + +#ifndef __HEADER_cwcbinhack_H__ +#define __HEADER_cwcbinhack_H__ + +static symbol_entry_t cwcbinhack_symbols[] = { + { 0x02c8, "OVERLAYBEGINADDRESS",0x00 }, + { 0x02c8, "MAGICSNOOPTASK",0x03 }, + { 0x0308, "#CODE_END",0x00 }, +}; /* cwcbinhack symbols */ + +static u32 cwcbinhack_code[] = { + /* 0x02c8 */ + 0x0007bfb0,0x000bc240,0x00000c2e,0x000c6084, /* 1 */ + 0x000b8630,0x00001016,0x00006408,0x000efb84, /* 2 */ + 0x00016008,0x00000000,0x0001c088,0x000c0000, /* 3 */ + 0x000fc908,0x000e3392,0x0005f488,0x000efb84, /* 4 */ + 0x0001d402,0x000b2e00,0x0003d418,0x00001000, /* 5 */ + 0x0008d574,0x000c4293,0x00065625,0x000ea30e, /* 6 */ + 0x00096c01,0x000c6f92,0x0001a58a,0x000c6085, /* 7 */ + 0x00002f43,0x00000000,0x000e03a0,0x00001016, /* 8 */ + 0x0005e608,0x000c0000,0x00000000,0x00000000, /* 9 */ + 0x000ca108,0x000dcca1,0x00003bac,0x000c3205, /* 10 */ + 0x00073843,0x00000000,0x00010730,0x00001017, /* 11 */ + 0x0001600a,0x000c0000,0x00057488,0x00000000, /* 12 */ + 0x00000000,0x000e5084,0x00000000,0x000eba44, /* 13 */ + 0x00087401,0x000e4782,0x00000734,0x00001000, /* 14 */ + 0x00010705,0x000a6880,0x00006a88,0x000c75c4, /* 15 */ + 0x00000000,0x00000000,0x00000000,0x00000000, /* 16 */ +}; +/* #CODE_END */ + +static segment_desc_t cwcbinhack_segments[] = { + { SEGTYPE_SP_PROGRAM, 0x00000000, 64, cwcbinhack_code }, +}; + +static dsp_module_desc_t cwcbinhack_module = { + "cwcbinhack", + { + 3, + cwcbinhack_symbols + }, + 1, + cwcbinhack_segments, +}; + +#endif /* __HEADER_cwcbinhack_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcdma.asp linux/sound/pci/cs46xx/imgs/cwcdma.asp --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcdma.asp 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwcdma.asp 2003-03-03 06:07:18.000000000 -0700 @@ -0,0 +1,169 @@ +// +// Copyright(c) by Benny Sjostrand (benny@hostmobility.com) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +// +// This code runs inside the DSP (cs4610, cs4612, cs4624, or cs4630), +// to compile it you need a tool named SPASM 3.0 and DSP code owned by +// Cirrus Logic(R). The SPASM program will generate a object file (cwcdma.osp), +// the "ospparser" tool will genereate the cwcdma.h file it's included from +// the cs46xx_lib.c file. +// +// +// The purpose of this code is very simple: make it possible to tranfser +// the samples 'as they are' with no alteration from a PCMreader SCB (DMA from host) +// to any other SCB. This is useful for AC3 throug SPDIF. SRC (source rate converters) +// task always alters the samples in some how, however it's from 48khz -> 48khz. The +// alterations are not audible, but AC3 wont work. +// +// ... +// | +// +---------------+ +// | AsynchFGTxSCB | +// +---------------+ +// | +// subListPtr +// | +// +--------------+ +// | DMAReader | +// +--------------+ +// | +// subListPtr +// | +// +-------------+ +// | PCMReader | +// +-------------+ +// (DMA from host) +// + +struct dmaSCB + { + long dma_reserved1[3]; + + short dma_reserved2:dma_outBufPtr; + + short dma_unused1:dma_unused2; + + long dma_reserved3[4]; + + short dma_subListPtr:dma_nextSCB; + short dma_SPBptr:dma_entryPoint; + + long dma_strmRsConfig; + long dma_strmBufPtr; + + long dma_reserved4; + + VolumeControl s2m_volume; + }; + +#export DMAReader +void DMAReader() +{ + execChild(); + r2 = r0->dma_subListPtr; + r1 = r0->nextSCB; + + rsConfig01 = r2->strmRsConfig; + // Load rsConfig for input buffer + + rsDMA01 = r2->basicReq.daw, , tb = Z(0 - rf); + // Load rsDMA in case input buffer is a DMA buffer Test to see if there is any data to transfer + + if (tb) goto execSibling_2ind1 after { + r5 = rf + (-1); + r6 = r1->dma_entryPoint; // r6 = entry point of sibling task + r1 = r1->dma_SPBptr, // r1 = pointer to sibling task's SPB + , ind = r6; // Load entry point of sibling task + } + + rsConfig23 = r0->dma_strmRsConfig; + // Load rsConfig for output buffer (never a DMA buffer) + + r4 = r0->dma_outBufPtr; + + rsa0 = r2->strmBufPtr; + // rsa0 = input buffer pointer + + for (i = r5; i >= 0; --i) + after { + rsa2 = r4; + // rsa2 = output buffer pointer + + nop; + nop; + } + //***************************** + // TODO: cycles to this point * + //***************************** + { + acc0 = (rsd0 = *rsa0++1); + // get sample + + nop; // Those "nop"'s are really uggly, but there's + nop; // something with DSP's pipelines which I don't + nop; // understand, resulting this code to fail without + // having those "nop"'s (Benny) + + rsa0?reqDMA = r2; + // Trigger DMA transfer on input stream, + // if needed to replenish input buffer + + nop; + // Yet another magic "nop" to make stuff work + + ,,r98 = acc0 $+>> 0; + // store sample in ALU + + nop; + // latency on load register. + // (this one is understandable) + + *rsa2++1 = r98; + // store sample in output buffer + + nop; // The same story + nop; // as above again ... + nop; + } + // TODO: cycles per loop iteration + + r2->strmBufPtr = rsa0,, ; + // Update the modified buffer pointers + + r4 = rsa2; + // Load output pointer position into r4 + + r2 = r0->nextSCB; + // Sibling task + + goto execSibling_2ind1 // takes 6 cycles + after { + r98 = r2->thisSPB:entryPoint; + // Load child routine entry and data address + + r1 = r9; + // r9 is r2->thisSPB + + r0->dma_outBufPtr = r4,, + // Store updated output buffer pointer + + ind = r8; + // r8 is r2->entryPoint + } +} diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcdma.h linux/sound/pci/cs46xx/imgs/cwcdma.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcdma.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwcdma.h 2003-03-03 06:07:18.000000000 -0700 @@ -0,0 +1,68 @@ +/* generated from cwcdma.osp DO NOT MODIFY */ + +#ifndef __HEADER_cwcdma_H__ +#define __HEADER_cwcdma_H__ + +symbol_entry_t cwcdma_symbols[] = { + { 0x8000, "EXECCHILD",0x03 }, + { 0x8001, "EXECCHILD_98",0x03 }, + { 0x8003, "EXECCHILD_PUSH1IND",0x03 }, + { 0x8008, "EXECSIBLING",0x03 }, + { 0x800a, "EXECSIBLING_298",0x03 }, + { 0x800b, "EXECSIBLING_2IND1",0x03 }, + { 0x8010, "TIMINGMASTER",0x03 }, + { 0x804f, "S16_CODECINPUTTASK",0x03 }, + { 0x805e, "PCMSERIALINPUTTASK",0x03 }, + { 0x806d, "S16_MIX_TO_OSTREAM",0x03 }, + { 0x809a, "S16_MIX",0x03 }, + { 0x80bb, "S16_UPSRC",0x03 }, + { 0x813b, "MIX3_EXP",0x03 }, + { 0x8164, "DECIMATEBYPOW2",0x03 }, + { 0x8197, "VARIDECIMATE",0x03 }, + { 0x81f2, "_3DINPUTTASK",0x03 }, + { 0x820a, "_3DPRLGCINPTASK",0x03 }, + { 0x8227, "_3DSTEREOINPUTTASK",0x03 }, + { 0x8242, "_3DOUTPUTTASK",0x03 }, + { 0x82c4, "HRTF_MORPH_TASK",0x03 }, + { 0x82c6, "WAIT4DATA",0x03 }, + { 0x82fa, "PROLOGIC",0x03 }, + { 0x8496, "DECORRELATOR",0x03 }, + { 0x84a4, "STEREO2MONO",0x03 }, + { 0x0000, "OVERLAYBEGINADDRESS",0x00 }, + { 0x0000, "DMAREADER",0x03 }, + { 0x0018, "#CODE_END",0x00 }, +}; /* cwcdma symbols */ + +u32 cwcdma_code[] = { +/* OVERLAYBEGINADDRESS */ +/* 0000 */ 0x00002731,0x00001400,0x0004c108,0x000e5044, +/* 0002 */ 0x0005f608,0x00000000,0x000007ae,0x000be300, +/* 0004 */ 0x00058630,0x00001400,0x0007afb0,0x000e9584, +/* 0006 */ 0x00007301,0x000a9840,0x0005e708,0x000cd104, +/* 0008 */ 0x00067008,0x00000000,0x000902a0,0x00001000, +/* 000A */ 0x00012a01,0x000c0000,0x00000000,0x00000000, +/* 000C */ 0x00021843,0x000c0000,0x00000000,0x000c0000, +/* 000E */ 0x0000e101,0x000c0000,0x00000cac,0x00000000, +/* 0010 */ 0x00080000,0x000e5ca1,0x00000000,0x000c0000, +/* 0012 */ 0x00000000,0x00000000,0x00000000,0x00092c00, +/* 0014 */ 0x000122c1,0x000e5084,0x00058730,0x00001400, +/* 0016 */ 0x000d7488,0x000e4782,0x00007401,0x0001c100 +}; + +/* #CODE_END */ + +segment_desc_t cwcdma_segments[] = { + { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000030, cwcdma_code }, +}; + +dsp_module_desc_t cwcdma_module = { + "cwcdma", + { + 27, + cwcdma_symbols + }, + 1, + cwcdma_segments, +}; + +#endif /* __HEADER_cwcdma_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcemb80.h linux/sound/pci/cs46xx/imgs/cwcemb80.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcemb80.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwcemb80.h 2002-08-01 03:26:33.000000000 -0600 @@ -0,0 +1,1607 @@ +/* generated from cwcemb80.osp DO NOT MODIFY */ + +#ifndef __HEADER_cwcemb80_H__ +#define __HEADER_cwcemb80_H__ + +static symbol_entry_t cwcemb80_symbols[] = { + { 0x0000, "BEGINADDRESS",0x00 }, + { 0x8000, "EXECCHILD",0x03 }, + { 0x8001, "EXECCHILD_98",0x03 }, + { 0x8003, "EXECCHILD_PUSH1IND",0x03 }, + { 0x8008, "EXECSIBLING",0x03 }, + { 0x800a, "EXECSIBLING_298",0x03 }, + { 0x800b, "EXECSIBLING_2IND1",0x03 }, + { 0x8010, "TIMINGMASTER",0x03 }, + { 0x804f, "S16_CODECINPUTTASK",0x03 }, + { 0x805e, "PCMSERIALINPUTTASK",0x03 }, + { 0x806d, "S16_MIX_TO_OSTREAM",0x03 }, + { 0x809a, "S16_MIX",0x03 }, + { 0x80bb, "S16_UPSRC",0x03 }, + { 0x813b, "MIX3_EXP",0x03 }, + { 0x8164, "DECIMATEBYPOW2",0x03 }, + { 0x8197, "VARIDECIMATE",0x03 }, + { 0x81f2, "_3DINPUTTASK",0x03 }, + { 0x820a, "_3DPRLGCINPTASK",0x03 }, + { 0x8227, "_3DSTEREOINPUTTASK",0x03 }, + { 0x8242, "_3DOUTPUTTASK",0x03 }, + { 0x82c4, "HRTF_MORPH_TASK",0x03 }, + { 0x82c6, "WAIT4DATA",0x03 }, + { 0x82fa, "PROLOGIC",0x03 }, + { 0x8496, "DECORRELATOR",0x03 }, + { 0x84a4, "STEREO2MONO",0x03 }, + { 0x0070, "SPOSCB",0x02 }, + { 0x0105, "TASKTREETHREAD",0x03 }, + { 0x0136, "TASKTREEHEADERCODE",0x03 }, + { 0x013f, "FGTASKTREEHEADERCODE",0x03 }, + { 0x0163, "NULLALGORITHM",0x03 }, + { 0x0167, "HFGEXECCHILD",0x03 }, + { 0x0168, "HFGEXECCHILD_98",0x03 }, + { 0x016a, "HFGEXECCHILD_PUSH1IND",0x03 }, + { 0x016d, "HFGEXECSIBLING",0x03 }, + { 0x016f, "HFGEXECSIBLING_298",0x03 }, + { 0x0170, "HFGEXECSIBLING_2IND1",0x03 }, + { 0x0173, "S16_CODECOUTPUTTASK",0x03 }, + { 0x018e, "#CODE_END",0x00 }, +}; /* cwcemb80 symbols */ + +static u32 cwcemb80_code[] = { +/* BEGINADDRESS */ +/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003, +/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003, +/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003, +/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003, +/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003, +/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003, +/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003, +/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003, +/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003, +/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003, +/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040, +/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003, +/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003, +/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003, +/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003, +/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003, +/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003, +/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003, +/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003, +/* 0040 */ 0x0000a730,0x00001008,0x000e2730,0x00001002, +/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002, +/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002, +/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002, +/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003, +/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000, +/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000, +/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000, +/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000, +/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000, +/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000, +/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003, +/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003, +/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000, +/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003, +/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000, +/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000, +/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000, +/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000, +/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140, +/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40, +/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00, +/* 007C */ 0x0000002e,0x0009d6c0,0x00038630,0x00001004, +/* 007E */ 0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, +/* 0080 */ 0x00000000,0x000c70e0,0x0007d182,0x0002c640, +/* 0082 */ 0x00000630,0x00001004,0x000799b8,0x0002c6c0, +/* 0084 */ 0x00031705,0x00092240,0x00039f05,0x000932c0, +/* 0086 */ 0x0003520a,0x00000000,0x00040731,0x0000100b, +/* 0088 */ 0x00010705,0x000b20c0,0x00000000,0x000eba44, +/* 008A */ 0x00032108,0x000c60c4,0x00065208,0x000c2917, +/* 008C */ 0x000406b0,0x00001007,0x00012f05,0x00036880, +/* 008E */ 0x0002818e,0x000c0000,0x0004410a,0x00000000, +/* 0090 */ 0x00040630,0x00001007,0x00029705,0x000c0000, +/* 0092 */ 0x00000000,0x00000000,0x00003fc1,0x0003fc40, +/* 0094 */ 0x000037c1,0x00091b40,0x00003fc1,0x000911c0, +/* 0096 */ 0x000037c1,0x000957c0,0x00003fc1,0x000951c0, +/* 0098 */ 0x000037c1,0x00000000,0x00003fc1,0x000991c0, +/* 009A */ 0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, +/* 009C */ 0x000037c1,0x00000000,0x0001ccc1,0x000915c0, +/* 009E */ 0x0001c441,0x0009d800,0x0009cdc1,0x00091240, +/* 00A0 */ 0x0001c541,0x00091d00,0x0009cfc1,0x00095240, +/* 00A2 */ 0x0001c741,0x00095c80,0x000e8ca9,0x00099240, +/* 00A4 */ 0x000e85ad,0x00095640,0x00069ca9,0x00099d80, +/* 00A6 */ 0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, +/* 00A8 */ 0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, +/* 00AA */ 0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, +/* 00AC */ 0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, +/* 00AE */ 0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, +/* 00B0 */ 0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, +/* 00B2 */ 0x0006fca9,0x00002500,0x000fb208,0x000c59a0, +/* 00B4 */ 0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, +/* 00B6 */ 0x000683ad,0x00095241,0x00020f05,0x000991c1, +/* 00B8 */ 0x00000000,0x00000000,0x00086f88,0x00001000, +/* 00BA */ 0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, +/* 00BC */ 0x0009de81,0x000bd300,0x0009d601,0x000b1700, +/* 00BE */ 0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, +/* 00C0 */ 0x000a0f81,0x000bd740,0x00020701,0x000b5c80, +/* 00C2 */ 0x000a1681,0x000b97c0,0x00021601,0x00002500, +/* 00C4 */ 0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, +/* 00C6 */ 0x00021681,0x00002d00,0x00020f81,0x000bd800, +/* 00C8 */ 0x000a0701,0x000b5bc0,0x00021601,0x00003500, +/* 00CA */ 0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, +/* 00CC */ 0x00021681,0x00003d00,0x00020f81,0x000b1d00, +/* 00CE */ 0x000a0701,0x000b1fc0,0x00021601,0x00020500, +/* 00D0 */ 0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, +/* 00D2 */ 0x00021681,0x00020d00,0x00020f81,0x000bde80, +/* 00D4 */ 0x000a0701,0x000bdfc0,0x00021601,0x00021500, +/* 00D6 */ 0x00020f81,0x000b9341,0x00020701,0x000b53c1, +/* 00D8 */ 0x00021681,0x00021d00,0x000a0f81,0x000d0380, +/* 00DA */ 0x0000b601,0x000b15c0,0x00007b01,0x00000000, +/* 00DC */ 0x00007b81,0x000bd1c0,0x00007b01,0x00000000, +/* 00DE */ 0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, +/* 00E0 */ 0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, +/* 00E2 */ 0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, +/* 00E4 */ 0x0007e488,0x000d7e45,0x00000000,0x000d7a44, +/* 00E6 */ 0x0007e48a,0x00000000,0x00011f05,0x00084080, +/* 00E8 */ 0x00000000,0x00000000,0x00001705,0x000b3540, +/* 00EA */ 0x00008a01,0x000bf040,0x00007081,0x000bb5c0, +/* 00EC */ 0x00055488,0x00000000,0x0000d482,0x0003fc40, +/* 00EE */ 0x0003fc88,0x00000000,0x0001e401,0x000b3a00, +/* 00F0 */ 0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, +/* 00F2 */ 0x000c86b0,0x00001007,0x00008281,0x000bb240, +/* 00F4 */ 0x0000b801,0x000b7140,0x00007888,0x00000000, +/* 00F6 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000, +/* 00F8 */ 0x00000000,0x00000000,0x00055288,0x000c555c, +/* 00FA */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +/* 00FC */ 0x0000fa88,0x00000000,0x00000032,0x00001000, +/* 00FE */ 0x0000073d,0x00001000,0x0007f188,0x000c0000, +/* 0100 */ 0x00000000,0x00000000,0x0008c01c,0x00001003, +/* 0102 */ 0x00002705,0x00001008,0x0008b201,0x000c1392, +/* 0104 */ 0x0000ba01,0x00000000, +/* TASKTREETHREAD */ +/* 0105 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4, +/* 0107 */ 0x00057488,0x00000000,0x000a6388,0x00001001, +/* 0109 */ 0x0008b334,0x000bc141,0x0003020e,0x00000000, +/* 010B */ 0x000886b0,0x00001008,0x00003625,0x000c5dfa, +/* 010D */ 0x000a638a,0x00001001,0x0008020e,0x00001002, +/* 010F */ 0x0008a6b0,0x00001008,0x0007f301,0x00000000, +/* 0111 */ 0x00000000,0x00000000,0x00002725,0x000a8c40, +/* 0113 */ 0x000000ae,0x00000000,0x000d8630,0x00001008, +/* 0115 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640, +/* 0117 */ 0x000a8630,0x00001008,0x000799b8,0x0002d6c0, +/* 0119 */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000, +/* 011B */ 0x00062208,0x000c4117,0x00070630,0x00001009, +/* 011D */ 0x00000000,0x000c0000,0x0001022e,0x00000000, +/* 011F */ 0x0003a630,0x00001009,0x00000000,0x000c0000, +/* 0121 */ 0x00000036,0x00001000,0x00000000,0x00000000, +/* 0123 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0125 */ 0x00000000,0x00000000,0x0002a730,0x00001008, +/* 0127 */ 0x0007f801,0x000c0000,0x00000037,0x00001000, +/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 012B */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 012D */ 0x0002a730,0x00001008,0x00000033,0x00001000, +/* 012F */ 0x0002a705,0x00001008,0x00007a01,0x000c0000, +/* 0131 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000, +/* 0133 */ 0x00060730,0x0000100a,0x00000000,0x000c0000, +/* 0135 */ 0x00000000,0x00000000, +/* TASKTREEHEADERCODE */ +/* 0136 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100b, +/* 0138 */ 0x00057488,0x00000000,0x00033b94,0x00081140, +/* 013A */ 0x000183ae,0x00000000,0x000786b0,0x0000100b, +/* 013C */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +/* 013E */ 0x00042731,0x00001003, +/* FGTASKTREEHEADERCODE */ +/* 013F */ 0x0007aab0,0x00034880,0x00048fb0,0x0000100a, +/* 0141 */ 0x00057488,0x00000000,0x00033b94,0x00081140, +/* 0143 */ 0x000183ae,0x00000000,0x000806b0,0x0000100b, +/* 0145 */ 0x00022f05,0x00000000,0x00007401,0x00091140, +/* 0147 */ 0x00048f05,0x000951c0,0x00042731,0x00001003, +/* 0149 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47, +/* 014B */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003, +/* 014D */ 0x00000000,0x00000000,0x0008e19c,0x00001003, +/* 014F */ 0x000083c1,0x00093040,0x00000f41,0x00097140, +/* 0151 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +/* 0153 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +/* 0155 */ 0x00000000,0x000fdc44,0x00055208,0x00000000, +/* 0157 */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00, +/* 0159 */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000, +/* 015B */ 0x00012f05,0x00036880,0x00065308,0x000c2997, +/* 015D */ 0x000d86b0,0x0000100a,0x0004410a,0x000d40c7, +/* 015F */ 0x00000000,0x00000000,0x00080730,0x00001004, +/* 0161 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000, +/* NULLALGORITHM */ +/* 0163 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47, +/* 0165 */ 0x00080000,0x000bffc7,0x0000273d,0x00001000, +/* HFGEXECCHILD */ +/* 0167 */ 0x00000000,0x000eba44, +/* HFGEXECCHILD_98 */ +/* 0168 */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +/* HFGEXECCHILD_PUSH1IND */ +/* 016A */ 0x00000734,0x00001000,0x00010705,0x000a6880, +/* 016C */ 0x00006a88,0x000c75c4, +/* HFGEXECSIBLING */ +/* 016D */ 0x00000000,0x000e5084,0x00000000,0x000eba44, +/* HFGEXECSIBLING_298 */ +/* 016F */ 0x00087401,0x000e4782, +/* HFGEXECSIBLING_2IND1 */ +/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880, +/* 0172 */ 0x00006a88,0x000c75c4, +/* S16_CODECOUTPUTTASK */ +/* 0173 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40, +/* 0175 */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80, +/* 0177 */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0, +/* 0179 */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0, +/* 017B */ 0x00073fb0,0x00074c80,0x000283a0,0x0000100c, +/* 017D */ 0x000ee388,0x00042970,0x00008301,0x00021ef2, +/* 017F */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b, +/* 0181 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916, +/* 0183 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000, +/* 0185 */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b, +/* 0187 */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956, +/* 0189 */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40, +/* 018B */ 0x00058730,0x00001400,0x000d7488,0x000c3a00, +/* 018D */ 0x00048f05,0x00000000 +}; +/* #CODE_END */ + +static u32 cwcemb80_parameter[] = { +/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0008 */ 0x00000000,0x00000000,0x00000163,0x00000000, +/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0018 */ 0x00000000,0x00200040,0x00008010,0x00000000, +/* 001C */ 0x00000000,0x80000001,0x00000001,0x00060000, +/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0028 */ 0x00000000,0x00900080,0x00000173,0x00000000, +/* 002C */ 0x00000000,0x00000010,0x00800000,0x00900000, +/* 0030 */ 0xf2c0000f,0x00000200,0x00000000,0x00010600, +/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0038 */ 0x00000000,0x00000000,0x00000163,0x330300c2, +/* 003C */ 0x06000000,0x00000000,0x80008000,0x80008000, +/* 0040 */ 0x3fc0000f,0x00000301,0x00010400,0x00000000, +/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0048 */ 0x00000000,0x00b00000,0x00d0806d,0x330480c3, +/* 004C */ 0x04800000,0x00000001,0x00800001,0x0000ffff, +/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0070 */ 0x066a0600,0x06350070,0x0000929d,0x929d929d, +/* 0074 */ 0x00000000,0x0000735a,0x00000600,0x00000000, +/* 0078 */ 0x929d735a,0x00000000,0x00010000,0x735a735a, +/* 007C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0080 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0084 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0088 */ 0x00000000,0x00000000,0x0000804f,0x000000c3, +/* 008C */ 0x05000000,0x00a00010,0x00000000,0x80008000, +/* 0090 */ 0x00000000,0x00000000,0x00000700,0x00000000, +/* 0094 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0098 */ 0x00000080,0x00a00000,0x0000809a,0x000000c2, +/* 009C */ 0x07400000,0x00000000,0x80008000,0xffffffff, +/* 00A0 */ 0x00c80028,0x00005555,0x00000000,0x000107a0, +/* 00A4 */ 0x00c80028,0x000000c2,0x06800000,0x00000000, +/* 00A8 */ 0x06e00080,0x00300000,0x000080bb,0x000000c9, +/* 00AC */ 0x07a00000,0x04000000,0x80008000,0xffffffff, +/* 00B0 */ 0x00c80028,0x00005555,0x00000000,0x00000780, +/* 00B4 */ 0x00c80028,0x000000c5,0xff800000,0x00000000, +/* 00B8 */ 0x00640080,0x00c00000,0x00008197,0x000000c9, +/* 00BC */ 0x07800000,0x04000000,0x80008000,0xffffffff, +/* 00C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00C8 */ 0x00000000,0x00000000,0x0000805e,0x000000c1, +/* 00CC */ 0x00000000,0x00800000,0x80008000,0x80008000, +/* 00D0 */ 0x00020000,0x0000ffff,0x00000000,0x00000000, +/* 00D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0100 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0104 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0108 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 010C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0110 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0114 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0118 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 011C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0120 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0124 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0128 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 012C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0130 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0134 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0138 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 013C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0140 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0144 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0148 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 014C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0150 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0154 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0158 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 015C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0160 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0164 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0168 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 016C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0170 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0174 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0178 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 017C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0180 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0184 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0188 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 018C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0190 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0194 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0198 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 019C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0200 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0204 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0208 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 020C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0210 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0214 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0218 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 021C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0220 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0224 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0228 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 022C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0230 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0234 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0238 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 023C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0240 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0244 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0248 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 024C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0250 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0254 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0258 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 025C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0260 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0264 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0268 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 026C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0270 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0274 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0278 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 027C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0280 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0284 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0288 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 028C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0290 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0294 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0298 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 029C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0300 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0304 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0308 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 030C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0310 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0314 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0318 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 031C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0320 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0324 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0328 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 032C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0330 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0334 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0338 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 033C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0340 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0344 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0348 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 034C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0350 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0354 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0358 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 035C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0360 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0364 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0368 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 036C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0370 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0374 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0378 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 037C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0380 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0384 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0388 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 038C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0390 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0394 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0398 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 039C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0400 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0404 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0408 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 040C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0410 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0414 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0418 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 041C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0420 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0424 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0428 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 042C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0430 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0434 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0438 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 043C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0440 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0444 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0448 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 044C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0450 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0454 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0458 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 045C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0460 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0464 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0468 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 046C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0470 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0474 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0478 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 047C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0480 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0484 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0488 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 048C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0490 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0494 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0498 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 049C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0500 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0504 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0508 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 050C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0510 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0514 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0518 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 051C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0520 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0524 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0528 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 052C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0530 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0534 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0538 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 053C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0540 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0544 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0548 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 054C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0550 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0554 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0558 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 055C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0560 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0564 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0568 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 056C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0570 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0574 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0578 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 057C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0580 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0584 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0588 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 058C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0590 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0594 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0598 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 059C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0600 */ 0x929d0600,0x929d929d,0x929d929d,0x929d0000, +/* 0604 */ 0x929d929d,0x929d929d,0x929d929d,0x929d929d, +/* 0608 */ 0x929d929d,0x00100635,0x060b013f,0x00000004, +/* 060C */ 0x00000001,0x007a0002,0x00000000,0x066e0610, +/* 0610 */ 0x0105929d,0x929d929d,0x929d929d,0x929d929d, +/* 0614 */ 0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, +/* 0618 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 061C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0620 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0624 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0628 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 062C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0630 */ 0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, +/* 0634 */ 0x00000000,0x929d929d,0x929d929d,0x929d929d, +/* 0638 */ 0x929d929d,0x929d929d,0x929d929d,0x929d929d, +/* 063C */ 0x929d929d,0x929d929d,0x00000000,0x06400136, +/* 0640 */ 0x0000270f,0x00010000,0x007a0000,0x00000000, +/* 0644 */ 0x068e0645,0x0105929d,0x929d929d,0x929d929d, +/* 0648 */ 0x929d929d,0x929d929d,0xa431ac75,0x0001735a, +/* 064C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0650 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0654 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0658 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 065C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0660 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0664 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +/* 0668 */ 0x735a0100,0x00000000,0x00000000,0x00000000, +/* 066C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0670 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0674 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0678 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 067C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0680 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0684 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0688 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 068C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0690 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0694 */ 0x00000000,0x00000000,0x00000000 +}; /* #PARAMETER_END */ + +static u32 cwcemb80_sample[] = { +/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0080 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0084 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0088 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 008C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0090 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0094 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0098 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 009C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 00FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0100 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0104 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0108 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 010C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0110 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0114 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0118 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 011C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0120 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0124 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0128 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 012C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0130 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0134 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0138 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 013C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0140 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0144 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0148 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 014C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0150 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0154 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0158 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 015C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0160 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0164 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0168 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 016C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0170 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0174 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0178 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 017C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0180 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0184 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0188 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 018C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0190 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0194 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0198 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 019C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 01FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0200 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0204 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0208 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 020C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0210 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0214 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0218 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 021C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0220 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0224 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0228 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 022C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0230 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0234 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0238 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 023C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0240 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0244 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0248 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 024C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0250 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0254 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0258 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 025C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0260 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0264 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0268 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 026C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0270 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0274 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0278 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 027C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0280 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0284 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0288 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 028C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0290 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0294 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0298 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 029C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 02FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0300 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0304 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0308 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 030C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0310 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0314 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0318 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 031C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0320 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0324 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0328 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 032C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0330 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0334 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0338 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 033C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0340 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0344 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0348 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 034C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0350 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0354 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0358 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 035C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0360 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0364 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0368 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 036C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0370 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0374 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0378 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 037C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0380 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0384 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0388 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 038C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0390 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0394 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0398 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 039C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 03FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0400 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0404 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0408 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 040C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0410 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0414 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0418 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 041C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0420 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0424 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0428 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 042C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0430 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0434 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0438 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 043C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0440 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0444 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0448 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 044C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0450 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0454 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0458 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 045C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0460 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0464 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0468 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 046C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0470 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0474 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0478 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 047C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0480 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0484 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0488 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 048C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0490 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0494 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0498 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 049C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 04FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0500 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0504 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0508 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 050C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0510 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0514 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0518 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 051C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0520 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0524 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0528 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 052C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0530 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0534 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0538 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 053C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0540 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0544 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0548 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 054C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0550 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0554 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0558 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 055C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0560 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0564 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0568 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 056C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0570 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0574 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0578 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 057C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0580 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0584 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0588 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 058C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0590 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0594 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0598 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 059C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 05FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0600 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0604 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0608 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 060C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0610 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0614 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0618 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 061C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0620 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0624 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0628 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 062C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0630 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0634 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0638 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 063C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0640 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0644 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0648 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 064C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0650 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0654 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0658 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 065C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0660 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0664 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0668 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 066C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0670 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0674 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0678 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 067C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0680 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0684 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0688 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 068C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0690 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0694 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0698 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 069C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 06FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0700 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0704 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0708 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 070C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0710 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0714 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0718 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 071C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0720 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0724 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0728 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 072C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0730 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0734 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0738 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 073C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0740 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0744 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0748 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 074C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0750 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0754 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0758 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 075C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0760 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0764 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0768 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 076C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0770 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0774 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0778 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 077C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0780 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0784 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0788 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 078C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0790 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0794 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0798 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 079C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 07FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0800 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0804 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0808 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 080C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0810 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0814 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0818 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 081C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0820 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0824 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0828 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 082C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0830 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0834 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0838 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 083C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0840 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0844 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0848 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 084C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0850 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0854 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0858 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 085C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0860 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0864 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0868 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 086C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0870 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0874 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0878 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 087C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0880 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0884 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0888 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 088C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0890 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0894 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0898 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 089C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 08FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0900 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0904 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0908 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 090C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0910 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0914 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0918 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 091C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0920 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0924 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0928 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 092C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0930 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0934 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0938 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 093C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0940 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0944 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0948 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 094C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0950 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0954 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0958 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 095C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0960 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0964 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0968 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 096C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0970 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0974 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0978 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 097C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0980 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0984 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0988 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 098C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0990 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0994 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0998 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 099C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09A0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09A4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09A8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09AC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09B0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09B4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09B8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09BC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09C0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09C4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09C8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09CC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09D0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09D4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09D8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09DC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09E0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09E4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09E8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09EC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09F0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09F4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09F8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 09FC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A00 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A04 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A08 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A0C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A10 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A14 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A18 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A1C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A20 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A24 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A28 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A2C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A30 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A34 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A38 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A3C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A40 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A44 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A48 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A4C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A50 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A54 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A58 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A5C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A60 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A64 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A68 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A6C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A70 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A74 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A78 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A7C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A80 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A84 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A88 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A8C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A90 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A94 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A98 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0A9C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AA0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AA4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AA8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AAC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AB0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AB4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AB8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0ABC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AC0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AC4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AC8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0ACC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AD0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AD4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AD8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0ADC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AE0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AE4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AE8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AEC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AF0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AF4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AF8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0AFC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B00 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B04 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B08 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B0C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B10 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B14 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B18 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B1C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B20 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B24 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B28 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B2C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B30 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B34 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B38 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B3C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B40 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B44 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B48 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B4C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B50 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B54 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B58 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B5C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B60 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B64 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B68 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B6C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B70 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B74 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B78 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B7C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B80 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B84 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B88 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B8C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B90 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B94 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B98 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0B9C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BA0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BA4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BA8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BAC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BB0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BB4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BB8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BBC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BC0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BC4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BC8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BCC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BD0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BD4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BD8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BDC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BE0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BE4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BE8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BEC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BF0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BF4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BF8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0BFC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C00 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C04 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C08 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C0C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C10 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C14 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C18 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C1C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C20 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C24 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C28 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C2C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C30 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C34 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C38 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C3C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C40 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C44 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C48 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C4C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C50 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C54 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C58 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C5C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C60 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C64 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C68 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C6C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C70 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C74 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C78 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C7C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C80 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C84 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C88 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C8C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C90 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C94 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C98 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0C9C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CA0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CA4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CA8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CAC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CB0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CB4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CB8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CBC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CC0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CC4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CC8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CCC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CD0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CD4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CD8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CDC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CE0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CE4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CE8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CEC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CF0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CF4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CF8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0CFC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D00 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D04 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D08 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D0C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D10 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D14 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D18 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D1C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D20 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D24 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D28 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D2C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D30 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D34 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D38 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D3C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D40 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D44 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D48 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D4C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D50 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D54 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D58 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D5C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D60 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D64 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D68 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D6C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D70 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D74 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D78 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D7C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D80 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D84 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D88 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D8C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D90 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D94 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D98 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0D9C */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DA0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DA4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DA8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DAC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DB0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DB4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DB8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DBC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DC0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DC4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DC8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DCC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DD0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DD4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DD8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DDC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DE0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DE4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DE8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DEC */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DF0 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DF4 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DF8 */ 0x00000000,0x00000000,0x00000000,0x00000000, +/* 0DFC */ 0x00000000,0x00000000,0x00000000,0x00010004 +}; /* #SAMPLE_END */ + + +static segment_desc_t cwcemb80_segments[] = { + { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000031c, cwcemb80_code }, + { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000697, cwcemb80_parameter }, + { SEGTYPE_SP_SAMPLE, 0x00000000, 0x00000e00, cwcemb80_sample }, +}; + +static dsp_module_desc_t cwcemb80_module = { + "cwcemb80", + { + 38, + cwcemb80_symbols + }, + 3, + cwcemb80_segments, +}; + +#endif /* __HEADER_cwcemb80_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcsnoop.h linux/sound/pci/cs46xx/imgs/cwcsnoop.h --- linux-2.4.21-rc1.orig/sound/pci/cs46xx/imgs/cwcsnoop.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/cs46xx/imgs/cwcsnoop.h 2002-08-01 03:26:33.000000000 -0600 @@ -0,0 +1,46 @@ +/* generated from cwcsnoop.osp DO NOT MODIFY */ + +#ifndef __HEADER_cwcsnoop_H__ +#define __HEADER_cwcsnoop_H__ + +static symbol_entry_t cwcsnoop_symbols[] = { + { 0x0500, "OVERLAYBEGINADDRESS",0x00 }, + { 0x0500, "OUTPUTSNOOP",0x03 }, + { 0x051f, "#CODE_END",0x00 }, +}; /* cwcsnoop symbols */ + +static u32 cwcsnoop_code[] = { +/* 0000 */ 0x0007bfb0,0x000b4e40,0x0007c088,0x000c0617, +/* 0002 */ 0x00049705,0x00000000,0x00080630,0x00001028, +/* 0004 */ 0x00076408,0x000efb84,0x00066008,0x00000000, +/* 0006 */ 0x0007c908,0x000c0000,0x00046725,0x000efa44, +/* 0008 */ 0x0005f708,0x00000000,0x0001d402,0x000b2e00, +/* 000A */ 0x0003d418,0x00001000,0x0008d574,0x000c4293, +/* 000C */ 0x00065625,0x000ea30e,0x00096c01,0x000c6f92, +/* 000E */ 0x0006a58a,0x000f6085,0x00002f43,0x00000000, +/* 0010 */ 0x000a83a0,0x00001028,0x0005e608,0x000c0000, +/* 0012 */ 0x00000000,0x00000000,0x000ca108,0x000dcca1, +/* 0014 */ 0x00003bac,0x000fb205,0x00073843,0x00000000, +/* 0016 */ 0x000d8730,0x00001028,0x0006600a,0x000c0000, +/* 0018 */ 0x00057488,0x00000000,0x00000000,0x000e5084, +/* 001A */ 0x00000000,0x000eba44,0x00087401,0x000e4782, +/* 001C */ 0x00000734,0x00001000,0x00010705,0x000a6880, +/* 001E */ 0x00006a88,0x000c75c4 +}; +/* #CODE_END */ + +static segment_desc_t cwcsnoop_segments[] = { + { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000003e, cwcsnoop_code }, +}; + +static dsp_module_desc_t cwcsnoop_module = { + "cwcsnoop", + { + 3, + cwcsnoop_symbols + }, + 1, + cwcsnoop_segments, +}; + +#endif /* __HEADER_cwcsnoop_H__ */ diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/Makefile linux/sound/pci/emu10k1/Makefile --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _emu10k1.o + +list-multi := snd-emu10k1.o snd-emu10k1-synth.o + +export-objs := emu10k1_main.o + +snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ + irq.o memory.o voice.o emumpu401.o emupcm.o io.o \ + emuproc.o emumixer.o emufx.o +snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-emu10k1.o: $(snd-emu10k1-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-objs) + +snd-emu10k1-synth.o: $(snd-emu10k1-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-synth-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1.c linux/sound/pci/emu10k1/emu10k1.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emu10k1.c 2003-01-20 08:20:40.000000000 -0700 @@ -0,0 +1,252 @@ +/* + * The driver for the EMU10K1 (SB Live!) based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("EMU10K1"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Creative Labs,SB Live!/PCI512/E-mu APS}," + "{Creative Labs,SB Audigy}}"); + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#define ENABLE_SYNTH +#include +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; +static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; +static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; +static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(extin, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default."); +MODULE_PARM_SYNTAX(extin, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); +MODULE_PARM(extout, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(extout, "Available external outputs for FX8010. Zero=default."); +MODULE_PARM_SYNTAX(extout, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); +MODULE_PARM(seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(seq_ports, "Allocated sequencer ports for internal synthesizer."); +MODULE_PARM_SYNTAX(seq_ports, SNDRV_ENABLED "allows:{{0,32}}"); +MODULE_PARM(max_synth_voices, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(max_synth_voices, "Maximum number of voices for WaveTable."); +MODULE_PARM_SYNTAX(max_synth_voices, SNDRV_ENABLED); +MODULE_PARM(max_buffer_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB."); +MODULE_PARM_SYNTAX(max_buffer_size, SNDRV_ENABLED); +MODULE_PARM(enable_ir, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable_ir, "Enable IR."); +MODULE_PARM_SYNTAX(enable_ir, SNDRV_ENABLE_DESC); + +static struct pci_device_id snd_emu10k1_ids[] __devinitdata = { + { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ + { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */ + { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids); + +static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + emu10k1_t *emu; +#ifdef ENABLE_SYNTH + snd_seq_device_t *wave = NULL; +#endif + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if (max_buffer_size[dev] < 32) + max_buffer_size[dev] = 32; + else if (max_buffer_size[dev] > 1024) + max_buffer_size[dev] = 1024; + if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev], + (long)max_buffer_size[dev] * 1024 * 1024, + enable_ir[dev], + &emu)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_fx8010_pcm(emu, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (!emu->APS) { /* APS board has not an AC97 mixer */ + if ((err = snd_emu10k1_mixer(emu)) < 0) { + snd_card_free(card); + return err; + } + } + if (emu->audigy) { + if ((err = snd_emu10k1_audigy_midi(emu)) < 0) { + snd_card_free(card); + return err; + } + } else { + if ((err = snd_emu10k1_midi(emu)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef ENABLE_SYNTH + if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + sizeof(snd_emu10k1_synth_arg_t), &wave) < 0 || + wave == NULL) { + snd_printk("can't initialize Emu10k1 wavetable synth\n"); + } else { + snd_emu10k1_synth_arg_t *arg; + arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); + strcpy(wave->name, "Emu-10k1 Synth"); + arg->hwptr = emu; + arg->index = 1; + arg->seq_ports = seq_ports[dev]; + arg->max_voices = max_synth_voices[dev]; + } +#endif + + if (emu->audigy) { + strcpy(card->driver, "Audigy"); + strcpy(card->shortname, "Sound Blaster Audigy"); + } else if (emu->APS) { + strcpy(card->driver, "E-mu APS"); + strcpy(card->shortname, "E-mu APS"); + } else { + strcpy(card->driver, "EMU10K1"); + strcpy(card->shortname, "Sound Blaster Live!"); + } + sprintf(card->longname, "%s (rev.%d) at 0x%lx, irq %i", card->shortname, emu->revision, emu->port, emu->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "EMU10K1_Audigy", + .id_table = snd_emu10k1_ids, + .probe = snd_card_emu10k1_probe, + .remove = __devexit_p(snd_card_emu10k1_remove), +}; + +static int __init alsa_card_emu10k1_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "EMU10K1/Audigy soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_emu10k1_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_emu10k1_init) +module_exit(alsa_card_emu10k1_exit) + +#ifndef MODULE + +/* format is: snd-emu10k1=enable,index,id, + seq_ports,max_synth_voices */ + +static int __init alsa_card_emu10k1_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&seq_ports[nr_dev]) == 2 && + get_option(&str,&max_synth_voices[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-emu10k1=", alsa_card_emu10k1_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_callback.c linux/sound/pci/emu10k1/emu10k1_callback.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_callback.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emu10k1_callback.c 2002-08-15 06:13:09.000000000 -0600 @@ -0,0 +1,540 @@ +/* + * synth callback routines for Emu10k1 + * + * Copyright (C) 2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1_synth_local.h" +#include + +/* voice status */ +enum { + V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END +}; + +/* Keeps track of what we are finding */ +typedef struct best_voice { + unsigned int time; + int voice; +} best_voice_t; + +/* + * prototypes + */ +static void lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only); +static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); +static int start_voice(snd_emux_voice_t *vp); +static void trigger_voice(snd_emux_voice_t *vp); +static void release_voice(snd_emux_voice_t *vp); +static void update_voice(snd_emux_voice_t *vp, int update); +static void terminate_voice(snd_emux_voice_t *vp); +static void free_voice(snd_emux_voice_t *vp); + +static void set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp); +static void set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp); +static void set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp); + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + + +/* + * set up operators + */ +static snd_emux_operators_t emu10k1_ops = { + .owner = THIS_MODULE, + .get_voice = get_voice, + .prepare = start_voice, + .trigger = trigger_voice, + .release = release_voice, + .update = update_voice, + .terminate = terminate_voice, + .free_voice = free_voice, + .sample_new = snd_emu10k1_sample_new, + .sample_free = snd_emu10k1_sample_free, +}; + +void +snd_emu10k1_ops_setup(snd_emux_t *emu) +{ + emu->ops = emu10k1_ops; +} + + +/* + * get more voice for pcm + * + * terminate most inactive voice and give it as a pcm voice. + */ +int +snd_emu10k1_synth_get_voice(emu10k1_t *hw) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + best_voice_t best[V_END]; + unsigned long flags; + int i; + + emu = snd_magic_cast(snd_emux_t, hw->synth, return -EINVAL); + + spin_lock_irqsave(&emu->voice_lock, flags); + lookup_voices(emu, hw, best, 1); /* no OFF voices */ + for (i = 0; i < V_END; i++) { + if (best[i].voice >= 0) { + int ch; + vp = &emu->voices[best[i].voice]; + if ((ch = vp->ch) < 0) { + //printk("synth_get_voice: ch < 0 (%d) ??", i); + continue; + } + vp->emu->num_voices--; + vp->ch = -1; + vp->state = SNDRV_EMUX_ST_OFF; + spin_unlock_irqrestore(&emu->voice_lock, flags); + return ch; + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + /* not found */ + return -ENOMEM; +} + + +/* + * turn off the voice (not terminated) + */ +static void +release_voice(snd_emux_voice_t *vp) +{ + int dcysusv; + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; + snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK; + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv); +} + + +/* + * terminate the voice + */ +static void +terminate_voice(snd_emux_voice_t *vp) +{ + emu10k1_t *hw; + + snd_assert(vp, return); + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); + if (vp->block) { + emu10k1_memblk_t *emem; + emem = (emu10k1_memblk_t *)vp->block; + if (emem->map_locked > 0) + emem->map_locked--; + } +} + +/* + * release the voice to system + */ +static void +free_voice(snd_emux_voice_t *vp) +{ + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + if (vp->ch >= 0) { + snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); + // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); + snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff); + snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff); + snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]); + vp->emu->num_voices--; + vp->ch = -1; + } +} + + +/* + * update registers + */ +static void +update_voice(snd_emux_voice_t *vp, int update) +{ + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + if (update & SNDRV_EMUX_UPDATE_VOLUME) + snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); + if (update & SNDRV_EMUX_UPDATE_PITCH) + snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); + if (update & SNDRV_EMUX_UPDATE_PAN) { + snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan); + snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux); + } + if (update & SNDRV_EMUX_UPDATE_FMMOD) + set_fmmod(hw, vp); + if (update & SNDRV_EMUX_UPDATE_TREMFREQ) + snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); + if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) + set_fm2frq2(hw, vp); + if (update & SNDRV_EMUX_UPDATE_Q) + set_filterQ(hw, vp); +} + + +/* + * look up voice table - get the best voice in order of preference + */ +/* spinlock held! */ +static void +lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only) +{ + snd_emux_voice_t *vp; + best_voice_t *bp; + int i; + + for (i = 0; i < V_END; i++) { + best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* + * Go through them all and get a best one to use. + * NOTE: could also look at volume and pick the quietest one. + */ + for (i = 0; i < emu->max_voices; i++) { + int state, val; + + vp = &emu->voices[i]; + state = vp->state; + if (state == SNDRV_EMUX_ST_OFF) { + if (vp->ch < 0) { + if (active_only) + continue; + bp = best + V_FREE; + } else + bp = best + V_OFF; + } + else if (state == SNDRV_EMUX_ST_RELEASED || + state == SNDRV_EMUX_ST_PENDING) { + bp = best + V_RELEASED; +#if 0 + val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch); + if (! val) + bp = best + V_OFF; +#endif + } + else if (state == SNDRV_EMUX_ST_STANDBY) + continue; + else if (state & SNDRV_EMUX_ST_ON) + bp = best + V_PLAYING; + else + continue; + + /* check if sample is finished playing (non-looping only) */ + if (bp != best + V_OFF && bp != best + V_FREE && + (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { + val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); + if (val >= vp->reg.loopstart) + bp = best + V_OFF; + } + + if (vp->time < bp->time) { + bp->time = vp->time; + bp->voice = i; + } + } +} + +/* + * get an empty voice + * + * emu->voice_lock is already held. + */ +static snd_emux_voice_t * +get_voice(snd_emux_t *emu, snd_emux_port_t *port) +{ + emu10k1_t *hw; + snd_emux_voice_t *vp; + best_voice_t best[V_END]; + int i; + + hw = snd_magic_cast(emu10k1_t, emu->hw, return NULL); + + lookup_voices(emu, hw, best, 0); + for (i = 0; i < V_END; i++) { + if (best[i].voice >= 0) { + vp = &emu->voices[best[i].voice]; + if (vp->ch < 0) { + /* allocate a voice */ + emu10k1_voice_t *hwvoice; + if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 0, &hwvoice) < 0 || hwvoice == NULL) + continue; + vp->ch = hwvoice->number; + emu->num_voices++; + } + return vp; + } + } + + /* not found */ + return NULL; +} + +/* + * prepare envelopes and LFOs + */ +static int +start_voice(snd_emux_voice_t *vp) +{ + unsigned int temp; + int ch; + unsigned int addr, mapped_offset; + snd_midi_channel_t *chan; + emu10k1_t *hw; + emu10k1_memblk_t *emem; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return -EINVAL); + ch = vp->ch; + snd_assert(ch >= 0, return -EINVAL); + chan = vp->chan; + + emem = (emu10k1_memblk_t *)vp->block; + if (emem == NULL) + return -EINVAL; + emem->map_locked++; + if (snd_emu10k1_memblk_map(hw, emem) < 0) { + // printk("emu: cannot map!\n"); + return -ENOMEM; + } + mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; + vp->reg.start += mapped_offset; + vp->reg.end += mapped_offset; + vp->reg.loopstart += mapped_offset; + vp->reg.loopend += mapped_offset; + + /* set channel routing */ + /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ + if (hw->audigy) { + temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | + (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); + snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp); + } else { + temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | + (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); + snd_emu10k1_ptr_write(hw, FXRT, ch, temp); + } + + /* channel to be silent and idle */ + snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0080); + snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF); + snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF); + snd_emu10k1_ptr_write(hw, PTRX, ch, 0); + snd_emu10k1_ptr_write(hw, CPF, ch, 0); + + /* set pitch offset */ + snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); + + /* set envelope parameters */ + snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay); + snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld); + snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus); + snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay); + snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + + /* cutoff and volume */ + temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol; + snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp); + + /* modulation envelope heights */ + snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe); + + /* lfo1/2 delay */ + snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay); + snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay); + + /* lfo1 pitch & cutoff shift */ + set_fmmod(hw, vp); + /* lfo1 volume & freq */ + snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); + /* lfo2 pitch & freq */ + set_fm2frq2(hw, vp); + + /* reverb and loop start (reverb 8bit, MSB) */ + temp = vp->reg.parm.reverb; + temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + addr = vp->reg.loopstart; + snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->reg.loopend; + temp = vp->reg.parm.chorus; + temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp <<24) | addr; + snd_emu10k1_ptr_write(hw, DSL, ch, temp); + + /* clear filter delay memory */ + snd_emu10k1_ptr_write(hw, Z1, ch, 0); + snd_emu10k1_ptr_write(hw, Z2, ch, 0); + + /* invalidate maps */ + temp = (hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(hw, MAPA, ch, temp); + snd_emu10k1_ptr_write(hw, MAPB, ch, temp); +#if 0 + /* cache */ + { + unsigned int val, sample; + val = 32; + if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + sample = 0x80808080; + else { + sample = 0; + val *= 2; + } + + /* cache */ + snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16); + snd_emu10k1_ptr_write(hw, CDE, ch, sample); + snd_emu10k1_ptr_write(hw, CDF, ch, sample); + + /* invalidate maps */ + temp = ((unsigned int)hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(hw, MAPA, ch, temp); + snd_emu10k1_ptr_write(hw, MAPB, ch, temp); + + /* fill cache */ + val -= 4; + val <<= 25; + val |= 0x1c << 16; + snd_emu10k1_ptr_write(hw, CCR, ch, val); + } +#endif + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->reg.start; + temp = vp->reg.parm.filterQ; + temp = (temp<<28) | addr; + if (vp->apitch < 0xe400) + temp |= CCCA_INTERPROM_0; + else { + unsigned int shift = (vp->apitch - 0xe000) >> 10; + temp |= shift << 25; + } + if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + temp |= CCCA_8BITSELECT; + snd_emu10k1_ptr_write(hw, CCCA, ch, temp); + + /* reset volume */ + temp = (unsigned int)vp->vtarget << 16; + snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget); + snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00); + return 0; +} + +/* + * Start envelope + */ +static void +trigger_voice(snd_emux_voice_t *vp) +{ + unsigned int temp, ptarget; + emu10k1_t *hw; + emu10k1_memblk_t *emem; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + + emem = (emu10k1_memblk_t *)vp->block; + if (! emem || emem->mapped_page < 0) + return; /* not mapped */ + +#if 0 + ptarget = (unsigned int)vp->ptarget << 16; +#else + ptarget = IP_TO_CP(vp->apitch); +#endif + /* set pitch target and pan (volume) */ + temp = ptarget | (vp->apan << 8) | vp->aaux; + snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp); + + /* pitch target */ + snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget); + + /* trigger voice */ + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK); +} + +#define MOD_SENSE 18 + +/* set lfo1 modulation height and cutoff */ +static void +set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fmmod; + short pitch; + unsigned char cutoff; + int modulation; + + pitch = (char)(vp->reg.parm.fmmod>>8); + cutoff = (vp->reg.parm.fmmod & 0xff); + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fmmod = ((unsigned char)pitch<<8) | cutoff; + snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod); +} + +/* set lfo2 pitch & frequency */ +static void +set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fm2frq2; + short pitch; + unsigned char freq; + int modulation; + + pitch = (char)(vp->reg.parm.fm2frq2>>8); + freq = vp->reg.parm.fm2frq2 & 0xff; + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fm2frq2 = ((unsigned char)pitch<<8) | freq; + snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2); +} + +/* set filterQ */ +static void +set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned int val; + val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE; + val |= (vp->reg.parm.filterQ << 28); + snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val); +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_main.c linux/sound/pci/emu10k1/emu10k1_main.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_main.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emu10k1_main.c 2003-03-11 08:25:33.000000000 -0700 @@ -0,0 +1,720 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if 0 +MODULE_AUTHOR("Jaroslav Kysela , Creative Labs, Inc."); +MODULE_DESCRIPTION("Routines for control of EMU10K1 chips"); +MODULE_LICENSE("GPL"); +#endif + +/************************************************************************* + * EMU10K1 init / done + *************************************************************************/ + +void snd_emu10k1_voice_init(emu10k1_t * emu, int ch) +{ + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + snd_emu10k1_ptr_write(emu, IP, ch, 0); + snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + snd_emu10k1_ptr_write(emu, CCR, ch, 0); + + snd_emu10k1_ptr_write(emu, PSST, ch, 0); + snd_emu10k1_ptr_write(emu, DSL, ch, 0x10); + snd_emu10k1_ptr_write(emu, CCCA, ch, 0); + snd_emu10k1_ptr_write(emu, Z1, ch, 0); + snd_emu10k1_ptr_write(emu, Z2, ch, 0); + snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000); + + snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0); + snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PEFE, ch, 0); + snd_emu10k1_ptr_write(emu, FMMOD, ch, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0); + + /*** these are last so OFF prevents writing ***/ + snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0); + snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0); + snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0); + + /* Audigy extra stuffs */ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100); + snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0); + } +} + +static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) +{ + int ch, idx, err; + unsigned int silent_page; + + emu->fx8010.itram_size = (16 * 1024)/2; + emu->fx8010.etram_size = 0; + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + + /* disable channel interrupt */ + outl(0, emu->port + INTE); + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + if (emu->audigy){ + snd_emu10k1_ptr_write(emu, 0x5e, 0, 0xf00); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x5f, 0, 0x3); /* ?? */ + } + + /* init envelope engine */ + for (ch = 0; ch < NUM_G; ch++) { + emu->voices[ch].emu = emu; + emu->voices[ch].number = ch; + snd_emu10k1_voice_init(emu, ch); + } + + /* + * Init to 0x02109204 : + * Clock accuracy = 0 (1000ppm) + * Sample Rate = 2 (48kHz) + * Audio Channel = 1 (Left of 2) + * Source Number = 0 (Unspecified) + * Generation Status = 1 (Original for Cat Code 12) + * Cat Code = 12 (Digital Signal Mixer) + * Mode = 0 (Mode 0) + * Emphasis = 0 (None) + * CP = 1 (Copyright unasserted) + * AN = 0 (Audio data) + * P = 0 (Consumer) + */ + snd_emu10k1_ptr_write(emu, SPCS0, 0, + emu->spdif_bits[0] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_emu10k1_ptr_write(emu, SPCS1, 0, + emu->spdif_bits[1] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_emu10k1_ptr_write(emu, SPCS2, 0, + emu->spdif_bits[2] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + + if (emu->audigy && emu->revision == 4) { /* audigy2 */ + /* Hacks for Alice3 to work independent of haP16V driver */ + u32 tmp; + + //Setup SRCMulti_I2S SamplingRate + tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); + tmp &= 0xfffff1ff; + tmp |= (0x2<<9); + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); + + /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ + outl(0x600000, emu->port + 0x20); + outl(0x14, emu->port + 0x24); + + /* Setup SRCMulti Input Audio Enable */ + outl(0x6E0000, emu->port + 0x20); + outl(0xFF00FF00, emu->port + 0x24); + } + + /* + * Clear page with silence & setup all pointers to this page + */ + memset(emu->silent_page, 0, PAGE_SIZE); + silent_page = emu->silent_page_dmaaddr << 1; + for (idx = 0; idx < MAXPAGES; idx++) + emu->ptb_pages[idx] = cpu_to_le32(silent_page | idx); + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ + snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ + + silent_page = (emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); + } + + /* + * Hokay, setup HCFG + * Mute Disable Audio = 0 + * Lock Tank Memory = 1 + * Lock Sound Memory = 0 + * Auto Mute = 1 + */ + if (emu->audigy) { + if (emu->revision == 4) /* audigy2 */ + outl(HCFG_AUDIOENABLE | + HCFG_AC3ENABLE_CDSPDIF | + HCFG_AC3ENABLE_GPSPDIF | + HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + else + outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + } else if (emu->model == 0x20 || + emu->model == 0xc400 || + (emu->model == 0x21 && emu->revision < 6)) + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG); + else + // With on-chip joystick + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + + if (enable_ir) { /* enable IR for SB Live */ + if (emu->audigy) { + unsigned int reg = inl(emu->port + A_IOCFG); + outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); + udelay(500); + outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG); + udelay(100); + outl(reg, emu->port + A_IOCFG); + } else { + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT2, emu->port + HCFG); + udelay(500); + outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG); + udelay(100); + outl(reg, emu->port + HCFG); + } + } + + if (!emu->APS) { /* enable analog output */ + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT0, emu->port + HCFG); + } + + /* + * Initialize the effect engine + */ + if ((err = snd_emu10k1_init_efx(emu)) < 0) + return err; + + /* + * Enable the audio bit + */ + outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); + + /* Enable analog/digital outs on audigy */ + if (emu->audigy) { + outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); + + if (emu->revision == 4) { /* audigy2 */ + /* Unmute Analog now. Set GPO6 to 1 for Apollo. + * This has to be done after init ALice3 I2SOut beyond 48KHz. + * So, sequence is important. */ + outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); + } + } + +#if 0 + { + unsigned int tmp; + /* FIXME: the following routine disables LiveDrive-II !! */ + // TOSLink detection + emu->tos_link = 0; + tmp = inl(emu->port + HCFG); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + outl(tmp|0x800, emu->port + HCFG); + udelay(50); + if (tmp != (inl(emu->port + HCFG) & ~0x800)) { + emu->tos_link = 1; + outl(tmp, emu->port + HCFG); + } + } + } +#endif + + snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE); + + emu->reserved_page = (emu10k1_memblk_t *)snd_emu10k1_synth_alloc(emu, 4096); + if (emu->reserved_page) + emu->reserved_page->map_locked = 1; + + return 0; +} + +static int snd_emu10k1_done(emu10k1_t * emu) +{ + int ch; + + outl(0, emu->port + INTE); + + /* + * Shutdown the chip + */ + for (ch = 0; ch < NUM_G; ch++) + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, VTFT, ch, 0); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + } + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, 0); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, 0); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, 0x8000); + + /* disable channel interrupt */ + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + /* remove reserved page */ + if (emu->reserved_page != NULL) { + snd_emu10k1_synth_free(emu, (snd_util_memblk_t *)emu->reserved_page); + emu->reserved_page = NULL; + } + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + snd_emu10k1_ptr_write(emu, PTB, 0, 0); + + snd_emu10k1_free_efx(emu); + + return 0; +} + +/************************************************************************* + * ECARD functional implementation + *************************************************************************/ + +/* In A1 Silicon, these bits are in the HC register */ +#define HOOKN_BIT (1L << 12) +#define HANDN_BIT (1L << 11) +#define PULSEN_BIT (1L << 10) + +#define EC_GDI1 (1 << 13) +#define EC_GDI0 (1 << 14) + +#define EC_NUM_CONTROL_BITS 20 + +#define EC_AC3_DATA_SELN 0x0001L +#define EC_EE_DATA_SEL 0x0002L +#define EC_EE_CNTRL_SELN 0x0004L +#define EC_EECLK 0x0008L +#define EC_EECS 0x0010L +#define EC_EESDO 0x0020L +#define EC_TRIM_CSN 0x0040L +#define EC_TRIM_SCLK 0x0080L +#define EC_TRIM_SDATA 0x0100L +#define EC_TRIM_MUTEN 0x0200L +#define EC_ADCCAL 0x0400L +#define EC_ADCRSTN 0x0800L +#define EC_DACCAL 0x1000L +#define EC_DACMUTEN 0x2000L +#define EC_LEDN 0x4000L + +#define EC_SPDIF0_SEL_SHIFT 15 +#define EC_SPDIF1_SEL_SHIFT 17 +#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) +#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) +#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) +#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) +#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should + * be incremented any time the EEPROM's + * format is changed. */ + +#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ + +/* Addresses for special values stored in to EEPROM */ +#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ +#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ +#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ + +#define EC_LAST_PROMFILE_ADDR 0x2f + +#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The + * can be up to 30 characters in length + * and is stored as a NULL-terminated + * ASCII string. Any unused bytes must be + * filled with zeros */ +#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ + + +/* Most of this stuff is pretty self-evident. According to the hardware + * dudes, we need to leave the ADCCAL bit low in order to avoid a DC + * offset problem. Weird. + */ +#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \ + EC_TRIM_CSN) + + +#define EC_DEFAULT_ADC_GAIN 0xC4C4 +#define EC_DEFAULT_SPDIF0_SEL 0x0 +#define EC_DEFAULT_SPDIF1_SEL 0x4 + +/************************************************************************** + * @func Clock bits into the Ecard's control latch. The Ecard uses a + * control latch will is loaded bit-serially by toggling the Modem control + * lines from function 2 on the E8010. This function hides these details + * and presents the illusion that we are actually writing to a distinct + * register. + */ + +static void snd_emu10k1_ecard_write(emu10k1_t * emu, unsigned int value) +{ + unsigned short count; + unsigned int data; + unsigned long hc_port; + unsigned int hc_value; + + hc_port = emu->port + HCFG; + hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT); + outl(hc_value, hc_port); + + for (count = 0; count < EC_NUM_CONTROL_BITS; count++) { + + /* Set up the value */ + data = ((value & 0x1) ? PULSEN_BIT : 0); + value >>= 1; + + outl(hc_value | data, hc_port); + + /* Clock the shift register */ + outl(hc_value | data | HANDN_BIT, hc_port); + outl(hc_value | data, hc_port); + } + + /* Latch the bits */ + outl(hc_value | HOOKN_BIT, hc_port); + outl(hc_value, hc_port); +} + +/************************************************************************** + * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The + * trim value consists of a 16bit value which is composed of two + * 8 bit gain/trim values, one for the left channel and one for the + * right channel. The following table maps from the Gain/Attenuation + * value in decibels into the corresponding bit pattern for a single + * channel. + */ + +static void snd_emu10k1_ecard_setadcgain(emu10k1_t * emu, + unsigned short gain) +{ + unsigned int bit; + + /* Enable writing to the TRIM registers */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + /* Do it again to insure that we meet hold time requirements */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + for (bit = (1 << 15); bit; bit >>= 1) { + unsigned int value; + + value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA); + + if (gain & bit) + value |= EC_TRIM_SDATA; + + /* Clock the bit */ + snd_emu10k1_ecard_write(emu, value); + snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK); + snd_emu10k1_ecard_write(emu, value); + } + + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); +} + +static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu) +{ + unsigned int hc_value; + + /* Set up the initial settings */ + emu->ecard_ctrl = EC_RAW_RUN_MODE | + EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) | + EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL); + + /* Step 0: Set the codec type in the hardware control register + * and enable audio output */ + hc_value = inl(emu->port + HCFG); + outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG); + inl(emu->port + HCFG); + + /* Step 1: Turn off the led and deassert TRIM_CS */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 2: Calibrate the ADC and DAC */ + snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 3: Wait for awhile; XXX We can't get away with this + * under a real operating system; we'll need to block and wait that + * way. */ + snd_emu10k1_wait(emu, 48000); + + /* Step 4: Switch off the DAC and ADC calibration. Note + * That ADC_CAL is actually an inverted signal, so we assert + * it here to stop calibration. */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 4: Switch into run mode */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); + + /* Step 5: Set the analog input gain */ + snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN); + + return 0; +} + +/* + * Create the EMU10K1 instance + */ + +static int snd_emu10k1_free(emu10k1_t *emu) +{ + if (emu->res_port != NULL) { /* avoid access to already used hardware */ + snd_emu10k1_fx8010_tram_setup(emu, 0); + snd_emu10k1_done(emu); + } + if (emu->memhdr) + snd_util_memhdr_free(emu->memhdr); + if (emu->silent_page) + snd_free_pci_pages(emu->pci, EMUPAGESIZE, emu->silent_page, emu->silent_page_dmaaddr); + if (emu->ptb_pages) + snd_free_pci_pages(emu->pci, 32 * 1024, (void *)emu->ptb_pages, emu->ptb_pages_dmaaddr); + if (emu->page_ptr_table) + vfree(emu->page_ptr_table); + if (emu->page_addr_table) + vfree(emu->page_addr_table); + if (emu->res_port) { + release_resource(emu->res_port); + kfree_nocheck(emu->res_port); + } + if (emu->irq >= 0) + free_irq(emu->irq, (void *)emu); + snd_magic_kfree(emu); + return 0; +} + +static int snd_emu10k1_dev_free(snd_device_t *device) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, device->device_data, return -ENXIO); + return snd_emu10k1_free(emu); +} + +int __devinit snd_emu10k1_create(snd_card_t * card, + struct pci_dev * pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu) +{ + emu10k1_t *emu; + int err; + int is_audigy; + static snd_device_ops_t ops = { + .dev_free = snd_emu10k1_dev_free, + }; + + *remu = NULL; + + // is_audigy = (int)pci->driver_data; + is_audigy = (pci->device == 0x0004); + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + emu = snd_magic_kcalloc(emu10k1_t, 0, GFP_KERNEL); + if (emu == NULL) + return -ENOMEM; + /* set the DMA transfer mask */ + emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK; + if (pci_set_dma_mask(pci, emu->dma_mask) < 0) { + snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask); + snd_magic_kfree(emu); + return -ENXIO; + } + emu->card = card; + spin_lock_init(&emu->reg_lock); + spin_lock_init(&emu->emu_lock); + spin_lock_init(&emu->voice_lock); + spin_lock_init(&emu->synth_lock); + spin_lock_init(&emu->memblk_lock); + init_MUTEX(&emu->ptb_lock); + init_MUTEX(&emu->fx8010.lock); + INIT_LIST_HEAD(&emu->mapped_link_head); + INIT_LIST_HEAD(&emu->mapped_order_link_head); + emu->pci = pci; + emu->irq = -1; + emu->synth = NULL; + emu->get_synth_voice = NULL; + emu->port = pci_resource_start(pci, 0); + + emu->audigy = is_audigy; + if (is_audigy) + emu->gpr_base = A_FXGPREGBASE; + else + emu->gpr_base = FXGPREGBASE; + + if ((emu->res_port = request_region(emu->port, 0x20, "EMU10K1")) == NULL) { + snd_emu10k1_free(emu); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) { + snd_emu10k1_free(emu); + return -EBUSY; + } + emu->irq = pci->irq; + + emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; + emu->ptb_pages = snd_malloc_pci_pages(pci, 32 * 1024, &emu->ptb_pages_dmaaddr); + if (emu->ptb_pages == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + + emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*)); + emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long)); + if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + + emu->silent_page = snd_malloc_pci_pages(pci, EMUPAGESIZE, &emu->silent_page_dmaaddr); + if (emu->silent_page == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE); + if (emu->memhdr == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + emu->memhdr->block_extra_size = sizeof(emu10k1_memblk_t) - sizeof(snd_util_memblk_t); + + pci_set_master(pci); + /* read revision & serial */ + pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&emu->revision); + pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model); + emu->card_type = EMU10K1_CARD_CREATIVE; + if (emu->serial == 0x40011102) { + emu->card_type = EMU10K1_CARD_EMUAPS; + emu->APS = 1; + } + + emu->fx8010.fxbus_mask = 0x303f; + if (extin_mask == 0) + extin_mask = 0x3fcf; + if (extout_mask == 0) + extout_mask = 0x1fff; + emu->fx8010.extin_mask = extin_mask; + emu->fx8010.extout_mask = extout_mask; + + if (emu->APS) { + if ((err = snd_emu10k1_ecard_init(emu)) < 0) { + snd_emu10k1_free(emu); + return err; + } + } else { + /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version + does not support this, it shouldn't do any harm */ + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); + } + + if ((err = snd_emu10k1_init(emu, enable_ir)) < 0) { + snd_emu10k1_free(emu); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0) { + snd_emu10k1_free(emu); + return err; + } + + snd_emu10k1_proc_init(emu); + + *remu = emu; + return 0; +} + +/* memory.c */ +EXPORT_SYMBOL(snd_emu10k1_synth_alloc); +EXPORT_SYMBOL(snd_emu10k1_synth_free); +EXPORT_SYMBOL(snd_emu10k1_synth_bzero); +EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); +EXPORT_SYMBOL(snd_emu10k1_memblk_map); +/* voice.c */ +EXPORT_SYMBOL(snd_emu10k1_voice_alloc); +EXPORT_SYMBOL(snd_emu10k1_voice_free); +/* io.c */ +EXPORT_SYMBOL(snd_emu10k1_ptr_read); +EXPORT_SYMBOL(snd_emu10k1_ptr_write); diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_patch.c linux/sound/pci/emu10k1/emu10k1_patch.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_patch.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emu10k1_patch.c 2002-08-12 02:43:47.000000000 -0600 @@ -0,0 +1,223 @@ +/* + * Patch transfer callback for Emu10k1 + * + * Copyright (C) 2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * All the code for loading in a patch. There is very little that is + * chip specific here. Just the actual writing to the board. + */ + +#include "emu10k1_synth_local.h" + +/* + */ +#define BLANK_LOOP_START 4 +#define BLANK_LOOP_END 8 +#define BLANK_LOOP_SIZE 12 +#define BLANK_HEAD_SIZE 32 + +/* + * allocate a sample block and copy data from userspace + */ +int +snd_emu10k1_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *data, long count) +{ + int offset; + int truesize, size, loopsize, blocksize; + int loopend, sampleend; + unsigned int start_addr; + emu10k1_t *emu; + + emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + snd_assert(sp != NULL, return -EINVAL); + snd_assert(hdr != NULL, return -EINVAL); + + if (sp->v.size == 0) { + snd_printd("emu: rom font for sample %d\n", sp->v.sample); + return 0; + } + + /* recalculate address offset */ + sp->v.end -= sp->v.start; + sp->v.loopstart -= sp->v.start; + sp->v.loopend -= sp->v.start; + sp->v.start = 0; + + /* some samples have invalid data. the addresses are corrected in voice info */ + sampleend = sp->v.end; + if (sampleend > sp->v.size) + sampleend = sp->v.size; + loopend = sp->v.loopend; + if (loopend > sampleend) + loopend = sampleend; + + /* be sure loop points start < end */ + if (sp->v.loopstart >= sp->v.loopend) { + int tmp = sp->v.loopstart; + sp->v.loopstart = sp->v.loopend; + sp->v.loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->v.size + BLANK_HEAD_SIZE; + loopsize = 0; +#if 0 /* not supported */ + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) + loopsize = sp->v.loopend - sp->v.loopstart; + truesize += loopsize; +#endif + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + + /* try to allocate a memory block */ + blocksize = truesize; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + blocksize *= 2; + sp->block = snd_emu10k1_synth_alloc(emu, blocksize); + if (sp->block == NULL) { + snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize); + /* not ENOMEM (for compatibility with OSS) */ + return -ENOSPC; + } + /* set the total size */ + sp->v.truesize = blocksize; + + /* write blank samples at head */ + offset = 0; + size = BLANK_HEAD_SIZE; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + snd_assert(offset + size <= blocksize, return -EINVAL); + snd_emu10k1_synth_bzero(emu, sp->block, offset, size); + offset += size; + + /* copy start->loopend */ + size = loopend; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + snd_assert(offset + size <= blocksize, return -EINVAL); + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; + } + offset += size; + data += size; + +#if 0 /* not suppported yet */ + /* handle reverse (or bidirectional) loop */ + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* copy loop in reverse */ + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { + int woffset; + unsigned short *wblock = (unsigned short*)block; + woffset = offset / 2; + snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL); + for (i = 0; i < loopsize; i++) + wblock[woffset + i] = wblock[woffset - i -1]; + offset += loopsize * 2; + } else { + snd_assert(offset + loopsize <= blocksize, return -EINVAL); + for (i = 0; i < loopsize; i++) + block[offset + i] = block[offset - i -1]; + offset += loopsize; + } + + /* modify loop pointers */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { + sp->v.loopend += loopsize; + } else { + sp->v.loopstart += loopsize; + sp->v.loopend += loopsize; + } + /* add sample pointer */ + sp->v.end += loopsize; + } +#endif + + /* loopend -> sample end */ + size = sp->v.size - loopend; + snd_assert(size >= 0, return -EINVAL); + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; + } + offset += size; + + /* clear rest of samples (if any) */ + if (offset < blocksize) + snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); + + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + +#if 0 /* not supported yet */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { + /* unsigned -> signed */ + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { + unsigned short *wblock = (unsigned short*)block; + for (i = 0; i < truesize; i++) + wblock[i] ^= 0x8000; + } else { + for (i = 0; i < truesize; i++) + block[i] ^= 0x80; + } + } +#endif + + /* recalculate offset */ + start_addr = BLANK_HEAD_SIZE * 2; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + start_addr >>= 1; + sp->v.start += start_addr; + sp->v.end += start_addr; + sp->v.loopstart += start_addr; + sp->v.loopend += start_addr; + + return 0; +} + +/* + * free a sample block + */ +int +snd_emu10k1_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr) +{ + emu10k1_t *emu; + + emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + snd_assert(sp != NULL, return -EINVAL); + snd_assert(hdr != NULL, return -EINVAL); + + if (sp->block) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + } + return 0; +} + diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_synth.c linux/sound/pci/emu10k1/emu10k1_synth.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emu10k1_synth.c 2002-05-25 04:26:11.000000000 -0600 @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Routines for control of EMU10K1 WaveTable synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1_synth_local.h" +#include + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth"); +MODULE_LICENSE("GPL"); + +/* + * create a new hardware dependent device for Emu10k1 + */ +int snd_emu10k1_synth_new_device(snd_seq_device_t *dev) +{ + snd_emux_t *emu; + emu10k1_t *hw; + snd_emu10k1_synth_arg_t *arg; + unsigned long flags; + + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (arg == NULL) + return -EINVAL; + + if (arg->seq_ports <= 0) + return 0; /* nothing */ + if (arg->max_voices < 1) + arg->max_voices = 1; + else if (arg->max_voices > 64) + arg->max_voices = 64; + + if (snd_emux_new(&emu) < 0) + return -ENOMEM; + + snd_emu10k1_ops_setup(emu); + emu->hw = hw = arg->hwptr; + emu->max_voices = arg->max_voices; + emu->num_ports = arg->seq_ports; + emu->pitch_shift = -501; + emu->memhdr = hw->memhdr; + emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */ + emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */ + emu->linear_panning = 0; + + if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) { + snd_emux_free(emu); + emu->hw = NULL; + return -ENOMEM; + } + + spin_lock_irqsave(&hw->voice_lock, flags); + hw->synth = emu; + hw->get_synth_voice = snd_emu10k1_synth_get_voice; + spin_unlock_irqrestore(&hw->voice_lock, flags); + + dev->driver_data = emu; + + return 0; +} + +int snd_emu10k1_synth_delete_device(snd_seq_device_t *dev) +{ + snd_emux_t *emu; + emu10k1_t *hw; + unsigned long flags; + + if (dev->driver_data == NULL) + return 0; /* not registered actually */ + + emu = snd_magic_cast(snd_emux_t, dev->driver_data, return -EINVAL); + + hw = snd_magic_cast(emu10k1_t, emu->hw, return -EINVAL); + spin_lock_irqsave(&hw->voice_lock, flags); + hw->synth = NULL; + hw->get_synth_voice = NULL; + spin_unlock_irqrestore(&hw->voice_lock, flags); + + snd_emux_free(emu); + return 0; +} + +/* + * INIT part + */ + +static int __init alsa_emu10k1_synth_init(void) +{ + + static snd_seq_dev_ops_t ops = { + snd_emu10k1_synth_new_device, + snd_emu10k1_synth_delete_device, + }; + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, sizeof(snd_emu10k1_synth_arg_t)); +} + +static void __exit alsa_emu10k1_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); +} + +module_init(alsa_emu10k1_synth_init) +module_exit(alsa_emu10k1_synth_exit) diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_synth_local.h linux/sound/pci/emu10k1/emu10k1_synth_local.h --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emu10k1_synth_local.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emu10k1_synth_local.h 2002-02-14 10:40:33.000000000 -0700 @@ -0,0 +1,38 @@ +#ifndef __EMU10K1_SYNTH_LOCAL_H +#define __EMU10K1_SYNTH_LOCAL_H +/* + * Local defininitons for Emu10k1 wavetable + * + * Copyright (C) 2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +/* emu10k1_patch.c */ +int snd_emu10k1_sample_new(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *_data, long count); +int snd_emu10k1_sample_free(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); +int snd_emu10k1_memhdr_init(snd_emux_t *emu); + +/* emu10k1_callback.c */ +void snd_emu10k1_ops_setup(snd_emux_t *emu); +int snd_emu10k1_synth_get_voice(emu10k1_t *hw); + + +#endif /* __EMU10K1_SYNTH_LOCAL_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emufx.c linux/sound/pci/emu10k1/emufx.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emufx.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emufx.c 2003-02-17 05:08:22.000000000 -0700 @@ -0,0 +1,2384 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for effect processor FX8010 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +#if 0 /* for testing purposes - digital out -> capture */ +#define EMU10K1_CAPTURE_DIGITAL_OUT +#endif +#if 0 /* for testing purposes - set S/PDIF to AC3 output */ +#define EMU10K1_SET_AC3_IEC958 +#endif +#if 0 /* for testing purposes - feed the front signal to Center/LFE outputs */ +#define EMU10K1_CENTER_LFE_FROM_FRONT +#endif + +/* + * Tables + */ + +static char *fxbuses[16] = { + /* 0x00 */ "PCM Left", + /* 0x01 */ "PCM Right", + /* 0x02 */ "PCM Surround Left", + /* 0x03 */ "PCM Surround Right", + /* 0x04 */ "MIDI Left", + /* 0x05 */ "MIDI Right", + /* 0x06 */ "Center", + /* 0x07 */ "LFE", + /* 0x08 */ NULL, + /* 0x09 */ NULL, + /* 0x0a */ NULL, + /* 0x0b */ NULL, + /* 0x0c */ "MIDI Reverb", + /* 0x0d */ "MIDI Chorus", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *creative_ins[16] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "TTL IEC958 Left", + /* 0x03 */ "TTL IEC958 Right", + /* 0x04 */ "Zoom Video Left", + /* 0x05 */ "Zoom Video Right", + /* 0x06 */ "Optical IEC958 Left", + /* 0x07 */ "Optical IEC958 Right", + /* 0x08 */ "Line/Mic 1 Left", + /* 0x09 */ "Line/Mic 1 Right", + /* 0x0a */ "Coaxial IEC958 Left", + /* 0x0b */ "Coaxial IEC958 Right", + /* 0x0c */ "Line/Mic 2 Left", + /* 0x0d */ "Line/Mic 2 Right", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *audigy_ins[16] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "Audigy CD Left", + /* 0x03 */ "Audigy CD Right", + /* 0x04 */ "Optical IEC958 Left", + /* 0x05 */ "Optical IEC958 Right", + /* 0x06 */ NULL, + /* 0x07 */ NULL, + /* 0x08 */ "Line/Mic 2 Left", + /* 0x09 */ "Line/Mic 2 Right", + /* 0x0a */ "SPDIF Left", + /* 0x0b */ "SPDIF Right", + /* 0x0c */ "Aux2 Left", + /* 0x0d */ "Aux2 Right", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *creative_outs[32] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "Optical IEC958 Left", + /* 0x03 */ "Optical IEC958 Right", + /* 0x04 */ "Center", + /* 0x05 */ "LFE", + /* 0x06 */ "Headphone Left", + /* 0x07 */ "Headphone Right", + /* 0x08 */ "Surround Left", + /* 0x09 */ "Surround Right", + /* 0x0a */ "PCM Capture Left", + /* 0x0b */ "PCM Capture Right", + /* 0x0c */ "MIC Capture", + /* 0x0d */ NULL, + /* 0x0e */ NULL, + /* 0x0f */ NULL, + /* 0x10 */ NULL, + /* 0x11 */ "Analog Center", + /* 0x12 */ "Analog LFE", + /* 0x13 */ NULL, + /* 0x14 */ NULL, + /* 0x15 */ NULL, + /* 0x16 */ NULL, + /* 0x17 */ NULL, + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ NULL, + /* 0x1b */ NULL, + /* 0x1c */ NULL, + /* 0x1d */ NULL, + /* 0x1e */ NULL, + /* 0x1f */ NULL, +}; + +static char *audigy_outs[32] = { + /* 0x00 */ "Digital Front Left", + /* 0x01 */ "Digital Front Right", + /* 0x02 */ "Digital Center", + /* 0x03 */ "Digital LEF", + /* 0x04 */ "Headphone Left", + /* 0x05 */ "Headphone Right", + /* 0x06 */ "Digital Rear Left", + /* 0x07 */ "Digital Rear Right", + /* 0x08 */ "Front Left", + /* 0x09 */ "Front Right", + /* 0x0a */ "Center", + /* 0x0b */ "LFE", + /* 0x0c */ NULL, + /* 0x0d */ NULL, + /* 0x0e */ "Rear Left", + /* 0x0f */ "Rear Right", + /* 0x10 */ "AC97 Front Left", + /* 0x11 */ "AC97 Front Right", + /* 0x12 */ "ADC Caputre Left", + /* 0x13 */ "ADC Capture Right", + /* 0x14 */ NULL, + /* 0x15 */ NULL, + /* 0x16 */ NULL, + /* 0x17 */ NULL, + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ NULL, + /* 0x1b */ NULL, + /* 0x1c */ NULL, + /* 0x1d */ NULL, + /* 0x1e */ NULL, + /* 0x1f */ NULL, +}; + +static const u32 bass_table[41][5] = { + { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, + { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, + { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, + { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, + { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, + { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, + { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, + { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, + { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, + { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, + { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, + { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, + { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, + { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, + { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, + { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, + { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, + { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, + { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, + { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, + { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee }, + { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, + { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, + { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, + { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, + { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, + { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, + { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, + { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, + { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, + { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, + { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, + { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, + { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, + { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, + { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, + { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, + { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, + { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, + { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, + { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } +}; + +static const u32 treble_table[41][5] = { + { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, + { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, + { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, + { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, + { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, + { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, + { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, + { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, + { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, + { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, + { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, + { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, + { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, + { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, + { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, + { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, + { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, + { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, + { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, + { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, + { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, + { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, + { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, + { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, + { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, + { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, + { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, + { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, + { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, + { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, + { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, + { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, + { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, + { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, + { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, + { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, + { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, + { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, + { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, + { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, + { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } +}; + +static const u32 db_table[101] = { + 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540, + 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8, + 0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1, + 0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0, + 0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9, + 0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb, + 0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005, + 0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d, + 0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd, + 0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8, + 0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481, + 0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333, + 0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d, + 0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6, + 0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d, + 0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf, + 0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038, + 0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a, + 0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea, + 0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272, + 0x7fffffff, +}; + +static const u32 onoff_table[2] = { + 0x00000000, 0x00000001 +}; + +/* + */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* + * controls + */ + +static int snd_emu10k1_gpr_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + + if (ctl->min == 0 && ctl->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ctl->vcount; + uinfo->value.integer.min = ctl->min; + uinfo->value.integer.max = ctl->max; + return 0; +} + +static int snd_emu10k1_gpr_ctl_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (i = 0; i < ctl->vcount; i++) + ucontrol->value.integer.value[i] = ctl->value[i]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_gpr_ctl_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + unsigned long flags; + unsigned int nval, val; + unsigned int i, j; + int change = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (i = 0; i < ctl->vcount; i++) { + nval = ucontrol->value.integer.value[i]; + if (nval < ctl->min) + nval = ctl->min; + if (nval > ctl->max) + nval = ctl->max; + if (nval != ctl->value[i]) + change = 1; + val = ctl->value[i] = nval; + switch (ctl->translation) { + case EMU10K1_GPR_TRANSLATION_NONE: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val); + break; + case EMU10K1_GPR_TRANSLATION_TABLE100: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]); + break; + case EMU10K1_GRP_TRANSLATION_BASS: + snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); + for (j = 0; j < 5; j++) + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]); + break; + case EMU10K1_GRP_TRANSLATION_TREBLE: + snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); + for (j = 0; j < 5; j++) + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]); + break; + case EMU10K1_GPR_TRANSLATION_ONOFF: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]); + break; + } + } + __error: + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +/* + * Interrupt handler + */ + +static void snd_emu10k1_fx8010_interrupt(emu10k1_t *emu) +{ + snd_emu10k1_fx8010_irq_t *irq, *nirq; + + irq = emu->fx8010.irq_handlers; + while (irq) { + nirq = irq->next; /* irq ptr can be removed from list */ + if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) { + if (irq->handler) + irq->handler(emu, irq->private_data); + snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1); + } + irq = nirq; + } +} + +static int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, + snd_fx8010_irq_handler_t *handler, + unsigned char gpr_running, + void *private_data, + snd_emu10k1_fx8010_irq_t **r_irq) +{ + snd_emu10k1_fx8010_irq_t *irq; + unsigned long flags; + + snd_runtime_check(emu, return -EINVAL); + snd_runtime_check(handler, return -EINVAL); + irq = kmalloc(sizeof(*irq), GFP_ATOMIC); + if (irq == NULL) + return -ENOMEM; + irq->handler = handler; + irq->gpr_running = gpr_running; + irq->private_data = private_data; + irq->next = NULL; + spin_lock_irqsave(&emu->fx8010.irq_lock, flags); + if (emu->fx8010.irq_handlers == NULL) { + emu->fx8010.irq_handlers = irq; + emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt; + snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE); + } else { + irq->next = emu->fx8010.irq_handlers; + emu->fx8010.irq_handlers = irq; + } + spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); + if (r_irq) + *r_irq = irq; + return 0; +} + +static int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, + snd_emu10k1_fx8010_irq_t *irq) +{ + snd_emu10k1_fx8010_irq_t *tmp; + unsigned long flags; + + snd_runtime_check(irq, return -EINVAL); + spin_lock_irqsave(&emu->fx8010.irq_lock, flags); + if ((tmp = emu->fx8010.irq_handlers) == irq) { + emu->fx8010.irq_handlers = tmp->next; + if (emu->fx8010.irq_handlers == NULL) { + snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); + emu->dsp_interrupt = NULL; + } + } else { + while (tmp && tmp->next != irq) + tmp = tmp->next; + if (tmp) + tmp->next = tmp->next->next; + } + spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); + kfree(irq); + return 0; +} + +/* + * PCM streams + */ + +#define INITIAL_TRAM_SHIFT 14 +#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) + +static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data) +{ + snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, private_data, return); + snd_pcm_period_elapsed(substream); +} + +static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, + unsigned short *dst_right, + unsigned short *src, + unsigned int count, + unsigned int tram_shift) +{ + // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); + if ((tram_shift & 1) == 0) { + while (count--) { + *dst_left-- = *src++; + *dst_right-- = *src++; + } + } else { + while (count--) { + *dst_right-- = *src++; + *dst_left-- = *src++; + } + } +} + +static void snd_emu10k1_fx8010_playback_tram_poke(emu10k1_t *emu, + unsigned int *tram_pos, + unsigned int *tram_shift, + unsigned int tram_size, + unsigned short *src, + unsigned int frames) +{ + unsigned int count; + + while (frames > *tram_pos) { + count = *tram_pos + 1; + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + src, count, *tram_shift); + src += count * 2; + frames -= count; + *tram_pos = (tram_size / 2) - 1; + (*tram_shift)++; + } + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + src, frames, *tram_shift++); + *tram_pos -= frames; +} + +static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - pcm->appl_ptr; + snd_pcm_uframes_t buffer_size = pcm->buffer_size / 2; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + pcm->sw_ready += diff; + pcm->appl_ptr = appl_ptr; + } + while (pcm->hw_ready < buffer_size && + pcm->sw_ready > 0) { + size_t hw_to_end = buffer_size - pcm->hw_data; + size_t sw_to_end = (runtime->buffer_size << 2) - pcm->sw_data; + size_t tframes = buffer_size - pcm->hw_ready; + if (pcm->sw_ready < tframes) + tframes = pcm->sw_ready; + if (hw_to_end < tframes) + tframes = hw_to_end; + if (sw_to_end < tframes) + tframes = sw_to_end; + snd_emu10k1_fx8010_playback_tram_poke(emu, &pcm->tram_pos, &pcm->tram_shift, + pcm->buffer_size, + (unsigned short *)(runtime->dma_area + (pcm->sw_data << 2)), + tframes); + pcm->hw_data += tframes; + if (pcm->hw_data == buffer_size) + pcm->hw_data = 0; + pcm->sw_data += tframes; + if (pcm->sw_data == runtime->buffer_size) + pcm->sw_data = 0; + pcm->hw_ready += tframes; + pcm->sw_ready -= tframes; + } + return 0; +} + +static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned int i; + + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned int i; + + // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); + pcm->sw_data = pcm->sw_io = pcm->sw_ready = 0; + pcm->hw_data = pcm->hw_io = pcm->hw_ready = 0; + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + pcm->appl_ptr = 0; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); + return 0; +} + +static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned long flags; + int result = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +#ifdef EMU10K1_SET_AC3_IEC958 + { + int i; + for (i = 0; i < 3; i++) { + unsigned int bits; + bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | + 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; + snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); + } + } +#endif + result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); + if (result < 0) + goto __err; + snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + break; + default: + result = -EINVAL; + break; + } + __err: + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + size_t ptr; + snd_pcm_sframes_t frames; + + if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) + return 0; + ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0); + frames = ptr - pcm->hw_io; + if (frames < 0) + frames += runtime->buffer_size; + pcm->hw_io = ptr; + pcm->hw_ready -= frames; + pcm->sw_io += frames; + if (pcm->sw_io > runtime->buffer_size) + pcm->sw_io -= runtime->buffer_size; + snd_emu10k1_fx8010_playback_transfer(substream); + return pcm->sw_io; +} + +static snd_pcm_hardware_t snd_emu10k1_fx8010_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 1024, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + runtime->hw = snd_emu10k1_fx8010_playback; + runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; + runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; + spin_lock(&emu->reg_lock); + if (pcm->valid == 0) { + spin_unlock(&emu->reg_lock); + return -ENODEV; + } + pcm->opened = 1; + spin_unlock(&emu->reg_lock); + return 0; +} + +static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + spin_lock(&emu->reg_lock); + pcm->opened = 0; + spin_unlock(&emu->reg_lock); + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { + .open = snd_emu10k1_fx8010_playback_open, + .close = snd_emu10k1_fx8010_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_emu10k1_fx8010_playback_hw_params, + .hw_free = snd_emu10k1_fx8010_playback_hw_free, + .prepare = snd_emu10k1_fx8010_playback_prepare, + .trigger = snd_emu10k1_fx8010_playback_trigger, + .pointer = snd_emu10k1_fx8010_playback_pointer, + .ack = snd_emu10k1_fx8010_playback_transfer, +}; + +static void snd_emu10k1_fx8010_pcm_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_fx8010 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1", device, 8, 0, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_fx8010_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 FX8010"); + emu->pcm_fx8010 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +/************************************************************************* + * EMU10K1 effect manager + *************************************************************************/ + +static void snd_emu10k1_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr, + u32 op, u32 r, u32 a, u32 x, u32 y) +{ + snd_assert(*ptr < 512, return); + set_bit(*ptr, icode->code_valid); + icode->code[*ptr ][0] = ((x & 0x3ff) << 10) | (y & 0x3ff); + icode->code[(*ptr)++][1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff); +} + +#define OP(icode, ptr, op, r, a, x, y) \ + snd_emu10k1_write_op(icode, ptr, op, r, a, x, y) + +static void snd_emu10k1_audigy_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr, + u32 op, u32 r, u32 a, u32 x, u32 y) +{ + snd_assert(*ptr < 512, return); + set_bit(*ptr, icode->code_valid); + icode->code[*ptr ][0] = ((x & 0x7ff) << 12) | (y & 0x7ff); + icode->code[(*ptr)++][1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff); +} + +#define A_OP(icode, ptr, op, r, a, x, y) \ + snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y) + +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data) +{ + pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + snd_emu10k1_ptr_write(emu, pc, 0, data); +} + +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc) +{ + pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + return snd_emu10k1_ptr_read(emu, pc, 0); +} + +static void snd_emu10k1_gpr_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int gpr; + + for (gpr = 0; gpr < 0x100; gpr++) { + if (!test_bit(gpr, icode->gpr_valid)) + continue; + snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, icode->gpr_map[gpr]); + } +} + +static void snd_emu10k1_gpr_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int gpr; + + for (gpr = 0; gpr < 0x100; gpr++) { + set_bit(gpr, icode->gpr_valid); + icode->gpr_map[gpr] = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0); + } +} + +static void snd_emu10k1_tram_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int tram; + + for (tram = 0; tram < 0xa0; tram++) { + if (!test_bit(tram, icode->tram_valid)) + continue; + snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, icode->tram_data_map[tram]); + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, icode->tram_addr_map[tram]); + } +} + +static void snd_emu10k1_tram_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int tram; + + memset(icode->tram_valid, 0, sizeof(icode->tram_valid)); + for (tram = 0; tram < 0xa0; tram++) { + set_bit(tram, icode->tram_valid); + icode->tram_data_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0); + icode->tram_addr_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0); + } +} + +static void snd_emu10k1_code_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + u32 pc; + + for (pc = 0; pc < 512; pc++) { + if (!test_bit(pc, icode->code_valid)) + continue; + snd_emu10k1_efx_write(emu, pc * 2, icode->code[pc][0]); + snd_emu10k1_efx_write(emu, pc * 2 + 1, icode->code[pc][1]); + } +} + +static void snd_emu10k1_code_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + u32 pc; + + memset(icode->code_valid, 0, sizeof(icode->code_valid)); + for (pc = 0; pc < 512; pc++) { + set_bit(pc, icode->code_valid); + icode->code[pc][0] = snd_emu10k1_efx_read(emu, pc * 2); + icode->code[pc][1] = snd_emu10k1_efx_read(emu, pc * 2 + 1); + } +} + +static snd_emu10k1_fx8010_ctl_t *snd_emu10k1_look_for_ctl(emu10k1_t *emu, snd_ctl_elem_id_t *id) +{ + snd_emu10k1_fx8010_ctl_t *ctl; + snd_kcontrol_t *kcontrol; + struct list_head *list; + + list_for_each(list, &emu->fx8010.gpr_ctl) { + ctl = emu10k1_gpr_ctl(list); + kcontrol = ctl->kcontrol; + if (kcontrol->id.iface == id->iface && + !strcmp(kcontrol->id.name, id->name) && + kcontrol->id.index == id->index) + return ctl; + } + return NULL; +} + +static int snd_emu10k1_verify_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + unsigned int i; + snd_ctl_elem_id_t *_id, id; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + + for (i = 0, _id = icode->gpr_del_controls; + i < icode->gpr_del_control_count; i++, _id++) { + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + if (snd_emu10k1_look_for_ctl(emu, &id) == NULL) + return -ENOENT; + } + for (i = 0, _gctl = icode->gpr_add_controls; + i < icode->gpr_add_control_count; i++, _gctl++) { + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return -EFAULT; + if (snd_emu10k1_look_for_ctl(emu, &gctl.id)) + continue; + if (snd_ctl_find_id(emu->card, &gctl.id) != NULL) + return -EEXIST; + if (gctl.id.iface != SNDRV_CTL_ELEM_IFACE_MIXER && + gctl.id.iface != SNDRV_CTL_ELEM_IFACE_PCM) + return -EINVAL; + } + for (i = 0, _gctl = icode->gpr_list_controls; + i < icode->gpr_list_control_count; i++, _gctl++) { + /* FIXME: we need to check the WRITE access */ + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return -EFAULT; + } + return 0; +} + +static void snd_emu10k1_ctl_private_free(snd_kcontrol_t *kctl) +{ + snd_emu10k1_fx8010_ctl_t *ctl; + + ctl = (snd_emu10k1_fx8010_ctl_t *)kctl->private_value; + kctl->private_value = 0; + list_del(&ctl->list); + kfree(ctl); +} + +static void snd_emu10k1_add_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + unsigned int i, j; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + snd_emu10k1_fx8010_ctl_t *ctl, nctl; + snd_kcontrol_new_t knew; + snd_kcontrol_t *kctl; + snd_ctl_elem_value_t val; + + for (i = 0, _gctl = icode->gpr_add_controls; + i < icode->gpr_add_control_count; i++, _gctl++) { + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return; + snd_runtime_check(gctl.id.iface == SNDRV_CTL_ELEM_IFACE_MIXER || + gctl.id.iface == SNDRV_CTL_ELEM_IFACE_PCM, continue); + snd_runtime_check(gctl.id.name[0] != '\0', continue); + ctl = snd_emu10k1_look_for_ctl(emu, &gctl.id); + memset(&knew, 0, sizeof(knew)); + knew.iface = gctl.id.iface; + knew.name = gctl.id.name; + knew.index = gctl.id.index; + knew.device = gctl.id.device; + knew.subdevice = gctl.id.subdevice; + knew.info = snd_emu10k1_gpr_ctl_info; + knew.get = snd_emu10k1_gpr_ctl_get; + knew.put = snd_emu10k1_gpr_ctl_put; + memset(&nctl, 0, sizeof(nctl)); + nctl.vcount = gctl.vcount; + nctl.count = gctl.count; + for (j = 0; j < 32; j++) { + nctl.gpr[j] = gctl.gpr[j]; + nctl.value[j] = ~gctl.value[j]; /* inverted, we want to write new value in gpr_ctl_put() */ + val.value.integer.value[j] = gctl.value[j]; + } + nctl.min = gctl.min; + nctl.max = gctl.max; + nctl.translation = gctl.translation; + if (ctl == NULL) { + ctl = (snd_emu10k1_fx8010_ctl_t *)kmalloc(sizeof(*ctl), GFP_KERNEL); + if (ctl == NULL) + continue; + knew.private_value = (unsigned long)ctl; + memcpy(ctl, &nctl, sizeof(nctl)); + if (snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu)) < 0) { + kfree(ctl); + continue; + } + kctl->private_free = snd_emu10k1_ctl_private_free; + ctl->kcontrol = kctl; + list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl); + } else { + /* overwrite */ + nctl.list = ctl->list; + nctl.kcontrol = ctl->kcontrol; + memcpy(ctl, &nctl, sizeof(nctl)); + snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id); + } + snd_emu10k1_gpr_ctl_put(ctl->kcontrol, &val); + } +} + +static void snd_emu10k1_del_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + unsigned int i; + snd_ctl_elem_id_t *_id, id; + snd_emu10k1_fx8010_ctl_t *ctl; + + for (i = 0, _id = icode->gpr_del_controls; + i < icode->gpr_del_control_count; i++, _id++) { + snd_runtime_check(copy_from_user(&id, _id, sizeof(id)) == 0, continue); + ctl = snd_emu10k1_look_for_ctl(emu, &id); + snd_runtime_check(ctl == NULL, continue); + snd_ctl_remove(emu->card, ctl->kcontrol); + list_del(&ctl->list); + } +} + +static int snd_emu10k1_list_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + unsigned int i = 0, j; + unsigned int total = 0; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + snd_emu10k1_fx8010_ctl_t *ctl; + snd_ctl_elem_id_t *id; + struct list_head *list; + + _gctl = icode->gpr_list_controls; + list_for_each(list, &emu->fx8010.gpr_ctl) { + ctl = emu10k1_gpr_ctl(list); + total++; + if (_gctl && i < icode->gpr_list_control_count) { + memset(&gctl, 0, sizeof(gctl)); + id = &ctl->kcontrol->id; + gctl.id.iface = id->iface; + strncpy(gctl.id.name, id->name, sizeof(gctl.id.name)); + gctl.id.index = id->index; + gctl.id.device = id->device; + gctl.id.subdevice = id->subdevice; + gctl.vcount = ctl->vcount; + gctl.count = ctl->count; + for (j = 0; j < 32; j++) { + gctl.gpr[j] = ctl->gpr[j]; + gctl.value[j] = ctl->value[j]; + } + gctl.min = ctl->min; + gctl.max = ctl->max; + gctl.translation = ctl->translation; + if (copy_to_user(_gctl, &gctl, sizeof(gctl))) + return -EFAULT; + _gctl++; + i++; + } + } + icode->gpr_list_control_total = total; + return 0; +} + +static int snd_emu10k1_icode_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int err = 0; + + down(&emu->fx8010.lock); + if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0) + goto __error; + strncpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name)-1); + emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0'; + /* stop FX processor - this may be dangerous, but it's better to miss + some samples than generate wrong ones - [jk] */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP); + /* ok, do the main job */ + snd_emu10k1_del_controls(emu, icode); + snd_emu10k1_gpr_poke(emu, icode); + snd_emu10k1_tram_poke(emu, icode); + snd_emu10k1_code_poke(emu, icode); + snd_emu10k1_add_controls(emu, icode); + /* start FX processor when the DSP code is updated */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg); + __error: + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_icode_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int err; + + down(&emu->fx8010.lock); + strncpy(icode->name, emu->fx8010.name, sizeof(icode->name)-1); + emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0'; + /* ok, do the main job */ + snd_emu10k1_gpr_peek(emu, icode); + snd_emu10k1_tram_peek(emu, icode); + snd_emu10k1_code_peek(emu, icode); + err = snd_emu10k1_list_controls(emu, icode); + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_ipcm_poke(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm) +{ + unsigned int i; + int err = 0; + snd_emu10k1_fx8010_pcm_t *pcm; + + if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) + return -EINVAL; + if (ipcm->channels > 32) + return -EINVAL; + pcm = &emu->fx8010.pcm[ipcm->substream]; + down(&emu->fx8010.lock); + spin_lock_irq(&emu->reg_lock); + if (pcm->opened) { + err = -EBUSY; + goto __error; + } + if (ipcm->channels == 0) { /* remove */ + pcm->valid = 0; + } else { + /* FIXME: we need to add universal code to the PCM transfer routine */ + if (ipcm->channels != 2) { + err = -EINVAL; + goto __error; + } + pcm->valid = 1; + pcm->opened = 0; + pcm->channels = ipcm->channels; + pcm->tram_start = ipcm->tram_start; + pcm->buffer_size = ipcm->buffer_size; + pcm->gpr_size = ipcm->gpr_size; + pcm->gpr_count = ipcm->gpr_count; + pcm->gpr_tmpcount = ipcm->gpr_tmpcount; + pcm->gpr_ptr = ipcm->gpr_ptr; + pcm->gpr_trigger = ipcm->gpr_trigger; + pcm->gpr_running = ipcm->gpr_running; + for (i = 0; i < pcm->channels; i++) + pcm->etram[i] = ipcm->etram[i]; + } + __error: + spin_unlock_irq(&emu->reg_lock); + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_ipcm_peek(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm) +{ + unsigned int i; + int err = 0; + snd_emu10k1_fx8010_pcm_t *pcm; + + if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) + return -EINVAL; + pcm = &emu->fx8010.pcm[ipcm->substream]; + down(&emu->fx8010.lock); + spin_lock_irq(&emu->reg_lock); + ipcm->channels = pcm->channels; + ipcm->tram_start = pcm->tram_start; + ipcm->buffer_size = pcm->buffer_size; + ipcm->gpr_size = pcm->gpr_size; + ipcm->gpr_ptr = pcm->gpr_ptr; + ipcm->gpr_count = pcm->gpr_count; + ipcm->gpr_tmpcount = pcm->gpr_tmpcount; + ipcm->gpr_trigger = pcm->gpr_trigger; + ipcm->gpr_running = pcm->gpr_running; + for (i = 0; i < pcm->channels; i++) + ipcm->etram[i] = pcm->etram[i]; + ipcm->res1 = ipcm->res2 = 0; + ipcm->pad = 0; + spin_unlock_irq(&emu->reg_lock); + up(&emu->fx8010.lock); + return err; +} + +#define SND_EMU10K1_GPR_CONTROLS 41 +#define SND_EMU10K1_INPUTS 10 +#define SND_EMU10K1_PLAYBACK_CHANNELS 6 +#define SND_EMU10K1_CAPTURE_CHANNELS 4 + +static void __devinit snd_emu10k1_init_mono_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 1; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->min = 0; + ctl->max = 100; + ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; +} + +static void __devinit snd_emu10k1_init_stereo_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 2; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; + ctl->min = 0; + ctl->max = 100; + ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; +} + +static void __devinit snd_emu10k1_init_mono_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 1; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->min = 0; + ctl->max = 1; + ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; +} + +static void __devinit snd_emu10k1_init_stereo_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 2; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; + ctl->min = 0; + ctl->max = 1; + ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; +} + + +/* + * initial DSP configuration for Audigy + */ + +#define A_GPR_ACCU 0xd6 + +static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu) +{ + int err, i, z, gpr, tmp, playback, capture, nctl; + u32 ptr; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_control_gpr_t *controls, *ctl; + mm_segment_t seg; + + spin_lock_init(&emu->fx8010.irq_lock); + INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); + + if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + kfree(icode); + return -ENOMEM; + } + + /* clear free GPRs */ + for (i = 0; i < 256; i++) + set_bit(i, icode->gpr_valid); + + strcpy(icode->name, "Audigy DSP code for ALSA"); + ptr = 0; + nctl = 0; + playback = 10; + capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */ + gpr = capture + 10; + tmp = 0x88; + + /* stop FX processor */ + snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP); + + /* Wave Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Surround Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Surround Playback Volume", gpr, 0); + gpr += 2; + + /* Wave Center Playback */ + /* Center = sub = Left/2 + Right/2 */ + A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_FXBUS(FXBUS_PCM_LEFT), 0xcd, A_FXBUS(FXBUS_PCM_RIGHT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_GPR(tmp)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Center Playback Volume", gpr, 0); + gpr++; + + /* Wave LFE Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_GPR(tmp)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave LFE Playback Volume", gpr, 0); + gpr++; + + /* Music Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+0), A_GPR(playback+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Capture */ + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Capture Volume", gpr, 0); + gpr += 2; + + /* Music Capture */ + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Capture Volume", gpr, 0); + gpr += 2; + + /* Surround Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 80); + gpr += 2; + + /* Center Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 80); + gpr++; + + /* LFE Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE)); + snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 80); + gpr++; + + /* + * inputs + */ +#define A_ADD_VOLUME_IN(var,vol,input) \ +A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) + + /* AC'97 Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_AC97_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_AC97_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "AC97 Playback Volume", gpr, 0); + gpr += 2; + /* AC'97 Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "AC97 Capture Volume", gpr, 100); + gpr += 2; + + /* Audigy CD Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_SPDIF_CD_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_SPDIF_CD_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Playback Volume", gpr, 0); + gpr += 2; + /* Audigy CD Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Capture Volume", gpr, 0); + gpr += 2; + + /* Optical SPDIF Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_OPT_SPDIF_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_OPT_SPDIF_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Optical IEC958 Playback Volume", gpr, 0); + gpr += 2; + /* Optical SPDIF Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Optical IEC958 Capture Volume", gpr, 0); + gpr += 2; + + /* Line2 Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_LINE2_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_LINE2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Playback Volume", gpr, 0); + gpr += 2; + /* Line2 Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Capture Volume", gpr, 0); + gpr += 2; + + /* RCA SPDIF Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_RCA_SPDIF_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_RCA_SPDIF_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "RCA SPDIF Playback Volume", gpr, 0); + gpr += 2; + /* RCA SPDIF Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_RCA_SPDIF_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_RCA_SPDIF_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "RCA SPDIF Capture Volume", gpr, 0); + gpr += 2; + + /* Aux2 Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_AUX2_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_AUX2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Aux2 Playback Volume", gpr, 0); + gpr += 2; + /* Aux2 Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Aux2 Capture Volume", gpr, 0); + gpr += 2; + + /* + * outputs + */ +#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src)) +#define A_PUT_STEREO_OUTPUT(out1,out2,src) \ + {A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);} + +#define _A_SWITCH(icode, ptr, dst, src, sw) \ + A_OP((icode), ptr, iMACINT0, dst, A_C_00000000, src, sw); +#define A_SWITCH(icode, ptr, dst, src, sw) \ + _A_SWITCH(icode, ptr, A_GPR(dst), A_GPR(src), A_GPR(sw)) +#define _A_SWITCH_NEG(icode, ptr, dst, src) \ + A_OP((icode), ptr, iANDXOR, dst, src, A_C_00000001, A_C_00000001); +#define A_SWITCH_NEG(icode, ptr, dst, src) \ + _A_SWITCH_NEG(icode, ptr, A_GPR(dst), A_GPR(src)) + + + /* + * Process tone control + */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), A_GPR(playback + 0), A_C_00000000, A_C_00000000); /* left */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), A_GPR(playback + 1), A_C_00000000, A_C_00000000); /* right */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), A_GPR(playback + 2), A_C_00000000, A_C_00000000); /* rear left */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */ + + ctl = &controls[nctl + 0]; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Bass"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_BASS; + ctl = &controls[nctl + 1]; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Treble"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_TREBLE; + +#define BASS_GPR 0x8c +#define TREBLE_GPR 0x96 + + for (z = 0; z < 5; z++) { + int j; + for (j = 0; j < 2; j++) { + controls[nctl + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j; + controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j; + } + } + for (z = 0; z < 3; z++) { /* front/rear/center-lfe */ + int j, k, l, d; + for (j = 0; j < 2; j++) { /* left/right */ + k = 0xb0 + (z * 8) + (j * 4); + l = 0xe0 + (z * 8) + (j * 4); + d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j; + + A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(BASS_GPR + 0 + j)); + A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(BASS_GPR + 4 + j)); + A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(BASS_GPR + 2 + j)); + A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(BASS_GPR + 8 + j)); + A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(BASS_GPR + 6 + j)); + A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000); + + A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(TREBLE_GPR + 0 + j)); + A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(TREBLE_GPR + 4 + j)); + A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(TREBLE_GPR + 2 + j)); + A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(TREBLE_GPR + 8 + j)); + A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(TREBLE_GPR + 6 + j)); + A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010); + + A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000); + + if (z == 2) /* center */ + break; + } + } + nctl += 2; + +#undef BASS_GPR +#undef TREBLE_GPR + + for (z = 0; z < 6; z++) { + A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0); + A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0); + A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1); + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000); + } + snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0); + gpr += 2; + + /* digital outputs */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS); + + /* analog speakers */ + //A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_STEREO_OUTPUT(A_EXTOUT_AC97_L, A_EXTOUT_AC97_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS); + A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS); + + /* headphone */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); + + /* ADC buffer */ + A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture); + A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1); + + /* + * ok, set up done.. + */ + + if (gpr > tmp) { + snd_BUG(); + err = -EIO; + goto __err; + } + /* clear remaining instruction memory */ + while (ptr < 0x200) + A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0); + + seg = snd_enter_user(); + icode->gpr_add_control_count = nctl; + icode->gpr_add_controls = controls; + err = snd_emu10k1_icode_poke(emu, icode); + snd_leave_user(seg); + + __err: + kfree(controls); + kfree(icode); + return err; +} + + +/* + * initial DSP configuration for Emu10k1 + */ + +/* when volume = max, then copy only to avoid volume modification */ +/* with iMAC0 (negative values) */ +static void __devinit _volume(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iMAC0, dst, C_00000000, src, vol); + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001); + OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000); +} +static void __devinit _volume_add(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001); + OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001); + OP(icode, ptr, iMAC0, dst, dst, src, vol); +} +static void __devinit _volume_out(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000); + OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001); + OP(icode, ptr, iMAC0, dst, C_00000000, src, vol); +} + +#define VOLUME(icode, ptr, dst, src, vol) \ + _volume(icode, ptr, GPR(dst), GPR(src), GPR(vol)) +#define VOLUME_IN(icode, ptr, dst, src, vol) \ + _volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol)) +#define VOLUME_ADD(icode, ptr, dst, src, vol) \ + _volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol)) +#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \ + _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol)) +#define VOLUME_OUT(icode, ptr, dst, src, vol) \ + _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol)) +#define _SWITCH(icode, ptr, dst, src, sw) \ + OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw); +#define SWITCH(icode, ptr, dst, src, sw) \ + _SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw)) +#define SWITCH_IN(icode, ptr, dst, src, sw) \ + _SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw)) +#define _SWITCH_NEG(icode, ptr, dst, src) \ + OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001); +#define SWITCH_NEG(icode, ptr, dst, src) \ + _SWITCH_NEG(icode, ptr, GPR(dst), GPR(src)) + + +static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) +{ + int err, i, z, gpr, tmp, playback, capture; + u32 ptr; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_pcm_t *ipcm; + emu10k1_fx8010_control_gpr_t *controls, *ctl; + mm_segment_t seg; + + spin_lock_init(&emu->fx8010.irq_lock); + INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); + + if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + kfree(icode); + return -ENOMEM; + } + if ((ipcm = snd_kcalloc(sizeof(emu10k1_fx8010_pcm_t), GFP_KERNEL)) == NULL) { + kfree(controls); + kfree(icode); + return -ENOMEM; + } + + /* clear free GPRs */ + for (i = 0; i < 256; i++) + set_bit(i, icode->gpr_valid); + + /* clear TRAM data & address lines */ + for (i = 0; i < 160; i++) + set_bit(i, icode->tram_valid); + + strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); + ptr = 0; i = 0; + /* we have 10 inputs */ + playback = SND_EMU10K1_INPUTS; + /* we have 6 playback channels and tone control doubles */ + capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); + gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS; + tmp = 0x88; /* we need 4 temporary GPR */ + /* from 0x8c to 0xff is the area for tone control */ + + /* stop FX processor */ + snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP); + + /* + * Process FX Buses + */ + OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */ + OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */ + + /* Raw S/PDIF PCM */ + ipcm->substream = 0; + ipcm->channels = 2; + ipcm->tram_start = 0; + ipcm->buffer_size = (64 * 1024) / 2; + ipcm->gpr_size = gpr++; + ipcm->gpr_ptr = gpr++; + ipcm->gpr_count = gpr++; + ipcm->gpr_tmpcount = gpr++; + ipcm->gpr_trigger = gpr++; + ipcm->gpr_running = gpr++; + ipcm->etram[0] = 0; + ipcm->etram[1] = 1; + + icode->gpr_map[gpr + 0] = 0xfffff000; + icode->gpr_map[gpr + 1] = 0xffff0000; + icode->gpr_map[gpr + 2] = 0x70000000; + icode->gpr_map[gpr + 3] = 0x00000007; + icode->gpr_map[gpr + 4] = 0x001f << 11; + icode->gpr_map[gpr + 5] = 0x001c << 11; + icode->gpr_map[gpr + 6] = (0x22 - 0x01) - 1; /* skip at 01 to 22 */ + icode->gpr_map[gpr + 7] = (0x22 - 0x06) - 1; /* skip at 06 to 22 */ + icode->gpr_map[gpr + 8] = 0x2000000 + (2<<11); + icode->gpr_map[gpr + 9] = 0x4000000 + (2<<11); + icode->gpr_map[gpr + 10] = 1<<11; + icode->gpr_map[gpr + 11] = (0x24 - 0x0a) - 1; /* skip at 0a to 24 */ + icode->gpr_map[gpr + 12] = 0; + + /* if the trigger flag is not set, skip */ + /* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000); + /* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6)); + /* if the running flag is set, we're running */ + /* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000); + /* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004); + /* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */ + /* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000); + /* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5)); + /* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7)); + /* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000); + + /* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001); + /* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000); + /* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11)); + /* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000); + + /* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000); + /* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000); + /* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2)); + /* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001); + /* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2)); + + /* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000); + /* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000); + /* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2)); + /* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001); + /* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2)); + + /* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000); + /* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size)); + /* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001); + /* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000); + /* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000); + + /* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000); + /* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + /* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000); + /* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000); + /* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000); + + /* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001); + /* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002); + + /* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff); + /* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff); + + /* 24: */ + gpr += 13; + + /* Wave Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME(icode, &ptr, playback + z, z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Surround Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0); + gpr += 2; + + /* Wave Center/LFE Playback Volume */ + OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000); + OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002); + VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0); + VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0); + + /* Wave Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z); + VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Music Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Music Playback Volume", gpr, 100); + gpr += 2; + + /* Music Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z); + VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Music Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Music Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Surround Digital Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100); + gpr += 2; + + /* Surround Digital Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z); + VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Digital Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Center Playback Volume */ + VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Center Playback Volume", gpr++, 100); + + /* LFE Playback Volume + Switch */ + VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr); + snd_emu10k1_init_mono_control(controls + i++, "LFE Playback Volume", gpr++, 100); + + /* + * Process inputs + */ + + if (emu->fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Bass"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_BASS; + ctl = &controls[i + 1]; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Treble"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_TREBLE; + +#define BASS_GPR 0x8c +#define TREBLE_GPR 0x96 + + for (z = 0; z < 5; z++) { + int j; + for (j = 0; j < 2; j++) { + controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j; + controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j; + } + } + for (z = 0; z < 3; z++) { /* front/rear/center-lfe */ + int j, k, l, d; + for (j = 0; j < 2; j++) { /* left/right */ + k = 0xa0 + (z * 8) + (j * 4); + l = 0xd0 + (z * 8) + (j * 4); + d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j; + + OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j)); + OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j)); + OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j)); + OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j)); + OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j)); + OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000); + + OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j)); + OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j)); + OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j)); + OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j)); + OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j)); + OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010); + + OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000); + + if (z == 2) /* center */ + break; + } + } + i += 2; + +#undef BASS_GPR +#undef TREBLE_GPR + + for (z = 0; z < 6; z++) { + SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0); + SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0); + SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1); + OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000); + } + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0); + gpr += 2; + + /* + * Process outputs + */ + if (emu->fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & (1<fx8010.extout_mask & (1<fx8010.extout_mask & (1< tmp) { + snd_BUG(); + err = -EIO; + goto __err; + } + if (i > SND_EMU10K1_GPR_CONTROLS) { + snd_BUG(); + err = -EIO; + goto __err; + } + + /* clear remaining instruction memory */ + while (ptr < 0x200) + OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000); + + if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0) + goto __err; + seg = snd_enter_user(); + icode->gpr_add_control_count = i; + icode->gpr_add_controls = controls; + err = snd_emu10k1_icode_poke(emu, icode); + snd_leave_user(seg); + if (err >= 0) + err = snd_emu10k1_ipcm_poke(emu, ipcm); + __err: + kfree(ipcm); + kfree(controls); + kfree(icode); + return err; +} + +int __devinit snd_emu10k1_init_efx(emu10k1_t *emu) +{ + if (emu->audigy) + return _snd_emu10k1_audigy_init_efx(emu); + else + return _snd_emu10k1_init_efx(emu); +} + +void snd_emu10k1_free_efx(emu10k1_t *emu) +{ + /* stop processor */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP); +} + +#if 0 // FIXME: who use them? +int snd_emu10k1_fx8010_tone_control_activate(emu10k1_t *emu, int output) +{ + snd_runtime_check(output >= 0 && output < 6, return -EINVAL); + snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1); + return 0; +} + +int snd_emu10k1_fx8010_tone_control_deactivate(emu10k1_t *emu, int output) +{ + snd_runtime_check(output >= 0 && output < 6, return -EINVAL); + snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0); + return 0; +} +#endif + +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size) +{ + u8 size_reg = 0; + + /* size is in samples */ + if (size != 0) { + size = (size - 1) >> 13; + + while (size) { + size >>= 1; + size_reg++; + } + size = 0x2000 << size_reg; + } + if (emu->fx8010.etram_size == size) + return 0; + spin_lock_irq(&emu->emu_lock); + outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG); + spin_unlock_irq(&emu->emu_lock); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, 0); + if (emu->fx8010.etram_pages != NULL) { + snd_free_pci_pages(emu->pci, emu->fx8010.etram_size * 2, emu->fx8010.etram_pages, emu->fx8010.etram_pages_dmaaddr); + emu->fx8010.etram_pages = NULL; + emu->fx8010.etram_size = 0; + } + + if (size > 0) { + emu->fx8010.etram_pages = snd_malloc_pci_pages(emu->pci, size * 2, &emu->fx8010.etram_pages_dmaaddr); + if (emu->fx8010.etram_pages == NULL) + return -ENOMEM; + memset(emu->fx8010.etram_pages, 0, size * 2); + snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages_dmaaddr); + snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg); + spin_lock_irq(&emu->emu_lock); + outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); + spin_unlock_irq(&emu->emu_lock); + } + + emu->fx8010.etram_size = size; + return 0; +} + +static int snd_emu10k1_fx8010_open(snd_hwdep_t * hw, struct file *file) +{ + return 0; +} + +static void copy_string(char *dst, char *src, char *null, int idx) +{ + if (src == NULL) + sprintf(dst, "%s %02X", null, idx); + else + strcpy(dst, src); +} + +static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info) +{ + char **fxbus, **extin, **extout; + unsigned short fxbus_mask, extin_mask, extout_mask; + int res; + + memset(info, 0, sizeof(info)); + info->card = emu->card_type; + info->internal_tram_size = emu->fx8010.itram_size; + info->external_tram_size = emu->fx8010.etram_size; + fxbus = fxbuses; + extin = emu->audigy ? audigy_ins : creative_ins; + extout = emu->audigy ? audigy_outs : creative_outs; + fxbus_mask = emu->fx8010.fxbus_mask; + extin_mask = emu->fx8010.extin_mask; + extout_mask = emu->fx8010.extout_mask; + for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { + copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res); + copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); + copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + } + for (res = 16; res < 32; res++, extout++) + copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + info->gpr_controls = emu->fx8010.gpr_count; + return 0; +} + +static int snd_emu10k1_fx8010_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, hw->private_data, return -ENXIO); + emu10k1_fx8010_info_t info; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_pcm_t *ipcm; + unsigned int addr; + int res; + + switch (cmd) { + case SNDRV_EMU10K1_IOCTL_INFO: + if ((res = snd_emu10k1_fx8010_info(emu, &info)) < 0) + return res; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + case SNDRV_EMU10K1_IOCTL_CODE_POKE: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL); + if (icode == NULL) + return -ENOMEM; + if (copy_from_user(icode, (void *)arg, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + res = snd_emu10k1_icode_poke(emu, icode); + kfree(icode); + return res; + case SNDRV_EMU10K1_IOCTL_CODE_PEEK: + icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL); + if (icode == NULL) + return -ENOMEM; + if (copy_from_user(icode, (void *)arg, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + res = snd_emu10k1_icode_peek(emu, icode); + if (res == 0 && copy_to_user((void *)arg, icode, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + kfree(icode); + return res; + case SNDRV_EMU10K1_IOCTL_PCM_POKE: + if (emu->audigy) + return -EINVAL; + ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + if (ipcm == NULL) + return -ENOMEM; + res = snd_emu10k1_ipcm_poke(emu, ipcm); + kfree(ipcm); + return res; + case SNDRV_EMU10K1_IOCTL_PCM_PEEK: + if (emu->audigy) + return -EINVAL; + ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + if (ipcm == NULL) + return -ENOMEM; + res = snd_emu10k1_ipcm_peek(emu, ipcm); + if (res == 0 && copy_to_user((void *)arg, ipcm, sizeof(*ipcm))) { + kfree(ipcm); + return -EFAULT; + } + kfree(ipcm); + return res; + case SNDRV_EMU10K1_IOCTL_TRAM_SETUP: + if (emu->audigy) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(addr, (unsigned int *)arg)) + return -EFAULT; + down(&emu->fx8010.lock); + res = snd_emu10k1_fx8010_tram_setup(emu, addr); + up(&emu->fx8010.lock); + return res; + case SNDRV_EMU10K1_IOCTL_STOP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP); + return 0; + case SNDRV_EMU10K1_IOCTL_CONTINUE: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0); + return 0; + case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC); + udelay(10); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg); + return 0; + case SNDRV_EMU10K1_IOCTL_SINGLE_STEP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(addr, (unsigned int *)arg)) + return -EFAULT; + if (addr > 0x1ff) + return -EINVAL; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr); + udelay(10); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr); + return 0; + case SNDRV_EMU10K1_IOCTL_DBG_READ: + if (emu->audigy) + addr = snd_emu10k1_ptr_read(emu, A_DBG, 0); + else + addr = snd_emu10k1_ptr_read(emu, DBG, 0); + if (put_user(addr, (unsigned int *)arg)) + return -EFAULT; + return 0; + } + return -ENOTTY; +} + +static int snd_emu10k1_fx8010_release(snd_hwdep_t * hw, struct file *file) +{ + return 0; +} + +int __devinit snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hw; + int err; + + if (rhwdep) + *rhwdep = NULL; + if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0) + return err; + strcpy(hw->name, "EMU10K1 (FX8010)"); + hw->iface = SNDRV_HWDEP_IFACE_EMU10K1; + hw->ops.open = snd_emu10k1_fx8010_open; + hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; + hw->ops.release = snd_emu10k1_fx8010_release; + hw->private_data = emu; + if (rhwdep) + *rhwdep = hw; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emumixer.c linux/sound/pci/emu10k1/emumixer.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emumixer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emumixer.c 2002-08-15 06:13:09.000000000 -0600 @@ -0,0 +1,504 @@ +/* + * Copyright (c) by Jaroslav Kysela , + * Takashi Iwai + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / mixer routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_emu10k1_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + unsigned long flags; + + spin_lock_irqsave(&emu->reg_lock, flags); + ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value, change; + unsigned int val; + unsigned long flags; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&emu->reg_lock, flags); + change = val != emu->spdif_bits[idx]; + if (change) { + snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val); + emu->spdif_bits[idx] = val; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + .info = snd_emu10k1_spdif_info, + .get = snd_emu10k1_spdif_get_mask +}; + +static snd_kcontrol_new_t snd_emu10k1_spdif_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_emu10k1_spdif_info, + .get = snd_emu10k1_spdif_get, + .put = snd_emu10k1_spdif_put +}; + + +static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route) +{ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, + snd_emu10k1_compose_audigy_fxrt1(route)); + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, + snd_emu10k1_compose_audigy_fxrt2(route)); + } else { + snd_emu10k1_ptr_write(emu, FXRT, voice, + snd_emu10k1_compose_send_routing(route)); + } +} + +static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char *volume) +{ + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]); + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]); + snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]); + snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]); + if (emu->audigy) { + unsigned int val = ((unsigned int)volume[4] << 24) | + ((unsigned int)volume[5] << 16) | + ((unsigned int)volume[6] << 8) | + (unsigned int)volume[7]; + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val); + } +} + +static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = emu->audigy ? 3*8 : 3*4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f; + return 0; +} + +static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int voice, idx; + int num_efx = emu->audigy ? 8 : 4; + int mask = emu->audigy ? 0x3f : 0x0f; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (voice = 0; voice < 3; voice++) + for (idx = 0; idx < num_efx; idx++) + ucontrol->value.integer.value[(voice * num_efx) + idx] = + mix->send_routing[voice][idx] & mask; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, voice, idx, val; + int num_efx = emu->audigy ? 8 : 4; + int mask = emu->audigy ? 0x3f : 0x0f; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (voice = 0; voice < 3; voice++) + for (idx = 0; idx < num_efx; idx++) { + val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask; + if (mix->send_routing[voice][idx] != val) { + mix->send_routing[voice][idx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number, + &mix->send_routing[1][0]); + update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number, + &mix->send_routing[2][0]); + } else if (mix->epcm->voices[0]) { + update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number, + &mix->send_routing[0][0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_send_routing_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EMU10K1 PCM Send Routing", + .info = snd_emu10k1_send_routing_info, + .get = snd_emu10k1_send_routing_get, + .put = snd_emu10k1_send_routing_put +}; + +static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = emu->audigy ? 3*8 : 3*4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx; + int num_efx = emu->audigy ? 8 : 4; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3*num_efx; idx++) + ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, idx, val; + int num_efx = emu->audigy ? 8 : 4; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3*num_efx; idx++) { + val = ucontrol->value.integer.value[idx] & 255; + if (mix->send_volume[idx/num_efx][idx%num_efx] != val) { + mix->send_volume[idx/num_efx][idx%num_efx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number, + &mix->send_volume[1][0]); + update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number, + &mix->send_volume[2][0]); + } else if (mix->epcm->voices[0]) { + update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number, + &mix->send_volume[0][0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_send_volume_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EMU10K1 PCM Send Volume", + .info = snd_emu10k1_send_volume_info, + .get = snd_emu10k1_send_volume_get, + .put = snd_emu10k1_send_volume_put +}; + +static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + return 0; +} + +static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3; idx++) + ucontrol->value.integer.value[idx] = mix->attn[idx]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, idx, val; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3; idx++) { + val = ucontrol->value.integer.value[idx] & 0xffff; + if (mix->attn[idx] != val) { + mix->attn[idx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]); + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]); + } else if (mix->epcm->voices[0]) { + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_attn_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "EMU10K1 PCM Volume", + .info = snd_emu10k1_attn_info, + .get = snd_emu10k1_attn_get, + .put = snd_emu10k1_attn_put +}; + +static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_emu10k1_shared_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + + if (emu->audigy) + ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0; + else + ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0; + return 0; +} + +static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int reg, val; + int change = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + if (emu->audigy) { + reg = inl(emu->port + A_IOCFG); + val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0; + change = (reg & A_IOCFG_GPOUT0) != val; + if (change) { + reg &= ~A_IOCFG_GPOUT0; + reg |= val; + outl(reg | val, emu->port + A_IOCFG); + } + } + reg = inl(emu->port + HCFG); + val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0; + change |= (reg & HCFG_GPOUT0) != val; + if (change) { + reg &= ~HCFG_GPOUT0; + reg |= val; + outl(reg | val, emu->port + HCFG); + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_shared_spdif __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SB Live Analog/Digital Output Jack", + .info = snd_emu10k1_shared_spdif_info, + .get = snd_emu10k1_shared_spdif_get, + .put = snd_emu10k1_shared_spdif_put +}; + +static snd_kcontrol_new_t snd_audigy_shared_spdif __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Audigy Analog/Digital Output Jack", + .info = snd_emu10k1_shared_spdif_info, + .get = snd_emu10k1_shared_spdif_get, + .put = snd_emu10k1_shared_spdif_put +}; + +/* + */ +static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + emu->ac97 = NULL; +} + +int __devinit snd_emu10k1_mixer(emu10k1_t *emu) +{ + ac97_t ac97; + int err, pcm, idx; + snd_kcontrol_t *kctl; + snd_card_t *card = emu->card; + + if (!emu->APS) { + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_emu10k1_ac97_write; + ac97.read = snd_emu10k1_ac97_read; + ac97.private_data = emu; + ac97.private_free = snd_emu10k1_mixer_free_ac97; + if ((err = snd_ac97_mixer(emu->card, &ac97, &emu->ac97)) < 0) + return err; + } else { + strcpy(emu->card->mixername, "EMU APS"); + } + + for (pcm = 0; pcm < 32; pcm++) { + emu10k1_pcm_mixer_t *mix; + int v; + + mix = &emu->pcm_mixer[pcm]; + mix->epcm = NULL; + + if ((kctl = mix->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + for (v = 0; v < 4; v++) + mix->send_routing[0][v] = + mix->send_routing[1][v] = + mix->send_routing[2][v] = v; + + if ((kctl = mix->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + memset(&mix->send_volume, 0, sizeof(mix->send_volume)); + mix->send_volume[0][0] = mix->send_volume[0][1] = + mix->send_volume[1][0] = mix->send_volume[2][1] = 255; + + if ((kctl = mix->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; + } + + for (idx = 0; idx < 3; idx++) { + if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = idx; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = idx; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + if (emu->audigy) { + if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + } else { + if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emumpu401.c linux/sound/pci/emu10k1/emumpu401.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emumpu401.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emumpu401.c 2002-08-15 06:13:09.000000000 -0600 @@ -0,0 +1,376 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of EMU10K1 MPU-401 in UART mode + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#define EMU10K1_MIDI_MODE_INPUT (1<<0) +#define EMU10K1_MIDI_MODE_OUTPUT (1<<1) + +static inline unsigned char mpu401_read(emu10k1_t *emu, emu10k1_midi_t *mpu, int idx) +{ + if (emu->audigy) + return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0); + else + return inb(emu->port + mpu->port + idx); +} + +static inline void mpu401_write(emu10k1_t *emu, emu10k1_midi_t *mpu, int data, int idx) +{ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data); + else + outb(data, emu->port + mpu->port + idx); +} + +#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0) +#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1) +#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0) +#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1) + +#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80)) +#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40)) + +#define MPU401_RESET 0xff +#define MPU401_ENTER_UART 0x3f +#define MPU401_ACK 0xfe + +static void mpu401_clear_rx(emu10k1_t *emu, emu10k1_midi_t *mpu) +{ + int timeout = 100000; + for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--) + mpu401_read_data(emu, mpu); +#ifdef CONFIG_SND_DEBUG + if (timeout <= 0) + snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu)); +#endif +} + +/* + + */ + +static void do_emu10k1_midi_interrupt(emu10k1_t *emu, emu10k1_midi_t *midi, unsigned int status) +{ + unsigned char byte; + + if (midi->rmidi == NULL) { + snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable); + return; + } + + spin_lock(&midi->input_lock); + if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) { + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + mpu401_clear_rx(emu, midi); + } else { + byte = mpu401_read_data(emu, midi); + spin_unlock(&midi->input_lock); + if (midi->substream_input) + snd_rawmidi_receive(midi->substream_input, &byte, 1); + spin_lock(&midi->input_lock); + } + } + spin_unlock(&midi->input_lock); + + spin_lock(&midi->output_lock); + if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) { + if (midi->substream_output && + snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { + mpu401_write_data(emu, midi, byte); + } else { + snd_emu10k1_intr_disable(emu, midi->tx_enable); + } + } + spin_unlock(&midi->output_lock); +} + +static void snd_emu10k1_midi_interrupt(emu10k1_t *emu, unsigned int status) +{ + do_emu10k1_midi_interrupt(emu, &emu->midi, status); +} + +static void snd_emu10k1_midi_interrupt2(emu10k1_t *emu, unsigned int status) +{ + do_emu10k1_midi_interrupt(emu, &emu->midi2, status); +} + +static void snd_emu10k1_midi_cmd(emu10k1_t * emu, emu10k1_midi_t *midi, unsigned char cmd, int ack) +{ + unsigned long flags; + int timeout, ok; + + spin_lock_irqsave(&midi->input_lock, flags); + mpu401_write_data(emu, midi, 0x00); + /* mpu401_clear_rx(emu, midi); */ + + mpu401_write_cmd(emu, midi, cmd); + if (ack) { + ok = 0; + timeout = 10000; + while (!ok && timeout-- > 0) { + if (mpu401_input_avail(emu, midi)) { + if (mpu401_read_data(emu, midi) == MPU401_ACK) + ok = 1; + } + } + if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK) + ok = 1; + } else { + ok = 1; + } + spin_unlock_irqrestore(&midi->input_lock, flags); + if (!ok) + snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", + cmd, emu->port, + mpu401_read_stat(emu, midi), + mpu401_read_data(emu, midi)); +} + +static int snd_emu10k1_midi_input_open(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT; + midi->substream_input = substream; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1); + snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_output_open(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT; + midi->substream_output = substream; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1); + snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_input_close(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + snd_emu10k1_intr_disable(emu, midi->rx_enable); + midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT; + midi->substream_input = NULL; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_output_close(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + snd_emu10k1_intr_disable(emu, midi->tx_enable); + midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT; + midi->substream_output = NULL; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static void snd_emu10k1_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + emu = midi->emu; + snd_assert(emu, return); + + if (up) + snd_emu10k1_intr_enable(emu, midi->rx_enable); + else + snd_emu10k1_intr_disable(emu, midi->rx_enable); +} + +static void snd_emu10k1_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return); + + if (up) { + int max = 4; + unsigned char byte; + + /* try to send some amount of bytes here before interrupts */ + spin_lock_irqsave(&midi->output_lock, flags); + while (max > 0) { + if (mpu401_output_ready(emu, midi)) { + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) || + snd_rawmidi_transmit(substream, &byte, 1) != 1) { + /* no more data */ + spin_unlock_irqrestore(&midi->output_lock, flags); + return; + } + mpu401_write_data(emu, midi, byte); + max--; + } else { + break; + } + } + spin_unlock_irqrestore(&midi->output_lock, flags); + snd_emu10k1_intr_enable(emu, midi->tx_enable); + } else { + snd_emu10k1_intr_disable(emu, midi->tx_enable); + } +} + +/* + + */ + +static snd_rawmidi_ops_t snd_emu10k1_midi_output = +{ + .open = snd_emu10k1_midi_output_open, + .close = snd_emu10k1_midi_output_close, + .trigger = snd_emu10k1_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_emu10k1_midi_input = +{ + .open = snd_emu10k1_midi_input_open, + .close = snd_emu10k1_midi_input_close, + .trigger = snd_emu10k1_midi_input_trigger, +}; + +static void snd_emu10k1_midi_free(snd_rawmidi_t *rmidi) +{ + emu10k1_midi_t *midi = (emu10k1_midi_t *)rmidi->private_data; + midi->interrupt = NULL; + midi->rmidi = NULL; +} + +static int __devinit emu10k1_midi_init(emu10k1_t *emu, emu10k1_midi_t *midi, int device, char *name) +{ + snd_rawmidi_t *rmidi; + int err; + + if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0) + return err; + midi->emu = emu; + spin_lock_init(&midi->open_lock); + spin_lock_init(&midi->input_lock); + spin_lock_init(&midi->output_lock); + strcpy(rmidi->name, name); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + rmidi->private_free = snd_emu10k1_midi_free; + midi->rmidi = rmidi; + return 0; +} + +int __devinit snd_emu10k1_midi(emu10k1_t *emu) +{ + emu10k1_midi_t *midi = &emu->midi; + int err; + + if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0) + return err; + + midi->tx_enable = INTE_MIDITXENABLE; + midi->rx_enable = INTE_MIDIRXENABLE; + midi->port = MUDATA; + midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; + midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; + midi->interrupt = snd_emu10k1_midi_interrupt; + return 0; +} + +int __devinit snd_emu10k1_audigy_midi(emu10k1_t *emu) +{ + emu10k1_midi_t *midi; + int err; + + midi = &emu->midi; + if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0) + return err; + + midi->tx_enable = INTE_MIDITXENABLE; + midi->rx_enable = INTE_MIDIRXENABLE; + midi->port = A_MUDATA1; + midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; + midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; + midi->interrupt = snd_emu10k1_midi_interrupt; + + midi = &emu->midi2; + if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0) + return err; + + midi->tx_enable = INTE_A_MIDITXENABLE2; + midi->rx_enable = INTE_A_MIDIRXENABLE2; + midi->port = A_MUDATA2; + midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2; + midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2; + midi->interrupt = snd_emu10k1_midi_interrupt2; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emupcm.c linux/sound/pci/emu10k1/emupcm.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emupcm.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emupcm.c 2003-02-28 07:29:26.000000000 -0700 @@ -0,0 +1,1157 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / PCM routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice) +{ + emu10k1_pcm_t *epcm; + + if ((epcm = voice->epcm) == NULL) + return; + if (epcm->substream == NULL) + return; +#if 0 + printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", + epcm->substream->runtime->hw->pointer(emu, epcm->substream), + snd_pcm_lib_period_bytes(epcm->substream), + snd_pcm_lib_buffer_bytes(epcm->substream)); +#endif + snd_pcm_period_elapsed(epcm->substream); +} + +static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_ADCBUFHALFFULL) { + if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_substream); +} + +static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_MICBUFHALFFULL) { + if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_mic_substream); +} + +static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_EFXBUFHALFFULL) { + if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); +} + +static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices) +{ + int err; + + if (epcm->voices[1] != NULL && voices < 2) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + } + if (voices == 1 && epcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && epcm->voices[0] != NULL && epcm->voices[1] != NULL) + return 0; + if (voices > 1) { + if (epcm->voices[0] != NULL && epcm->voices[1] == NULL) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + } + } + err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices > 1, &epcm->voices[0]); + if (err < 0) + return err; + epcm->voices[0]->epcm = epcm; + if (voices > 1) { + epcm->voices[1] = &epcm->emu->voices[epcm->voices[0]->number + 1]; + epcm->voices[1]->epcm = epcm; + } + if (epcm->extra == NULL) { + err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, 0, &epcm->extra); + if (err < 0) { + // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + if (epcm->voices[1]) + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + return err; + } + epcm->extra->epcm = epcm; + epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; + } + return 0; +} + +static unsigned int capture_period_sizes[31] = { + 384, 448, 512, 640, + 384*2, 448*2, 512*2, 640*2, + 384*4, 448*4, 512*4, 640*4, + 384*8, 448*8, 512*8, 640*8, + 384*16, 448*16, 512*16, 640*16, + 384*32, 448*32, 512*32, 640*32, + 384*64, 448*64, 512*64, 640*64, + 384*128,448*128,512*128 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = { + .count = 31, + .list = capture_period_sizes, + .mask = 0 +}; + +static unsigned int capture_rates[8] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = { + .count = 8, + .list = capture_rates, + .mask = 0 +}; + +static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) +{ + switch (rate) { + case 8000: return ADCCR_SAMPLERATE_8; + case 11025: return ADCCR_SAMPLERATE_11; + case 16000: return ADCCR_SAMPLERATE_16; + case 22050: return ADCCR_SAMPLERATE_22; + case 24000: return ADCCR_SAMPLERATE_24; + case 32000: return ADCCR_SAMPLERATE_32; + case 44100: return ADCCR_SAMPLERATE_44; + case 48000: return ADCCR_SAMPLERATE_48; + default: + snd_BUG(); + return ADCCR_SAMPLERATE_8; + } +} + +static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) +{ + switch (rate) { + case 8000: return A_ADCCR_SAMPLERATE_8; + case 11025: return A_ADCCR_SAMPLERATE_11; + case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */ + case 16000: return ADCCR_SAMPLERATE_16; + case 22050: return ADCCR_SAMPLERATE_22; + case 24000: return ADCCR_SAMPLERATE_24; + case 32000: return ADCCR_SAMPLERATE_32; + case 44100: return ADCCR_SAMPLERATE_44; + case 48000: return ADCCR_SAMPLERATE_48; + default: + snd_BUG(); + return A_ADCCR_SAMPLERATE_8; + } +} + +static unsigned int emu10k1_calc_pitch_target(unsigned int rate) +{ + unsigned int pitch_target; + + pitch_target = (rate << 8) / 375; + pitch_target = (pitch_target >> 1) + (pitch_target & 1); + return pitch_target; +} + +#define PITCH_48000 0x00004000 +#define PITCH_96000 0x00008000 +#define PITCH_85000 0x00007155 +#define PITCH_80726 0x00006ba2 +#define PITCH_67882 0x00005a82 +#define PITCH_57081 0x00004c1c + +static unsigned int emu10k1_select_interprom(unsigned int pitch_target) +{ + if (pitch_target == PITCH_48000) + return CCCA_INTERPROM_0; + else if (pitch_target < PITCH_48000) + return CCCA_INTERPROM_1; + else if (pitch_target >= PITCH_96000) + return CCCA_INTERPROM_0; + else if (pitch_target >= PITCH_85000) + return CCCA_INTERPROM_6; + else if (pitch_target >= PITCH_80726) + return CCCA_INTERPROM_5; + else if (pitch_target >= PITCH_67882) + return CCCA_INTERPROM_4; + else if (pitch_target >= PITCH_57081) + return CCCA_INTERPROM_3; + else + return CCCA_INTERPROM_2; +} + + +static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, + int master, int extra, + emu10k1_voice_t *evoice, + unsigned int start_addr, + unsigned int end_addr) +{ + snd_pcm_substream_t *substream = evoice->epcm->substream; + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; + unsigned int silent_page, tmp; + int voice, stereo, w_16; + unsigned char attn, send_amount[8]; + unsigned char send_routing[8]; + unsigned long flags; + unsigned int pitch_target; + + voice = evoice->number; + stereo = runtime->channels == 2; + w_16 = snd_pcm_format_width(runtime->format) == 16; + + if (!extra && stereo) { + start_addr >>= 1; + end_addr >>= 1; + } + if (w_16) { + start_addr >>= 1; + end_addr >>= 1; + } + + spin_lock_irqsave(&emu->reg_lock, flags); + + /* volume parameters */ + if (extra) { + attn = 0; + memset(send_routing, 0, sizeof(send_routing)); + send_routing[0] = 0; + send_routing[1] = 1; + send_routing[2] = 2; + send_routing[3] = 3; + memset(send_amount, 0, sizeof(send_amount)); + } else { + tmp = stereo ? (master ? 1 : 2) : 0; + memcpy(send_routing, &mix->send_routing[tmp][0], 8); + memcpy(send_amount, &mix->send_volume[tmp][0], 8); + } + + if (master) { + unsigned int ccis = stereo ? 28 : 30; + if (w_16) + ccis *= 2; + evoice->epcm->ccca_start_addr = start_addr + ccis; + if (extra) { + start_addr += ccis; + end_addr += ccis; + } + if (stereo && !extra) { + snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); + snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); + } else { + snd_emu10k1_ptr_write(emu, CPF, voice, 0); + } + } + + // setup routing + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, + ((unsigned int)send_routing[3] << 24) | + ((unsigned int)send_routing[2] << 16) | + ((unsigned int)send_routing[1] << 8) | + (unsigned int)send_routing[0]); + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, + ((unsigned int)send_routing[7] << 24) | + ((unsigned int)send_routing[6] << 16) | + ((unsigned int)send_routing[5] << 8) | + (unsigned int)send_routing[4]); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, + ((unsigned int)send_amount[4] << 24) | + ((unsigned int)send_amount[5] << 16) | + ((unsigned int)send_amount[6] << 8) | + (unsigned int)send_amount[7]); + } else + snd_emu10k1_ptr_write(emu, FXRT, voice, + snd_emu10k1_compose_send_routing(send_routing)); + // Stop CA + // Assumption that PT is already 0 so no harm overwriting + snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); + snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); + snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); + pitch_target = emu10k1_calc_pitch_target(runtime->rate); + snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr | + emu10k1_select_interprom(pitch_target) | + (w_16 ? 0 : CCCA_8BITSELECT)); + // Clear filter delay memory + snd_emu10k1_ptr_write(emu, Z1, voice, 0); + snd_emu10k1_ptr_write(emu, Z2, voice, 0); + // invalidate maps + silent_page = ((unsigned int)emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); + // modulation envelope + snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); + snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); + snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f); + snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000); + snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000); + snd_emu10k1_ptr_write(emu, FMMOD, voice, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); + snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); + // volume envelope + snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); + snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); + // filter envelope + snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); + // pitch envelope + snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); + + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) + return err; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + snd_util_memblk_t *memblk; + if (epcm->memblk != NULL) + snd_emu10k1_free_pages(emu, epcm->memblk); + memblk = snd_emu10k1_alloc_pages(emu, substream); + if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) { + epcm->start_addr = 0; + return -ENOMEM; + } + epcm->start_addr = ((emu10k1_memblk_t *)memblk)->mapped_page << PAGE_SHIFT; + } + return 0; +} + +static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm; + + if (runtime->private_data == NULL) + return 0; + epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + if (epcm->extra) { + snd_emu10k1_voice_free(epcm->emu, epcm->extra); + epcm->extra = NULL; + } + if (epcm->voices[1]) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + } + if (epcm->voices[0]) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + } + if (epcm->memblk) { + snd_emu10k1_free_pages(emu, epcm->memblk); + epcm->memblk = NULL; + epcm->start_addr = 0; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int start_addr, end_addr; + + start_addr = epcm->start_addr; + end_addr = snd_pcm_lib_period_bytes(substream); + if (runtime->channels == 2) + end_addr >>= 1; + end_addr += start_addr; + snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, + start_addr, end_addr); + end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); + snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], + start_addr, end_addr); + if (epcm->voices[1]) + snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], + start_addr, end_addr); + return 0; +} + +static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + int idx; + + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + break; + default: + break; + } + snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); + epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); + epcm->capture_bs_val = 0; + for (idx = 0; idx < 31; idx++) { + if (capture_period_sizes[idx] == epcm->capture_bufsize) { + epcm->capture_bs_val = idx + 1; + break; + } + } + if (epcm->capture_bs_val == 0) { + snd_BUG(); + epcm->capture_bs_val++; + } + if (epcm->type == CAPTURE_AC97ADC) { + epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; + if (runtime->channels > 1) + epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; + epcm->capture_cr_val |= emu->audigy ? + snd_emu10k1_audigy_capture_rate_reg(runtime->rate) : + snd_emu10k1_capture_rate_reg(runtime->rate); + } + return 0; +} + +static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice) +{ + snd_pcm_runtime_t *runtime; + unsigned int voice, i, ccis, cra = 64, cs, sample; + + if (evoice == NULL) + return; + runtime = evoice->epcm->substream->runtime; + voice = evoice->number; + sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; + if (runtime->channels > 1) { + ccis = 28; + cs = 4; + } else { + ccis = 30; + cs = 2; + } + if (sample == 0) /* 16-bit */ + ccis *= 2; + for (i = 0; i < cs; i++) + snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); + // reset cache + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); + snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); + if (runtime->channels > 1) { + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); + snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); + } + // fill cache + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); +} + +static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + emu10k1_pcm_mixer_t *mix; + unsigned int voice, pitch, pitch_target, tmp; + unsigned int attn; + + if (evoice == NULL) /* skip second voice for mono */ + return; + substream = evoice->epcm->substream; + runtime = substream->runtime; + mix = &emu->pcm_mixer[substream->number]; + voice = evoice->number; + pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; + pitch_target = emu10k1_calc_pitch_target(runtime->rate); + attn = extra ? 0 : 0x00ff; + tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; + snd_emu10k1_ptr_write(emu, IFATN, voice, attn); + snd_emu10k1_ptr_write(emu, VTFT, voice, (mix->attn[tmp] << 16) | 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, voice, (mix->attn[tmp] << 16) | 0xffff); + snd_emu10k1_voice_clear_loop_stop(emu, voice); + if (extra) + snd_emu10k1_voice_intr_enable(emu, voice); + snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); + snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); + if (master) + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); + snd_emu10k1_ptr_write(emu, IP, voice, pitch); +} + +static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice) +{ + unsigned int voice; + + if (evoice == NULL) + return; + voice = evoice->number; + snd_emu10k1_voice_intr_disable(emu, voice); + snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); + snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff); + snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); + snd_emu10k1_ptr_write(emu, IP, voice, 0); +} + +static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned long flags; + int result = 0; + + // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */ + snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]); + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0); + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0); + snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); + epcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + epcm->running = 0; + snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); + snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); + snd_emu10k1_playback_stop_voice(emu, epcm->extra); + break; + default: + result = -EINVAL; + break; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned long flags; + int result = 0; + + // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream)); + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + outl(epcm->capture_ipr, emu->port + IPR); + snd_emu10k1_intr_enable(emu, epcm->capture_inte); + // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); + break; + default: + break; + } + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); + epcm->running = 1; + epcm->first_ptr = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + epcm->running = 0; + snd_emu10k1_intr_disable(emu, epcm->capture_inte); + outl(epcm->capture_ipr, emu->port + IPR); + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + break; + default: + break; + } + break; + default: + result = -EINVAL; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int ptr; + + if (!epcm->running) + return 0; + ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; +#if 0 /* Perex's code */ + ptr += runtime->buffer_size; + ptr -= epcm->ccca_start_addr; + ptr %= runtime->buffer_size; +#else /* EMU10K1 Open Source code from Creative */ + if (ptr < epcm->ccca_start_addr) + ptr += runtime->buffer_size - epcm->ccca_start_addr; + else + ptr -= epcm->ccca_start_addr; +#endif + // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); + return ptr; +} + +static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int ptr; + + if (!epcm->running) + return 0; + if (epcm->first_ptr) { + udelay(50); // hack, it takes awhile until capture is started + epcm->first_ptr = 0; + } + ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; + return bytes_to_frames(runtime, ptr); +} + +/* + * Playback support device description + */ + +static snd_pcm_hardware_t snd_emu10k1_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_emu10k1_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (64*1024), + .period_bytes_min = 384, + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +/* + * + */ + +static void snd_emu10k1_pcm_mixer_notify1(snd_card_t *card, snd_kcontrol_t *kctl, int activate) +{ + snd_runtime_check(kctl != NULL, return); + if (activate) + kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); +} + +static void snd_emu10k1_pcm_mixer_notify(snd_card_t *card, emu10k1_pcm_mixer_t *mix, int activate) +{ + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_routing, activate); + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_volume, activate); + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_attn, activate); +} + +static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return); + + if (epcm) + snd_magic_kfree(epcm); +} + +static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + emu10k1_pcm_mixer_t *mix; + snd_pcm_runtime_t *runtime = substream->runtime; + int i, err; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = PLAYBACK_EMUVOICE; + epcm->substream = substream; + runtime->private_data = epcm; + runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_playback; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { + snd_magic_kfree(epcm); + return err; + } + if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { + snd_magic_kfree(epcm); + return err; + } + mix = &emu->pcm_mixer[substream->number]; + for (i = 0; i < 4; i++) + mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; + memset(&mix->send_volume, 0, sizeof(mix->send_volume)); + mix->send_volume[0][0] = mix->send_volume[0][1] = + mix->send_volume[1][0] = mix->send_volume[2][1] = 255; + mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; + mix->epcm = epcm; + snd_emu10k1_pcm_mixer_notify(emu->card, mix, 1); + return 0; +} + +static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; + + mix->epcm = NULL; + snd_emu10k1_pcm_mixer_notify(emu->card, mix, 0); + return 0; +} + +static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_AC97ADC; + epcm->substream = substream; + epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; + epcm->capture_inte = INTE_ADCBUFENABLE; + epcm->capture_ba_reg = ADCBA; + epcm->capture_bs_reg = ADCBS; + epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; + runtime->private_data = epcm; + runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; + emu->pcm_capture_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); + return 0; +} + +static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_substream = NULL; + return 0; +} + +static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_AC97MIC; + epcm->substream = substream; + epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; + epcm->capture_inte = INTE_MICBUFENABLE; + epcm->capture_ba_reg = MICBA; + epcm->capture_bs_reg = MICBS; + epcm->capture_idx_reg = MICIDX; + substream->runtime->private_data = epcm; + substream->runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + runtime->hw.rates = SNDRV_PCM_RATE_8000; + runtime->hw.rate_min = runtime->hw.rate_max = 8000; + runtime->hw.channels_min = 1; + emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; + emu->pcm_capture_mic_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + return 0; +} + +static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_mic_substream = NULL; + return 0; +} + +static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + int nefx = emu->audigy ? 64 : 32; + int idx; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_EFX; + epcm->substream = substream; + epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; + epcm->capture_inte = INTE_EFXBUFENABLE; + epcm->capture_ba_reg = FXBA; + epcm->capture_bs_reg = FXBS; + epcm->capture_idx_reg = FXIDX; + substream->runtime->private_data = epcm; + substream->runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + runtime->hw.rates = SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = runtime->hw.rate_max = 48000; + spin_lock_irqsave(&emu->reg_lock, flags); + runtime->hw.channels_min = runtime->hw.channels_max = 0; + for (idx = 0; idx < nefx; idx++) { + if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { + runtime->hw.channels_min++; + runtime->hw.channels_max++; + } + } + epcm->capture_cr_val = emu->efx_voices_mask[0]; + epcm->capture_cr_val2 = emu->efx_voices_mask[1]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; + emu->pcm_capture_efx_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + return 0; +} + +static int snd_emu10k1_capture_efx_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_efx_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_playback_ops = { + .open = snd_emu10k1_playback_open, + .close = snd_emu10k1_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_emu10k1_playback_hw_params, + .hw_free = snd_emu10k1_playback_hw_free, + .prepare = snd_emu10k1_playback_prepare, + .trigger = snd_emu10k1_playback_trigger, + .pointer = snd_emu10k1_playback_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static snd_pcm_ops_t snd_emu10k1_capture_ops = { + .open = snd_emu10k1_capture_open, + .close = snd_emu10k1_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_emu10k1_capture_hw_params, + .hw_free = snd_emu10k1_capture_hw_free, + .prepare = snd_emu10k1_capture_prepare, + .trigger = snd_emu10k1_capture_trigger, + .pointer = snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "EMU10K1"); + emu->pcm = pcm; + + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_sg_pages(emu->pci, substream, 64*1024, 64*1024)) < 0) + return err; + + for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_pci_pages(emu->pci, substream, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = { + .open = snd_emu10k1_capture_mic_open, + .close = snd_emu10k1_capture_mic_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_emu10k1_capture_hw_params, + .hw_free = snd_emu10k1_capture_hw_free, + .prepare = snd_emu10k1_capture_prepare, + .trigger = snd_emu10k1_capture_trigger, + .pointer = snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_mic = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_mic_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 MIC"); + emu->pcm_mic = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int nefx = emu->audigy ? 64 : 32; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = nefx; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int nefx = emu->audigy ? 64 : 32; + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < nefx; idx++) + ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval[2], bits; + int nefx = emu->audigy ? 64 : 32; + int change, idx; + + nval[0] = nval[1] = 0; + for (idx = 0, bits = 0; idx < nefx; idx++) + if (ucontrol->value.integer.value[idx]) { + nval[idx / 32] |= 1 << (idx % 32); + bits++; + } + if (bits != 1 && bits != 2 && bits != 4 && bits != 8) + return -EINVAL; + spin_lock_irqsave(&emu->reg_lock, flags); + change = (nval[0] != emu->efx_voices_mask[0]) || + (nval[1] != emu->efx_voices_mask[1]); + emu->efx_voices_mask[0] = nval[0]; + emu->efx_voices_mask[1] = nval[1]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "EFX voices mask", + .info = snd_emu10k1_pcm_efx_voices_mask_info, + .get = snd_emu10k1_pcm_efx_voices_mask_get, + .put = snd_emu10k1_pcm_efx_voices_mask_put +}; + +static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = { + .open = snd_emu10k1_capture_efx_open, + .close = snd_emu10k1_capture_efx_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_emu10k1_capture_hw_params, + .hw_free = snd_emu10k1_capture_hw_free, + .prepare = snd_emu10k1_capture_prepare, + .trigger = snd_emu10k1_capture_trigger, + .pointer = snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_efx = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 0, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_efx_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 EFX"); + emu->pcm_efx = pcm; + if (rpcm) + *rpcm = pcm; + + emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; + emu->efx_voices_mask[1] = 0; + snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu)); + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/emuproc.c linux/sound/pci/emu10k1/emuproc.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/emuproc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/emuproc.c 2003-03-01 12:03:22.000000000 -0700 @@ -0,0 +1,281 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / proc interface routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu, + snd_info_buffer_t * buffer, + char *title, + int status_reg, + int rate_reg) +{ + static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" }; + static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" }; + static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" }; + unsigned int status, rate = 0; + + status = snd_emu10k1_ptr_read(emu, status_reg, 0); + if (rate_reg > 0) + rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); + + snd_iprintf(buffer, "\n%s\n", title); + + snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); + snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); + snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); + snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); + snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); + snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); + snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy"); + snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16); + snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]); + snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]); + snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]); + + if (rate_reg > 0) { + snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); + snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE); + } +} + +static void snd_emu10k1_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + static char *outputs[32] = { + /* 00 */ "PCM Left", + /* 01 */ "PCM Right", + /* 02 */ "PCM Surround Left", + /* 03 */ "PCM Surround Right", + /* 04 */ "MIDI Left", + /* 05 */ "MIDI Right", + /* 06 */ "PCM Center", + /* 07 */ "PCM LFE", + /* 08 */ "???", + /* 09 */ "???", + /* 10 */ "???", + /* 11 */ "???", + /* 12 */ "MIDI Reverb", + /* 13 */ "MIDI Chorus", + /* 14 */ "???", + /* 15 */ "???", + /* 16 */ "???", + /* 17 */ "???", + /* 18 */ "ADC Left / CDROM S/PDIF Left", + /* 19 */ "ADC Right / CDROM S/PDIF Right", + /* 20 */ "MIC / Zoom Video Left", + /* 21 */ "Zoom Video Right", + /* 22 */ "S/PDIF Left", + /* 23 */ "S/PDIF Right", + /* 24 */ "???", + /* 25 */ "???", + /* 26 */ "???", + /* 27 */ "???", + /* 28 */ "???", + /* 29 */ "???", + /* 30 */ "???", + /* 31 */ "???" + }; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + unsigned int val; + int nefx = emu->audigy ? 64 : 32; + int idx; + + snd_iprintf(buffer, "EMU10K1\n\n"); + val = emu->audigy ? + snd_emu10k1_ptr_read(emu, A_FXRT1, 0) : + snd_emu10k1_ptr_read(emu, FXRT, 0); + snd_iprintf(buffer, "Card : %s\n", + emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); + snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); + snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_size); + snd_iprintf(buffer, "\n"); + if (emu->audigy) { + snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", + val & 0x3f, + (val >> 8) & 0x3f, + (val >> 16) & 0x3f, + (val >> 24) & 0x3f); + } else { + snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", + (val >> 16) & 0x0f, + (val >> 20) & 0x0f, + (val >> 24) & 0x0f, + (val >> 28) & 0x0f); + } + snd_iprintf(buffer, "\nCaptured FX Outputs :\n"); + for (idx = 0; idx < nefx; idx++) { + if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx%32]); + } + snd_iprintf(buffer, "\nAll FX Outputs :\n"); + for (idx = 0; idx < 32; idx++) + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS); + snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS); + val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); + snd_iprintf(buffer, "\nZoomed Video\n"); + snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE); +} + +static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + u32 pc; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + + snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name); + snd_iprintf(buffer, " Code dump :\n"); + for (pc = 0; pc < 512; pc++) { + u32 low, high; + + low = snd_emu10k1_efx_read(emu, pc * 2); + high = snd_emu10k1_efx_read(emu, pc * 2 + 1); + if (emu->audigy) + snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n", + (high >> 24) & 0x0f, + (high >> 12) & 0x7ff, + (high >> 0) & 0x7ff, + (low >> 12) & 0x7ff, + (low >> 0) & 0x7ff, + pc, + high, low); + else + snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n", + (high >> 20) & 0x0f, + (high >> 10) & 0x3ff, + (high >> 0) & 0x3ff, + (low >> 10) & 0x3ff, + (low >> 0) & 0x3ff, + pc, + high, low); + } +} + +#define TOTAL_SIZE_GPR (0x100*4) +#define TOTAL_SIZE_TANKMEM_DATA (0xa0*4) +#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4) +#define TOTAL_SIZE_CODE (0x200*8) + +static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return -ENXIO); + unsigned int offset; + + if (!strcmp(entry->name, "fx8010_tram_addr")) { + if (emu->audigy) return -EINVAL; + offset = TANKMEMADDRREGBASE; + } else if (!strcmp(entry->name, "fx8010_tram_data")) { + if (emu->audigy) return -EINVAL; + offset = TANKMEMDATAREGBASE; + } else if (!strcmp(entry->name, "fx8010_code")) { + offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + } else { + offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE; + } + size = count; + if (file->f_pos + size > entry->size) + size = (long)entry->size - file->f_pos; + if (size > 0) { + unsigned int *tmp; + long res; + unsigned int idx; + if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL) + return -ENOMEM; + for (idx = 0; idx < ((file->f_pos & 3) + size + 3) >> 2; idx++) + tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (file->f_pos >> 2), 0); + if (copy_to_user(buf, ((char *)tmp) + (file->f_pos & 3), size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = { + .read = snd_emu10k1_fx8010_read, +}; + +int __devinit snd_emu10k1_proc_init(emu10k1_t * emu) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read); + + if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->size = TOTAL_SIZE_GPR; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + } + if (!emu->audigy && ! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->size = TOTAL_SIZE_TANKMEM_DATA; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + } + if (!emu->audigy && ! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->size = TOTAL_SIZE_TANKMEM_ADDR; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + } + if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->size = TOTAL_SIZE_CODE; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + } + if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->c.text.read_size = 64*1024; + entry->c.text.read = snd_emu10k1_proc_acode_read; + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/io.c linux/sound/pci/emu10k1/io.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/io.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/io.c 2002-08-12 02:43:47.000000000 -0600 @@ -0,0 +1,340 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn) +{ + unsigned long flags; + unsigned int regptr, val; + unsigned int mask; + + mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; + regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + unsigned char size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; + } +} + +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data) +{ + unsigned int regptr; + unsigned long flags; + unsigned int mask; + + mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; + regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + unsigned char size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + data |= inl(emu->port + DATA) & ~mask; + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + } else { + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + } +} + +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) | intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) & ~intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIEH << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val |= 1 << (voicenum - 32); + } else { + outl(CLIEL << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val |= 1 << voicenum; + } + outl(val, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIEH << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val &= ~(1 << (voicenum - 32)); + } else { + outl(CLIEL << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val &= ~(1 << voicenum); + } + outl(val, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIPH << 16, emu->port + PTR); + voicenum = 1 << (voicenum - 32); + } else { + outl(CLIPL << 16, emu->port + PTR); + voicenum = 1 << voicenum; + } + outl(voicenum, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int sol; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(SOLEH << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol |= 1 << (voicenum - 32); + } else { + outl(SOLEL << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol |= 1 << voicenum; + } + outl(sol, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int sol; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(SOLEH << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol &= ~(1 << (voicenum - 32)); + } else { + outl(SOLEL << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol &= ~(1 << voicenum); + } + outl(sol, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait) +{ + volatile unsigned count; + unsigned int newtime = 0, curtime; + + curtime = inl(emu->port + WC) >> 6; + while (wait-- > 0) { + count = 0; + while (count++ < 16384) { + newtime = inl(emu->port + WC) >> 6; + if (newtime != curtime) + break; + } + if (count >= 16384) + break; + curtime = newtime; + } +} + +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return -ENXIO); + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + val = inw(emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; +} + +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + outw(data, emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +/* + * convert rate to pitch + */ + +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate) +{ + static u32 logMagTable[128] = { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, + 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, + 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, + 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, + 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, + 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, + 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, + 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, + 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, + 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, + 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, + 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, + 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, + 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df + }; + static char logSlopeTable[128] = { + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f + }; + int i; + + if (rate == 0) + return 0; /* Bail out if no leading "1" */ + rate *= 11185; /* Scale 48000 to 0x20002380 */ + for (i = 31; i > 0; i--) { + if (rate & 0x80000000) { /* Detect leading "1" */ + return (((unsigned int) (i - 15) << 20) + + logMagTable[0x7f & (rate >> 24)] + + (0x7f & (rate >> 17)) * + logSlopeTable[0x7f & (rate >> 24)]); + } + rate <<= 1; + } + + return 0; /* Should never reach this point */ +} + +/* + * Returns an attenuation based upon a cumulative volume value + * Algorithm calculates 0x200 - 0x10 log2 (input) + */ + +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value) +{ + unsigned short count = 16, ans; + + if (value == 0) + return 0xFF; + + /* Find first SET bit. This is the integer part of the value */ + while ((value & 0x10000) == 0) { + value <<= 1; + count--; + } + + /* The REST of the data is the fractional part. */ + ans = (unsigned short) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); + if (ans > 0xFF) + ans = 0xFF; + + return (unsigned char) ans; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/irq.c linux/sound/pci/emu10k1/irq.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/irq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/irq.c 2002-08-12 02:43:47.000000000 -0600 @@ -0,0 +1,148 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for IRQ control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, dev_id, return); + unsigned int status; + + while ((status = inl(emu->port + IPR)) != 0) { + // printk("irq - status = 0x%x\n", status); + if (status & IPR_PCIERROR) { + snd_printk("interrupt: PCI error\n"); + snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); + } + if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { + if (emu->hwvol_interrupt) + emu->hwvol_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); + outl(status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE), emu->port + IPR); + } + if (status & IPR_CHANNELLOOP) { + int voice; + int voice_max = status & IPR_CHANNELNUMBERMASK; + int voice_max_l; + u32 val; + emu10k1_voice_t *pvoice = emu->voices; + + val = snd_emu10k1_ptr_read(emu, CLIPL, 0); + voice_max_l = voice_max; + if (voice_max_l >= 0x20) + voice_max_l = 0x1f; + for (voice = 0; voice <= voice_max_l; voice++) { + if (val & 1) { + if (pvoice->use && pvoice->interrupt != NULL) { + pvoice->interrupt(emu, pvoice); + snd_emu10k1_voice_intr_ack(emu, voice); + } else { + snd_emu10k1_voice_intr_disable(emu, voice); + } + } + val >>= 1; + pvoice++; + } + if (voice_max > 0x1f) { + val = snd_emu10k1_ptr_read(emu, CLIPH, 0); + for (; voice <= voice_max; voice++) { + if(val & 1) { + if (pvoice->use && pvoice->interrupt != NULL) { + pvoice->interrupt(emu, pvoice); + snd_emu10k1_voice_intr_ack(emu, voice); + } else { + snd_emu10k1_voice_intr_disable(emu, voice); + } + } + val >>= 1; + pvoice++; + } + } + outl(IPR_CHANNELLOOP | (status & IPR_CHANNELNUMBERMASK), emu->port + IPR); + } + if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { + if (emu->capture_interrupt) + emu->capture_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); + outl(status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { + if (emu->capture_mic_interrupt) + emu->capture_mic_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); + outl(status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { + if (emu->capture_efx_interrupt) + emu->capture_efx_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); + outl(status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { + if (emu->midi.interrupt) + emu->midi.interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); + outl(status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY), emu->port + IPR); + } + if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { + if (emu->midi2.interrupt) + emu->midi2.interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); + outl(status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2), emu->port + IPR); + } + if (status & IPR_INTERVALTIMER) { + if (emu->timer_interrupt) + emu->timer_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); + outl(IPR_INTERVALTIMER, emu->port + IPR); + } + if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { + if (emu->spdif_interrupt) + emu->spdif_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); + outl(status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE), emu->port + IPR); + } + if (status & IPR_FXDSP) { + if (emu->dsp_interrupt) + emu->dsp_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); + outl(IPR_FXDSP, emu->port + IPR); + } + } +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/memory.c linux/sound/pci/emu10k1/memory.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/memory.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/memory.c 2003-03-10 05:54:47.000000000 -0700 @@ -0,0 +1,555 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Takashi Iwai + * + * EMU10K1 memory page allocation (PTB area) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +/* page arguments of these two macros are Emu page (4096 bytes), not like + * aligned pages in others + */ +#define __set_ptb_entry(emu,page,addr) \ + ((emu)->ptb_pages[page] = cpu_to_le32(((addr) << 1) | (page))) + +#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE) +#define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES) +/* get aligned page from offset address */ +#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT) +/* get offset address from aligned page */ +#define aligned_page_offset(page) ((page) << PAGE_SHIFT) + +#if PAGE_SIZE == 4096 +/* page size == EMUPAGESIZE */ +/* fill PTB entrie(s) corresponding to page with addr */ +#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr) +/* fill PTB entrie(s) corresponding to page with silence pointer */ +#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page_dmaaddr) +#else +/* fill PTB entries -- we need to fill UNIT_PAGES entries */ +static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) { + __set_ptb_entry(emu, page, addr); + addr += EMUPAGESIZE; + } +} +static inline void set_silent_ptb(emu10k1_t *emu, int page) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) + /* do not increment ptr */ + __set_ptb_entry(emu, page, emu->silent_page_dmaaddr); +} +#endif /* PAGE_SIZE */ + + +/* + */ +static int synth_alloc_pages(emu10k1_t *hw, emu10k1_memblk_t *blk); +static int synth_free_pages(emu10k1_t *hw, emu10k1_memblk_t *blk); + +#define get_emu10k1_memblk(l,member) list_entry(l, emu10k1_memblk_t, member) + + +/* initialize emu10k1 part */ +static void emu10k1_memblk_init(emu10k1_memblk_t *blk) +{ + blk->mapped_page = -1; + INIT_LIST_HEAD(&blk->mapped_link); + INIT_LIST_HEAD(&blk->mapped_order_link); + blk->map_locked = 0; + + blk->first_page = get_aligned_page(blk->mem.offset); + blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1); + blk->pages = blk->last_page - blk->first_page + 1; +} + +/* + * search empty region on PTB with the given size + * + * if an empty region is found, return the page and store the next mapped block + * in nextp + * if not found, return a negative error code. + */ +static int search_empty_map_area(emu10k1_t *emu, int npages, struct list_head **nextp) +{ + int page = 0, found_page = -ENOMEM; + int max_size = npages; + int size; + struct list_head *candidate = &emu->mapped_link_head; + struct list_head *pos; + + list_for_each (pos, &emu->mapped_link_head) { + emu10k1_memblk_t *blk = get_emu10k1_memblk(pos, mapped_link); + snd_assert(blk->mapped_page >= 0, continue); + size = blk->mapped_page - page; + if (size == npages) { + *nextp = pos; + return page; + } + else if (size > max_size) { + /* we look for the maximum empty hole */ + max_size = size; + candidate = pos; + found_page = page; + } + page = blk->mapped_page + blk->pages; + } + size = MAX_ALIGN_PAGES - page; + if (size >= max_size) { + *nextp = pos; + return page; + } + *nextp = candidate; + return found_page; +} + +/* + * map a memory block onto emu10k1's PTB + * + * call with memblk_lock held + */ +static int map_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, pg; + struct list_head *next; + + page = search_empty_map_area(emu, blk->pages, &next); + if (page < 0) /* not found */ + return page; + /* insert this block in the proper position of mapped list */ + list_add_tail(&blk->mapped_link, next); + /* append this as a newest block in order list */ + list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head); + blk->mapped_page = page; + /* fill PTB */ + for (pg = blk->first_page; pg <= blk->last_page; pg++) { + set_ptb_entry(emu, page, emu->page_addr_table[pg]); + page++; + } + return 0; +} + +/* + * unmap the block + * return the size of resultant empty pages + * + * call with memblk_lock held + */ +static int unmap_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int start_page, end_page, mpage, pg; + struct list_head *p; + emu10k1_memblk_t *q; + + /* calculate the expected size of empty region */ + if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) { + q = get_emu10k1_memblk(p, mapped_link); + start_page = q->mapped_page + q->pages; + } else + start_page = 0; + if ((p = blk->mapped_link.next) != &emu->mapped_link_head) { + q = get_emu10k1_memblk(p, mapped_link); + end_page = q->mapped_page; + } else + end_page = MAX_ALIGN_PAGES; + + /* remove links */ + list_del(&blk->mapped_link); + list_del(&blk->mapped_order_link); + /* clear PTB */ + mpage = blk->mapped_page; + for (pg = blk->first_page; pg <= blk->last_page; pg++) { + set_silent_ptb(emu, mpage); + mpage++; + } + blk->mapped_page = -1; + return end_page - start_page; /* return the new empty size */ +} + +/* + * search empty pages with the given size, and create a memory block + * + * unlike synth_alloc the memory block is aligned to the page start + */ +static emu10k1_memblk_t * +search_empty(emu10k1_t *emu, int size) +{ + struct list_head *p; + emu10k1_memblk_t *blk; + int page, psize; + + psize = get_aligned_page(size + PAGE_SIZE -1); + page = 0; + list_for_each(p, &emu->memhdr->block) { + blk = get_emu10k1_memblk(p, mem.list); + if (page + psize <= blk->first_page) + goto __found_pages; + page = blk->last_page + 1; + } + if (page + psize > emu->max_cache_pages) + return NULL; + +__found_pages: + /* create a new memory block */ + blk = (emu10k1_memblk_t *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev); + if (blk == NULL) + return NULL; + blk->mem.offset = aligned_page_offset(page); /* set aligned offset */ + emu10k1_memblk_init(blk); + return blk; +} + + +/* + * check if the given pointer is valid for pages + */ +static int is_valid_page(emu10k1_t *emu, dma_addr_t addr) +{ + if (addr & ~emu->dma_mask) { + snd_printk("max memory size is 0x%lx (addr = 0x%lx)!!\n", emu->dma_mask, (unsigned long)addr); + return 0; + } + if (addr & (EMUPAGESIZE-1)) { + snd_printk("page is not aligned\n"); + return 0; + } + return 1; +} + +/* + * map the given memory block on PTB. + * if the block is already mapped, update the link order. + * if no empty pages are found, tries to release unsed memory blocks + * and retry the mapping. + */ +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int err; + int size; + struct list_head *p, *nextp; + emu10k1_memblk_t *deleted; + unsigned long flags; + + spin_lock_irqsave(&emu->memblk_lock, flags); + if (blk->mapped_page >= 0) { + /* update order link */ + list_del(&blk->mapped_order_link); + list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head); + spin_unlock_irqrestore(&emu->memblk_lock, flags); + return 0; + } + if ((err = map_memblk(emu, blk)) < 0) { + /* no enough page - try to unmap some blocks */ + /* starting from the oldest block */ + p = emu->mapped_order_link_head.next; + for (; p != &emu->mapped_order_link_head; p = nextp) { + nextp = p->next; + deleted = get_emu10k1_memblk(p, mapped_order_link); + if (deleted->map_locked) + continue; + size = unmap_memblk(emu, deleted); + if (size >= blk->pages) { + /* ok the empty region is enough large */ + err = map_memblk(emu, blk); + break; + } + } + } + spin_unlock_irqrestore(&emu->memblk_lock, flags); + return err; +} + +/* + * page allocation for DMA + */ +snd_util_memblk_t * +snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + struct snd_sg_buf *sgbuf = runtime->dma_private; + snd_util_memhdr_t *hdr; + emu10k1_memblk_t *blk; + int page, err, idx; + + snd_assert(emu, return NULL); + snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG, return NULL); + snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL); + hdr = emu->memhdr; + snd_assert(hdr, return NULL); + + down(&hdr->block_mutex); + blk = search_empty(emu, runtime->dma_bytes); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + /* fill buffer addresses but pointers are not stored so that + * snd_free_pci_page() is not called in in synth_free() + */ + idx = 0; + for (page = blk->first_page; page <= blk->last_page; page++, idx++) { + dma_addr_t addr; +#ifdef CONFIG_SND_DEBUG + if (idx >= sgbuf->pages) { + printk(KERN_ERR "emu: pages overflow! (%d-%d) for %d\n", + blk->first_page, blk->last_page, sgbuf->pages); + up(&hdr->block_mutex); + return NULL; + } +#endif + addr = sgbuf->table[idx].addr; + if (! is_valid_page(emu, addr)) { + printk(KERN_ERR "emu: failure page = %d\n", idx); + up(&hdr->block_mutex); + return NULL; + } + emu->page_addr_table[page] = addr; + emu->page_ptr_table[page] = NULL; + } + + /* set PTB entries */ + blk->map_locked = 1; /* do not unmap this block! */ + err = snd_emu10k1_memblk_map(emu, blk); + if (err < 0) { + __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk); + up(&hdr->block_mutex); + return NULL; + } + up(&hdr->block_mutex); + return (snd_util_memblk_t *)blk; +} + + +/* + * release DMA buffer from page table + */ +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk) +{ + snd_assert(emu && blk, return -EINVAL); + return snd_emu10k1_synth_free(emu, blk); +} + + +/* + * memory allocation using multiple pages (for synth) + * Unlike the DMA allocation above, non-contiguous pages are assined. + */ + +/* + * allocate a synth sample area + */ +snd_util_memblk_t * +snd_emu10k1_synth_alloc(emu10k1_t *hw, unsigned int size) +{ + emu10k1_memblk_t *blk; + snd_util_memhdr_t *hdr = hw->memhdr; + + down(&hdr->block_mutex); + blk = (emu10k1_memblk_t *)__snd_util_mem_alloc(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (synth_alloc_pages(hw, blk)) { + __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk); + up(&hdr->block_mutex); + return NULL; + } + snd_emu10k1_memblk_map(hw, blk); + up(&hdr->block_mutex); + return (snd_util_memblk_t *)blk; +} + + +/* + * free a synth sample area + */ +int +snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *memblk) +{ + snd_util_memhdr_t *hdr = emu->memhdr; + emu10k1_memblk_t *blk = (emu10k1_memblk_t *)memblk; + unsigned long flags; + + down(&hdr->block_mutex); + spin_lock_irqsave(&emu->memblk_lock, flags); + if (blk->mapped_page >= 0) + unmap_memblk(emu, blk); + spin_unlock_irqrestore(&emu->memblk_lock, flags); + synth_free_pages(emu, blk); + __snd_util_mem_free(hdr, memblk); + up(&hdr->block_mutex); + return 0; +} + + +/* check new allocation range */ +static void get_single_page_range(snd_util_memhdr_t *hdr, emu10k1_memblk_t *blk, int *first_page_ret, int *last_page_ret) +{ + struct list_head *p; + emu10k1_memblk_t *q; + int first_page, last_page; + first_page = blk->first_page; + if ((p = blk->mem.list.prev) != &hdr->block) { + q = get_emu10k1_memblk(p, mem.list); + if (q->last_page == first_page) + first_page++; /* first page was already allocated */ + } + last_page = blk->last_page; + if ((p = blk->mem.list.next) != &hdr->block) { + q = get_emu10k1_memblk(p, mem.list); + if (q->first_page == last_page) + last_page--; /* last page was already allocated */ + } + *first_page_ret = first_page; + *last_page_ret = last_page; +} + +/* + * allocate kernel pages + */ +static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, first_page, last_page; + void *ptr; + dma_addr_t addr; + + emu10k1_memblk_init(blk); + get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + /* allocate kernel pages */ + for (page = first_page; page <= last_page; page++) { + ptr = snd_malloc_pci_page(emu->pci, &addr); + if (ptr == NULL) + goto __fail; + if (! is_valid_page(emu, addr)) { + snd_free_pci_page(emu->pci, ptr, addr); + goto __fail; + } + emu->page_addr_table[page] = addr; + emu->page_ptr_table[page] = ptr; + } + return 0; + +__fail: + /* release allocated pages */ + last_page = page - 1; + for (page = first_page; page <= last_page; page++) { + snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); + emu->page_addr_table[page] = 0; + emu->page_ptr_table[page] = NULL; + } + + return -ENOMEM; +} + +/* + * free pages + */ +static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, first_page, last_page; + + get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + for (page = first_page; page <= last_page; page++) { + if (emu->page_ptr_table[page]) + snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); + emu->page_addr_table[page] = 0; + emu->page_ptr_table[page] = NULL; + } + + return 0; +} + +/* calculate buffer pointer from offset address */ +inline static void *offset_ptr(emu10k1_t *emu, int page, int offset) +{ + char *ptr; + snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL); + ptr = emu->page_ptr_table[page]; + if (! ptr) { + printk("emu10k1: access to NULL ptr: page = %d\n", page); + return NULL; + } + ptr += offset & (PAGE_SIZE - 1); + return (void*)ptr; +} + +/* + * bzero(blk + offset, size) + */ +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size) +{ + int page, nextofs, end_offset, temp, temp1; + void *ptr; + emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk; + + offset += blk->offset & (PAGE_SIZE - 1); + end_offset = offset + size; + page = get_aligned_page(offset); + do { + nextofs = aligned_page_offset(page + 1); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + ptr = offset_ptr(emu, page + p->first_page, offset); + if (ptr) + memset(ptr, 0, temp); + offset = nextofs; + page++; + } while (offset < end_offset); + return 0; +} + +/* + * copy_from_user(blk + offset, data, size) + */ +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size) +{ + int page, nextofs, end_offset, temp, temp1; + void *ptr; + emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk; + + offset += blk->offset & (PAGE_SIZE - 1); + end_offset = offset + size; + page = get_aligned_page(offset); + do { + nextofs = aligned_page_offset(page + 1); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + ptr = offset_ptr(emu, page + p->first_page, offset); + if (ptr && copy_from_user(ptr, data, temp)) + return -EFAULT; + offset = nextofs; + data += temp; + page++; + } while (offset < end_offset); + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/emu10k1/voice.c linux/sound/pci/emu10k1/voice.c --- linux-2.4.21-rc1.orig/sound/pci/emu10k1/voice.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/emu10k1/voice.c 2002-08-12 02:43:47.000000000 -0600 @@ -0,0 +1,111 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips - voice manager + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) +{ + emu10k1_voice_t *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < 64; idx += pair ? 2 : 1) { + voice = &emu->voices[idx]; + voice2 = pair ? &emu->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case EMU10K1_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case EMU10K1_SYNTH: + voice->synth = 1; + break; + case EMU10K1_MIDI: + voice->midi = 1; + break; + } + // printk("voice alloc - %i, pair = %i\n", voice->number, pair); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == EMU10K1_PCM, return -EINVAL); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (;;) { + result = voice_alloc(emu, type, pair, rvoice); + if (result == 0 || type != EMU10K1_PCM) + break; + + /* free a voice from synth */ + if (emu->get_synth_voice) { + result = emu->get_synth_voice(emu); + if (result >= 0) { + emu10k1_voice_t *pvoice = &emu->voices[result]; + pvoice->interrupt = NULL; + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->epcm = NULL; + } + } + if (result < 0) + break; + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + return result; +} + +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + spin_lock_irqsave(&emu->voice_lock, flags); + pvoice->interrupt = NULL; + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->epcm = NULL; + snd_emu10k1_voice_init(emu, pvoice->number); + spin_unlock_irqrestore(&emu->voice_lock, flags); + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/pci/ens1370.c linux/sound/pci/ens1370.c --- linux-2.4.21-rc1.orig/sound/pci/ens1370.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ens1370.c 2003-02-25 05:35:44.000000000 -0700 @@ -0,0 +1,2397 @@ +/* + * Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard + * Copyright (c) by Jaroslav Kysela , + * Thomas Sailer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CHIP1371 +#include +#else +#include +#endif +#define SNDRV_GET_ID +#include +#include + +#define chip_t ensoniq_t + +#ifndef CHIP1371 +#undef CHIP1370 +#define CHIP1370 +#endif + +MODULE_AUTHOR("Jaroslav Kysela , Thomas Sailer "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifdef CHIP1370 +MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370"); +MODULE_DEVICES("{{Ensoniq,AudioPCI-97 ES1370}," + "{Creative Labs,SB PCI64/128 (ES1370)}}"); +#endif +#ifdef CHIP1371 +MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+"); +MODULE_DEVICES("{{Ensoniq,AudioPCI ES1371/73}," + "{Ensoniq,AudioPCI ES1373}," + "{Creative Labs,Ectiva EV1938}," + "{Creative Labs,SB PCI64/128 (ES1371/73)}," + "{Creative Labs,Vibra PCI128}," + "{Ectiva,EV1938}}"); +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 +#define CT5880REV_CT5880_D 0x03 /* ??? -jk */ +#define CT5880REV_CT5880_E 0x04 /* mw */ +#define ES1371REV_ES1371_B 0x09 +#define EV1938REV_EV1938_A 0x00 +#define ES1371REV_ES1373_8 0x08 + +/* + * Direct registers + */ + +#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x) + +#define ES_REG_CONTROL 0x00 /* R/W: Interrupt/Chip select control register */ +#define ES_1370_ADC_STOP (1<<31) /* disable capture buffer transfers */ +#define ES_1370_XCTL1 (1<<30) /* general purpose output bit */ +#define ES_1373_TEST_BIT (1<<29) /* should be set to 0 for normal operation */ +#define ES_1373_RECEN_B (1<<28) /* mix record with playback for I2S/SPDIF out */ +#define ES_1373_SPDIF_THRU (1<<26) /* 0 = SPDIF thru mode, 1 = SPDIF == dig out */ +#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24)/* joystick port mapping */ +#define ES_1371_JOY_ASELM (0x03<<24) /* mask for above */ +#define ES_1371_JOY_ASELI(i) (((i)>>24)&0x03) +#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f)/* GPIO in [3:0] pins - R/O */ +#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16)/* clock divide ratio for DAC2 */ +#define ES_1370_PCLKDIVM ((0x1fff)<<16) /* mask for above */ +#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff)/* clock divide ratio for DAC2 */ +#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16)/* GPIO out [3:0] pins - W/R */ +#define ES_1371_GPIO_OUTM (0x0f<<16) /* mask for above */ +#define ES_MSFMTSEL (1<<15) /* MPEG serial data format; 0 = SONY, 1 = I2S */ +#define ES_1370_M_SBB (1<<14) /* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */ +#define ES_1371_SYNC_RES (1<<14) /* Warm AC97 reset */ +#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12)/* fixed frequency clock for DAC1 */ +#define ES_1370_WTSRSELM (0x03<<12) /* mask for above */ +#define ES_1371_ADC_STOP (1<<13) /* disable CCB transfer capture information */ +#define ES_1371_PWR_INTRM (1<<12) /* power level change interrupts enable */ +#define ES_1370_DAC_SYNC (1<<11) /* DAC's are synchronous */ +#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = AC'97 ADC; 1 = I2S */ +#define ES_CCB_INTRM (1<<10) /* CCB voice interrupts enable */ +#define ES_1370_M_CB (1<<9) /* capture clock source; 0 = ADC; 1 = MPEG */ +#define ES_1370_XCTL0 (1<<8) /* generap purpose output bit */ +#define ES_1371_PDLEV(o) (((o)&0x03)<<8) /* current power down level */ +#define ES_1371_PDLEVM (0x03<<8) /* mask for above */ +#define ES_BREQ (1<<7) /* memory bus request enable */ +#define ES_DAC1_EN (1<<6) /* DAC1 playback channel enable */ +#define ES_DAC2_EN (1<<5) /* DAC2 playback channel enable */ +#define ES_ADC_EN (1<<4) /* ADC capture channel enable */ +#define ES_UART_EN (1<<3) /* UART enable */ +#define ES_JYSTK_EN (1<<2) /* Joystick module enable */ +#define ES_1370_CDC_EN (1<<1) /* Codec interface enable */ +#define ES_1371_XTALCKDIS (1<<1) /* Xtal clock disable */ +#define ES_1370_SERR_DISABLE (1<<0) /* PCI serr signal disable */ +#define ES_1371_PCICLKDIS (1<<0) /* PCI clock disable */ +#define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */ +#define ES_INTR (1<<31) /* Interrupt is pending */ +#define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */ +#define ES_1373_REAR_BIT27 (1<<27) /* rear bits: 000 - front, 010 - mirror, 101 - separate */ +#define ES_1373_REAR_BIT26 (1<<26) +#define ES_1373_REAR_BIT24 (1<<24) +#define ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */ +#define ES_1373_SPDIF_EN (1<<18) /* SPDIF enable */ +#define ES_1373_SPDIF_TEST (1<<17) /* SPDIF test */ +#define ES_1371_TEST (1<<16) /* test ASIC */ +#define ES_1373_GPIO_INT(i) (((i)&0x0f)>>12)/* GPIO [3:0] pins - interrupt pending */ +#define ES_1370_CSTAT (1<<10) /* CODEC is busy or register write in progress */ +#define ES_1370_CBUSY (1<<9) /* CODEC is busy */ +#define ES_1370_CWRIP (1<<8) /* CODEC register write in progress */ +#define ES_1371_SYNC_ERR (1<<8) /* CODEC synchronization error occurred */ +#define ES_1371_VC(i) (((i)>>6)&0x03) /* voice code from CCB module */ +#define ES_1370_VC(i) (((i)>>5)&0x03) /* voice code from CCB module */ +#define ES_1371_MPWR (1<<5) /* power level interrupt pending */ +#define ES_MCCB (1<<4) /* CCB interrupt pending */ +#define ES_UART (1<<3) /* UART interrupt pending */ +#define ES_DAC1 (1<<2) /* DAC1 channel interrupt pending */ +#define ES_DAC2 (1<<1) /* DAC2 channel interrupt pending */ +#define ES_ADC (1<<0) /* ADC channel interrupt pending */ +#define ES_REG_UART_DATA 0x08 /* R/W: UART data register */ +#define ES_REG_UART_STATUS 0x09 /* R/O: UART status register */ +#define ES_RXINT (1<<7) /* RX interrupt occurred */ +#define ES_TXINT (1<<2) /* TX interrupt occurred */ +#define ES_TXRDY (1<<1) /* transmitter ready */ +#define ES_RXRDY (1<<0) /* receiver ready */ +#define ES_REG_UART_CONTROL 0x09 /* W/O: UART control register */ +#define ES_RXINTEN (1<<7) /* RX interrupt enable */ +#define ES_TXINTENO(o) (((o)&0x03)<<5) /* TX interrupt enable */ +#define ES_TXINTENM (0x03<<5) /* mask for above */ +#define ES_TXINTENI(i) (((i)>>5)&0x03) +#define ES_CNTRL(o) (((o)&0x03)<<0) /* control */ +#define ES_CNTRLM (0x03<<0) /* mask for above */ +#define ES_REG_UART_RES 0x0a /* R/W: UART reserver register */ +#define ES_TEST_MODE (1<<0) /* test mode enabled */ +#define ES_REG_MEM_PAGE 0x0c /* R/W: Memory page register */ +#define ES_MEM_PAGEO(o) (((o)&0x0f)<<0) /* memory page select - out */ +#define ES_MEM_PAGEM (0x0f<<0) /* mask for above */ +#define ES_MEM_PAGEI(i) (((i)>>0)&0x0f) /* memory page select - in */ +#define ES_REG_1370_CODEC 0x10 /* W/O: Codec write register address */ +#define ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0)) +#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */ +#define ES_1371_CODEC_RDY (1<<31) /* codec ready */ +#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */ +#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */ +#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0)) +#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD) +#define ES_1371_CODEC_READ(i) (((i)>>0)&0xffff) + +#define ES_REG_1371_SMPRATE 0x10 /* W/R: Codec rate converter interface register */ +#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)/* address of the sample rate converter */ +#define ES_1371_SRC_RAM_ADDRM (0x7f<<25) /* mask for above */ +#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)/* address of the sample rate converter */ +#define ES_1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */ +#define ES_1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */ +#define ES_1371_SRC_DISABLE (1<<22) /* sample rate converter disable */ +#define ES_1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_P2 (1<<20) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_R1 (1<<19) /* capture channel accumulator update disable */ +#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)/* current value of the sample rate converter */ +#define ES_1371_SRC_RAM_DATAM (0xffff<<0) /* mask for above */ +#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)/* current value of the sample rate converter */ + +#define ES_REG_1371_LEGACY 0x18 /* W/R: Legacy control/status register */ +#define ES_1371_JFAST (1<<31) /* fast joystick timing */ +#define ES_1371_HIB (1<<30) /* host interrupt blocking enable */ +#define ES_1371_VSB (1<<29) /* SB; 0 = addr 0x220xH, 1 = 0x22FxH */ +#define ES_1371_VMPUO(o) (((o)&0x03)<<27)/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */ +#define ES_1371_VMPUM (0x03<<27) /* mask for above */ +#define ES_1371_VMPUI(i) (((i)>>27)&0x03)/* base register address */ +#define ES_1371_VCDCO(o) (((o)&0x03)<<25)/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */ +#define ES_1371_VCDCM (0x03<<25) /* mask for above */ +#define ES_1371_VCDCI(i) (((i)>>25)&0x03)/* CODEC address */ +#define ES_1371_FIRQ (1<<24) /* force an interrupt */ +#define ES_1371_SDMACAP (1<<23) /* enable event capture for slave DMA controller */ +#define ES_1371_SPICAP (1<<22) /* enable event capture for slave IRQ controller */ +#define ES_1371_MDMACAP (1<<21) /* enable event capture for master DMA controller */ +#define ES_1371_MPICAP (1<<20) /* enable event capture for master IRQ controller */ +#define ES_1371_ADCAP (1<<19) /* enable event capture for ADLIB register; 0x388xH */ +#define ES_1371_SVCAP (1<<18) /* enable event capture for SB registers */ +#define ES_1371_CDCCAP (1<<17) /* enable event capture for CODEC registers */ +#define ES_1371_BACAP (1<<16) /* enable event capture for SoundScape base address */ +#define ES_1371_EXI(i) (((i)>>8)&0x07) /* event number */ +#define ES_1371_AI(i) (((i)>>3)&0x1f) /* event significant I/O address */ +#define ES_1371_WR (1<<2) /* event capture; 0 = read; 1 = write */ +#define ES_1371_LEGINT (1<<0) /* interrupt for legacy events; 0 = interrupt did occur */ + +#define ES_REG_CHANNEL_STATUS 0x1c /* R/W: first 32-bits from S/PDIF channel status block, es1373 */ + +#define ES_REG_SERIAL 0x20 /* R/W: Serial interface control register */ +#define ES_1371_DAC_TEST (1<<22) /* DAC test mode enable */ +#define ES_P2_END_INCO(o) (((o)&0x07)<<19)/* binary offset value to increment / loop end */ +#define ES_P2_END_INCM (0x07<<19) /* mask for above */ +#define ES_P2_END_INCI(i) (((i)>>16)&0x07)/* binary offset value to increment / loop end */ +#define ES_P2_ST_INCO(o) (((o)&0x07)<<16)/* binary offset value to increment / start */ +#define ES_P2_ST_INCM (0x07<<16) /* mask for above */ +#define ES_P2_ST_INCI(i) (((i)<<16)&0x07)/* binary offset value to increment / start */ +#define ES_R1_LOOP_SEL (1<<15) /* ADC; 0 - loop mode; 1 = stop mode */ +#define ES_P2_LOOP_SEL (1<<14) /* DAC2; 0 - loop mode; 1 = stop mode */ +#define ES_P1_LOOP_SEL (1<<13) /* DAC1; 0 - loop mode; 1 = stop mode */ +#define ES_P2_PAUSE (1<<12) /* DAC2; 0 - play mode; 1 = pause mode */ +#define ES_P1_PAUSE (1<<11) /* DAC1; 0 - play mode; 1 = pause mode */ +#define ES_R1_INT_EN (1<<10) /* ADC interrupt enable */ +#define ES_P2_INT_EN (1<<9) /* DAC2 interrupt enable */ +#define ES_P1_INT_EN (1<<8) /* DAC1 interrupt enable */ +#define ES_P1_SCT_RLD (1<<7) /* force sample counter reload for DAC1 */ +#define ES_P2_DAC_SEN (1<<6) /* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */ +#define ES_R1_MODEO(o) (((o)&0x03)<<4) /* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */ +#define ES_R1_MODEM (0x03<<4) /* mask for above */ +#define ES_R1_MODEI(i) (((i)>>4)&0x03) +#define ES_P2_MODEO(o) (((o)&0x03)<<2) /* DAC2 mode; -- '' -- */ +#define ES_P2_MODEM (0x03<<2) /* mask for above */ +#define ES_P2_MODEI(i) (((i)>>2)&0x03) +#define ES_P1_MODEO(o) (((o)&0x03)<<0) /* DAC1 mode; -- '' -- */ +#define ES_P1_MODEM (0x03<<0) /* mask for above */ +#define ES_P1_MODEI(i) (((i)>>0)&0x03) + +#define ES_REG_DAC1_COUNT 0x24 /* R/W: DAC1 sample count register */ +#define ES_REG_DAC2_COUNT 0x28 /* R/W: DAC2 sample count register */ +#define ES_REG_ADC_COUNT 0x2c /* R/W: ADC sample count register */ +#define ES_REG_CURR_COUNT(i) (((i)>>16)&0xffff) +#define ES_REG_COUNTO(o) (((o)&0xffff)<<0) +#define ES_REG_COUNTM (0xffff<<0) +#define ES_REG_COUNTI(i) (((i)>>0)&0xffff) + +#define ES_REG_DAC1_FRAME 0x30 /* R/W: PAGE 0x0c; DAC1 frame address */ +#define ES_REG_DAC1_SIZE 0x34 /* R/W: PAGE 0x0c; DAC1 frame size */ +#define ES_REG_DAC2_FRAME 0x38 /* R/W: PAGE 0x0c; DAC2 frame address */ +#define ES_REG_DAC2_SIZE 0x3c /* R/W: PAGE 0x0c; DAC2 frame size */ +#define ES_REG_ADC_FRAME 0x30 /* R/W: PAGE 0x0d; ADC frame address */ +#define ES_REG_ADC_SIZE 0x34 /* R/W: PAGE 0x0d; ADC frame size */ +#define ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16) +#define ES_REG_FCURR_COUNTM (0xffff<<16) +#define ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc) +#define ES_REG_FSIZEO(o) (((o)&0xffff)<<0) +#define ES_REG_FSIZEM (0xffff<<0) +#define ES_REG_FSIZEI(i) (((i)>>0)&0xffff) +#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */ +#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */ + +#define ES_REG_UART_FIFO 0x30 /* R/W: PAGE 0x0e; UART FIFO register */ +#define ES_REG_UF_VALID (1<<8) +#define ES_REG_UF_BYTEO(o) (((o)&0xff)<<0) +#define ES_REG_UF_BYTEM (0xff<<0) +#define ES_REG_UF_BYTEI(i) (((i)>>0)&0xff) + + +/* + * Pages + */ + +#define ES_PAGE_DAC 0x0c +#define ES_PAGE_ADC 0x0d +#define ES_PAGE_UART 0x0e +#define ES_PAGE_UART1 0x0f + +/* + * Sample rate converter addresses + */ + +#define ES_SMPREG_DAC1 0x70 +#define ES_SMPREG_DAC2 0x74 +#define ES_SMPREG_ADC 0x78 +#define ES_SMPREG_VOL_ADC 0x6c +#define ES_SMPREG_VOL_DAC1 0x7c +#define ES_SMPREG_VOL_DAC2 0x7e +#define ES_SMPREG_TRUNC_N 0x00 +#define ES_SMPREG_INT_REGS 0x01 +#define ES_SMPREG_ACCUM_FRAC 0x02 +#define ES_SMPREG_VFREQ_FRAC 0x03 + +/* + * Some contants + */ + +#define ES_1370_SRCLOCK 1411200 +#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2) + +/* + * Open modes + */ + +#define ES_MODE_PLAY1 0x0001 +#define ES_MODE_PLAY2 0x0002 +#define ES_MODE_CAPTURE 0x0004 + +#define ES_MODE_OUTPUT 0x0001 /* for MIDI */ +#define ES_MODE_INPUT 0x0002 /* for MIDI */ + +/* + + */ + +typedef struct _snd_ensoniq ensoniq_t; + +struct _snd_ensoniq { + spinlock_t reg_lock; + + int irq; + + unsigned long playback1size; + unsigned long playback2size; + unsigned long capture3size; + + unsigned long port; + struct resource *res_port; + unsigned int mode; + unsigned int uartm; /* UART mode */ + + unsigned int ctrl; /* control register */ + unsigned int sctrl; /* serial control register */ + unsigned int cssr; /* control status register */ + unsigned int uartc; /* uart control register */ + unsigned int rev; /* chip revision */ + + union { +#ifdef CHIP1371 + struct { + ac97_t *ac97; + } es1371; +#else + struct { + int pclkdiv_lock; + ak4531_t *ak4531; + } es1370; +#endif + } u; + + struct pci_dev *pci; + unsigned short subsystem_vendor_id; + unsigned short subsystem_device_id; + snd_card_t *card; + snd_pcm_t *pcm1; /* DAC1/ADC PCM */ + snd_pcm_t *pcm2; /* DAC2 PCM */ + snd_pcm_substream_t *playback1_substream; + snd_pcm_substream_t *playback2_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p1_dma_size; + unsigned int p2_dma_size; + unsigned int c_dma_size; + unsigned int p1_period_size; + unsigned int p2_period_size; + unsigned int c_period_size; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + unsigned int spdif; + unsigned int spdif_default; + unsigned int spdif_stream; + +#ifdef CHIP1370 + unsigned char *bugbuf; + dma_addr_t bugbuf_addr; +#endif + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + struct gameport gameport; + struct semaphore joy_sem; // gameport configuration semaphore +#endif +}; + +static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_audiopci_ids[] __devinitdata = { +#ifdef CHIP1370 + { 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */ +#endif +#ifdef CHIP1371 + { 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1371 */ + { 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1373 - CT5880 */ + { 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ectiva EV1938 */ +#endif + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_audiopci_ids); + +/* + * constants + */ + +#define POLL_COUNT 0xa000 + +#ifdef CHIP1370 +static unsigned int snd_es1370_fixed_rates[] = + {5512, 11025, 22050, 44100}; +static snd_pcm_hw_constraint_list_t snd_es1370_hw_constraints_rates = { + .count = 4, + .list = snd_es1370_fixed_rates, + .mask = 0, +}; +static ratnum_t es1370_clock = { + .num = ES_1370_SRCLOCK, + .den_min = 29, + .den_max = 353, + .den_step = 1, +}; +static snd_pcm_hw_constraint_ratnums_t snd_es1370_hw_constraints_clock = { + .nrats = 1, + .rats = &es1370_clock, +}; +#else +static ratden_t es1371_dac_clock = { + .num_min = 3000 * (1 << 15), + .num_max = 48000 * (1 << 15), + .num_step = 3000, + .den = 1 << 15, +}; +static snd_pcm_hw_constraint_ratdens_t snd_es1371_hw_constraints_dac_clock = { + .nrats = 1, + .rats = &es1371_dac_clock, +}; +static ratnum_t es1371_adc_clock = { + .num = 48000 << 15, + .den_min = 32768, + .den_max = 393216, + .den_step = 1, +}; +static snd_pcm_hw_constraint_ratnums_t snd_es1371_hw_constraints_adc_clock = { + .nrats = 1, + .rats = &es1371_adc_clock, +}; +#endif +static const unsigned int snd_ensoniq_sample_shift[] = + {0, 1, 1, 2}; + +/* + * common I/O routines + */ + +#ifdef CHIP1371 + +static unsigned int snd_es1371_wait_src_ready(ensoniq_t * ensoniq) +{ + unsigned int t, r = 0; + + for (t = 0; t < POLL_COUNT; t++) { + r = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((r & ES_1371_SRC_RAM_BUSY) == 0) + return r; + } + snd_printk("wait source ready timeout 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_SMPRATE), r); + return 0; +} + +static unsigned int snd_es1371_src_read(ensoniq_t * ensoniq, unsigned short reg) +{ + unsigned int temp, i, orig, r; + + /* wait for ready */ + temp = orig = snd_es1371_wait_src_ready(ensoniq); + + /* expose the SRC state bits */ + r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + /* now, wait for busy and the correct time to read */ + temp = snd_es1371_wait_src_ready(ensoniq); + + if ((temp & 0x00870000) != 0x00010000) { + /* wait for the right state */ + for (i = 0; i < POLL_COUNT; i++) { + temp = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((temp & 0x00870000) == 0x00010000) + break; + } + } + + /* hide the state bits */ + r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + return temp; +} + +static void snd_es1371_src_write(ensoniq_t * ensoniq, + unsigned short reg, unsigned short data) +{ + unsigned int r; + + r = snd_es1371_wait_src_ready(ensoniq) & + (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data); + outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +#ifdef CHIP1370 + +static void snd_es1370_codec_write(ak4531_t *ak4531, + unsigned short reg, unsigned short val) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + unsigned long flags; + signed long end_time = jiffies + HZ / 10; + +#if 0 + printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n", reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); +#endif + do { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) { + outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +#endif + } while ((signed long)(end_time - jiffies) > 0); + snd_printk("codec write timeout, status = 0x%x\n", inl(ES_REG(ensoniq, STATUS))); +} + +#endif /* CHIP1370 */ + +#ifdef CHIP1371 + +static void snd_es1371_codec_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + unsigned long flags; + unsigned int t, x; + + for (t = 0; t < POLL_COUNT; t++) { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000) + break; + } + outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + } + snd_printk("codec write timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); +} + +static unsigned short snd_es1371_codec_read(ac97_t *ac97, + unsigned short reg) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return -ENXIO); + unsigned long flags; + unsigned int t, x, fail = 0; + + __again: + for (t = 0; t < POLL_COUNT; t++) { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000) + break; + } + outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for WIP again */ + for (t = 0; t < POLL_COUNT; t++) { + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) + break; + } + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) { + if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) { + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return ES_1371_CODEC_READ(x); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + if (++fail > 10) { + snd_printk("codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; + } + goto __again; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + } + snd_printk("es1371: codec read timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; +} + +static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int n, truncm, freq, result; + + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + result = (48000UL << 15) / (freq / n); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8); +} + +static void snd_es1371_dac1_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)) | ES_1371_DIS_P1; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); +} + +static void snd_es1371_dac2_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)) | ES_1371_DIS_P2; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +static int snd_ensoniq_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == ensoniq->playback1_substream) { + what |= ES_P1_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_P2_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) + return -EINVAL; + s = s->link_next; + } while (s != substream); + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + ensoniq->sctrl |= what; + else + ensoniq->sctrl &= ~what; + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == ensoniq->playback1_substream) { + what |= ES_DAC1_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_DAC2_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) { + what |= ES_ADC_EN; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) + ensoniq->ctrl |= what; + else + ensoniq->ctrl &= ~what; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +/* + * PCM part + */ + +static int snd_ensoniq_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ensoniq_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ensoniq_playback1_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_DAC1_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME)); + outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE)); + ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM); + ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT)); +#ifdef CHIP1370 + ensoniq->ctrl &= ~ES_1370_WTSRSELM; + switch (runtime->rate) { + case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break; + case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break; + case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break; + case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break; + default: snd_BUG(); + } +#else + snd_es1371_dac1_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_playback2_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_DAC2_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME)); + outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE)); + ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN | + ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM); + ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) | + ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2; + } +#else + snd_es1371_dac2_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_ADC_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME)); + outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE)); + ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM); + ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE; + } +#else + snd_es1371_adc_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_capture_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_hardware_t snd_ensoniq_playback1 = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = +#ifndef CHIP1370 + SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, +#else + (SNDRV_PCM_RATE_KNOT | /* 5512Hz rate */ + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_44100), +#endif + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ensoniq_playback2 = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ensoniq_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY1; + ensoniq->playback1_substream = substream; + runtime->hw = snd_ensoniq_playback1; + snd_pcm_set_sync(substream); + spin_lock_irq(&ensoniq->reg_lock); + if (ensoniq->spdif && ensoniq->playback2_substream == NULL) + ensoniq->spdif_stream = ensoniq->spdif_default; + spin_unlock_irq(&ensoniq->reg_lock); +#ifdef CHIP1370 + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_rates); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback2_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY2; + ensoniq->playback2_substream = substream; + runtime->hw = snd_ensoniq_playback2; + snd_pcm_set_sync(substream); + spin_lock_irq(&ensoniq->reg_lock); + if (ensoniq->spdif && ensoniq->playback1_substream == NULL) + ensoniq->spdif_stream = ensoniq->spdif_default; + spin_unlock_irq(&ensoniq->reg_lock); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_capture_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_CAPTURE; + ensoniq->capture_substream = substream; + runtime->hw = snd_ensoniq_capture; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_adc_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback1_close(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback1_substream = NULL; + ensoniq->mode &= ~ES_MODE_PLAY1; + return 0; +} + +static int snd_ensoniq_playback2_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback2_substream = NULL; + spin_lock_irqsave(&ensoniq->reg_lock, flags); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2; +#endif + ensoniq->mode &= ~ES_MODE_PLAY2; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->capture_substream = NULL; + spin_lock_irqsave(&ensoniq->reg_lock, flags); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE; +#endif + ensoniq->mode &= ~ES_MODE_CAPTURE; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_ensoniq_playback1_ops = { + .open = snd_ensoniq_playback1_open, + .close = snd_ensoniq_playback1_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ensoniq_hw_params, + .hw_free = snd_ensoniq_hw_free, + .prepare = snd_ensoniq_playback1_prepare, + .trigger = snd_ensoniq_trigger, + .pointer = snd_ensoniq_playback1_pointer, +}; + +static snd_pcm_ops_t snd_ensoniq_playback2_ops = { + .open = snd_ensoniq_playback2_open, + .close = snd_ensoniq_playback2_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ensoniq_hw_params, + .hw_free = snd_ensoniq_hw_free, + .prepare = snd_ensoniq_playback2_prepare, + .trigger = snd_ensoniq_trigger, + .pointer = snd_ensoniq_playback2_pointer, +}; + +static snd_pcm_ops_t snd_ensoniq_capture_ops = { + .open = snd_ensoniq_capture_open, + .close = snd_ensoniq_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ensoniq_hw_params, + .hw_free = snd_ensoniq_hw_free, + .prepare = snd_ensoniq_capture_prepare, + .trigger = snd_ensoniq_trigger, + .pointer = snd_ensoniq_capture_pointer, +}; + +static void snd_ensoniq_pcm_free(snd_pcm_t *pcm) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq->pcm1 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm); +#endif + if (err < 0) + return err; + +#ifdef CHIP1370 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); +#endif + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops); + + pcm->private_data = ensoniq; + pcm->private_free = snd_ensoniq_pcm_free; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC2/ADC"); +#else + strcpy(pcm->name, "ES1371 DAC2/ADC"); +#endif + ensoniq->pcm1 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_ensoniq_pcm_free2(snd_pcm_t *pcm) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq->pcm2 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm); +#endif + if (err < 0) + return err; + +#ifdef CHIP1370 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); +#endif + pcm->private_data = ensoniq; + pcm->private_free = snd_ensoniq_pcm_free2; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC1"); +#else + strcpy(pcm->name, "ES1371 DAC1"); +#endif + ensoniq->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +/* + * ENS1371 mixer (including SPDIF interface) + */ +#ifdef CHIP1371 +static int snd_ens1373_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ens1373_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ens1373_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change; + + val = ((u32)ucontrol->value.iec958.status[0] << 0) | + ((u32)ucontrol->value.iec958.status[1] << 8) | + ((u32)ucontrol->value.iec958.status[2] << 16) | + ((u32)ucontrol->value.iec958.status[3] << 24); + spin_lock_irq(&ensoniq->reg_lock); + change = ensoniq->spdif_default != val; + ensoniq->spdif_default = val; + if (change && ensoniq->playback1_substream == NULL && ensoniq->playback2_substream == NULL) + outl(val, ES_REG(ensoniq, CHANNEL_STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static int snd_ens1373_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int snd_ens1373_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ens1373_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change; + + val = ((u32)ucontrol->value.iec958.status[0] << 0) | + ((u32)ucontrol->value.iec958.status[1] << 8) | + ((u32)ucontrol->value.iec958.status[2] << 16) | + ((u32)ucontrol->value.iec958.status[3] << 24); + spin_lock_irq(&ensoniq->reg_lock); + change = ensoniq->spdif_stream != val; + ensoniq->spdif_stream = val; + if (change && (ensoniq->playback1_substream != NULL || ensoniq->playback2_substream != NULL)) + outl(val, ES_REG(ensoniq, CHANNEL_STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ens1373_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_ens1373_spdif_info, + .get = snd_ens1373_spdif_default_get, + .put = snd_ens1373_spdif_default_put, +}; + +static snd_kcontrol_new_t snd_ens1373_spdif_mask __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + .info = snd_ens1373_spdif_info, + .get = snd_ens1373_spdif_mask_get +}; + +static snd_kcontrol_new_t snd_ens1373_spdif_stream __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_ens1373_spdif_info, + .get = snd_ens1373_spdif_stream_get, + .put = snd_ens1373_spdif_stream_put +}; + +#define ES1371_SPDIF(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \ + .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put } + +static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1371_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int nval1, nval2; + int change; + + nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0; + nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0; + spin_lock_irq(&ensoniq->reg_lock); + change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1; + ensoniq->ctrl &= ~ES_1373_SPDIF_THRU; + ensoniq->ctrl |= nval1; + ensoniq->cssr &= ~ES_1373_SPDIF_EN; + ensoniq->cssr |= nval2; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_es1371_mixer_spdif __devinitdata = +ES1371_SPDIF("IEC958 Playback Switch"); + +static int snd_es1373_rear_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1373_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + int val = 0; + + spin_lock_irq(&ensoniq->reg_lock); + if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26) + val = 1; + ucontrol->value.integer.value[0] = val; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_es1373_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int nval1; + int change; + + nval1 = ucontrol->value.integer.value[0] ? ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); + spin_lock_irq(&ensoniq->reg_lock); + change = (ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1; + ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24); + ensoniq->cssr |= nval1; + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ens1373_rear __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "AC97 2ch->4ch Copy Switch", + .info = snd_es1373_rear_info, + .get = snd_es1373_rear_get, + .put = snd_es1373_rear_put, +}; + +static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + ensoniq->u.es1371.ac97 = NULL; +} + +static struct { + unsigned short vid; /* vendor ID */ + unsigned short did; /* device ID */ + unsigned char rev; /* revision */ +} es1371_spdif_present[] __devinitdata = { + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 }, + { .vid = PCI_ANY_ID, .did = PCI_ANY_ID } +}; + +static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) +{ + snd_card_t *card = ensoniq->card; + ac97_t ac97; + int err, idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_es1371_codec_write; + ac97.read = snd_es1371_codec_read; + ac97.private_data = ensoniq; + ac97.private_free = snd_ensoniq_mixer_free_ac97; + if ((err = snd_ac97_mixer(card, &ac97, &ensoniq->u.es1371.ac97)) < 0) + return err; + for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++) + if (ensoniq->pci->vendor == es1371_spdif_present[idx].vid && + ensoniq->pci->device == es1371_spdif_present[idx].did && + ensoniq->rev == es1371_spdif_present[idx].rev) { + snd_kcontrol_t *kctl; + int index = 0; + + ensoniq->spdif_default = ensoniq->spdif_stream = SNDRV_PCM_DEFAULT_CON_SPDIF; + outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS)); + + if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF) + index++; + + kctl = snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq); + kctl->id.index = index; + snd_ctl_add(card, kctl); + + kctl = snd_ctl_new1(&snd_ens1373_spdif_default, ensoniq); + kctl->id.index = index; + snd_ctl_add(card, kctl); + + kctl = snd_ctl_new1(&snd_ens1373_spdif_mask, ensoniq); + kctl->id.index = index; + snd_ctl_add(card, kctl); + + kctl = snd_ctl_new1(&snd_ens1373_spdif_stream, ensoniq); + kctl->id.index = index; + snd_ctl_add(card, kctl); + break; + } + if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) { + /* mirror rear to front speakers */ + ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); + ensoniq->cssr |= ES_1373_REAR_BIT26; + snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_rear, ensoniq)); + } + return 0; +} + +#endif /* CHIP1371 */ + +/* generic control callbacks for ens1370 and for joystick */ +#if defined(CHIP1370) || defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) +#define ENSONIQ_CONTROL(xname, mask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \ + .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \ + .private_value = mask } + +static int snd_ensoniq_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int mask = kcontrol->private_value; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +#ifdef CHIP1370 +static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int mask = kcontrol->private_value; + unsigned int nval; + int change; + + nval = ucontrol->value.integer.value[0] ? mask : 0; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & mask) != nval; + ensoniq->ctrl &= ~mask; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} +#endif /* CHIP1370 */ +#endif /* CHIP1370 || GAMEPORT */ + +/* + * ENS1370 mixer + */ + +#ifdef CHIP1370 +static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = { +ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0), +ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1) +}; + +#define ES1370_CONTROLS ARRAY_SIZE(snd_es1370_controls) + +static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + ensoniq->u.es1370.ak4531 = NULL; +} + +static int __devinit snd_ensoniq_1370_mixer(ensoniq_t * ensoniq) +{ + snd_card_t *card = ensoniq->card; + ak4531_t ak4531; + unsigned int idx; + int err; + + /* try reset AK4531 */ + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + + memset(&ak4531, 0, sizeof(ak4531)); + ak4531.write = snd_es1370_codec_write; + ak4531.private_data = ensoniq; + ak4531.private_free = snd_ensoniq_mixer_free_ak4531; + if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0) + return err; + for (idx = 0; idx < ES1370_CONTROLS; idx++) + snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq)); + return 0; +} + +#endif /* CHIP1370 */ + +/* + * General Switches... + */ + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) +/* MQ: gameport driver connectivity */ +#define ENSONIQ_JOY_CONTROL(xname, mask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \ + .get = snd_ensoniq_control_get, .put = snd_ensoniq_joy_control_put, \ + .private_value = mask } + +static int snd_ensoniq_joy_enable(ensoniq_t *ensoniq) +{ + static unsigned long last_jiffies = 0; + unsigned long flags; + + if (!request_region(ensoniq->gameport.io, 8, "ens137x: gameport")) { +#define ES___GAMEPORT_LOG_DELAY (30*HZ) + // avoid log pollution: limit to 2 infos per minute + if (time_after(jiffies, last_jiffies + ES___GAMEPORT_LOG_DELAY)) { + last_jiffies = jiffies; + snd_printk("gameport io port 0x%03x in use", ensoniq->gameport.io); + } + return 0; + } + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl |= ES_JYSTK_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + gameport_register_port(&ensoniq->gameport); + return 1; +} + +static int snd_ensoniq_joy_disable(ensoniq_t *ensoniq) +{ + unsigned long flags; + + gameport_unregister_port(&ensoniq->gameport); + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_JYSTK_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + release_region(ensoniq->gameport.io, 8); + return 1; +} + +static int snd_ensoniq_joy_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int nval; + int change; + + down(&ensoniq->joy_sem); + nval = ucontrol->value.integer.value[0] ? ES_JYSTK_EN : 0; + change = (ensoniq->ctrl & ES_JYSTK_EN) != nval; // spinlock shouldn't be needed because of joy_sem + if (change) { + if (nval) // enable + change = snd_ensoniq_joy_enable(ensoniq); + else change = snd_ensoniq_joy_disable(ensoniq); + } + up(&ensoniq->joy_sem); + return change; +} + +static snd_kcontrol_new_t snd_ensoniq_control_joystick __devinitdata = +ENSONIQ_JOY_CONTROL("Joystick Enable", ES_JYSTK_EN); + +#ifdef CHIP1371 + +#define ES1371_JOYSTICK_ADDR(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_es1371_joystick_addr_info, \ + .get = snd_es1371_joystick_addr_get, .put = snd_es1371_joystick_addr_put } + +static int snd_es1371_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + sprintf(uinfo->value.enumerated.name, "port 0x%x", (uinfo->value.enumerated.item * 8) + 0x200); + return 0; +} + +static int snd_es1371_joystick_addr_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.enumerated.item[0] = ES_1371_JOY_ASELI(ensoniq->ctrl); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_es1371_joystick_addr_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval; + int change; + + down(&ensoniq->joy_sem); + nval = ES_1371_JOY_ASEL(ucontrol->value.integer.value[0]); + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(change = !(ensoniq->ctrl & ES_JYSTK_EN))) + goto no_change; // FIXME: now we allow change only when joystick is disabled + change = (ensoniq->ctrl & ES_1371_JOY_ASELM) != nval; + ensoniq->ctrl &= ~ES_1371_JOY_ASELM; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + ensoniq->gameport.io = 0x200 + ES_1371_JOY_ASELI(nval) * 8; +no_change: + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + up(&ensoniq->joy_sem); + return change; +} + +static snd_kcontrol_new_t snd_es1371_joystick_addr __devinitdata = +ES1371_JOYSTICK_ADDR("Joystick Address"); + +#endif /* CHIP1371 */ +#endif /* CONFIG_GAMEPORT */ + +/* + + */ + +static void snd_ensoniq_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, entry->private_data, return); + +#ifdef CHIP1370 + snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n"); +#else + snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n"); +#endif + snd_iprintf(buffer, "Joystick enable : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); +#ifdef CHIP1370 + snd_iprintf(buffer, "MIC +5V bias : %s\n", ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off"); + snd_iprintf(buffer, "Line In to AOUT : %s\n", ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off"); +#else + snd_iprintf(buffer, "Joystick port : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200); +#endif +} + +static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) + snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); +} + +/* + + */ + +static int snd_ensoniq_free(ensoniq_t *ensoniq) +{ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + if (ensoniq->ctrl & ES_JYSTK_EN) + snd_ensoniq_joy_disable(ensoniq); +#endif + if (ensoniq->irq < 0) + goto __hw_end; +#ifdef CHIP1370 + outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#else + outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#endif + synchronize_irq(ensoniq->irq); + pci_set_power_state(ensoniq->pci, 3); + __hw_end: +#ifdef CHIP1370 + if (ensoniq->bugbuf) + snd_free_pci_pages(ensoniq->pci, 16, ensoniq->bugbuf, ensoniq->bugbuf_addr); +#endif + if (ensoniq->res_port) { + release_resource(ensoniq->res_port); + kfree_nocheck(ensoniq->res_port); + } + if (ensoniq->irq >= 0) + free_irq(ensoniq->irq, (void *)ensoniq); + snd_magic_kfree(ensoniq); + return 0; +} + +static int snd_ensoniq_dev_free(snd_device_t *device) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, device->device_data, return -ENXIO); + return snd_ensoniq_free(ensoniq); +} + +#ifdef CHIP1371 +static struct { + unsigned short svid; /* subsystem vendor ID */ + unsigned short sdid; /* subsystem device ID */ +} es1371_amplifier_hack[] = { + { .svid = 0x107b, .sdid = 0x2150 }, /* Gateway Solo 2150 */ + { .svid = 0x13bd, .sdid = 0x100c }, /* EV1938 on Mebius PC-MJ100V */ + { .svid = 0x1102, .sdid = 0x5938 }, /* Targa Xtender300 */ + { .svid = 0x1102, .sdid = 0x8938 }, /* IPC Topnote G notebook */ + { .svid = PCI_ANY_ID, .sdid = PCI_ANY_ID } +}; +static struct { + unsigned short vid; /* vendor ID */ + unsigned short did; /* device ID */ + unsigned char rev; /* revision */ +} es1371_ac97_reset_hack[] = { + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 }, + { .vid = PCI_ANY_ID, .did = PCI_ANY_ID } +}; +#endif + +static int __devinit snd_ensoniq_create(snd_card_t * card, + struct pci_dev *pci, + ensoniq_t ** rensoniq) +{ + ensoniq_t *ensoniq; + unsigned short cmdw; + unsigned char cmdb; +#ifdef CHIP1371 + int idx; +#endif + int err; + static snd_device_ops_t ops = { + .dev_free = snd_ensoniq_dev_free, + }; + + *rensoniq = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + ensoniq = snd_magic_kcalloc(ensoniq_t, 0, GFP_KERNEL); + if (ensoniq == NULL) + return -ENOMEM; + spin_lock_init(&ensoniq->reg_lock); + ensoniq->card = card; + ensoniq->pci = pci; + ensoniq->irq = -1; + ensoniq->port = pci_resource_start(pci, 0); + if ((ensoniq->res_port = request_region(ensoniq->port, 0x40, "Ensoniq AudioPCI")) == NULL) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ensoniq->port, ensoniq->port + 0x40 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, "Ensoniq AudioPCI", (void *)ensoniq)) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + ensoniq->irq = pci->irq; +#ifdef CHIP1370 + if ((ensoniq->bugbuf = snd_malloc_pci_pages(pci, 16, &ensoniq->bugbuf_addr)) == NULL) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to allocate space for phantom area - bugbuf\n"); + return -EBUSY; + } +#endif + pci_set_master(pci); + pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb); + ensoniq->rev = cmdb; + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw); + ensoniq->subsystem_vendor_id = cmdw; + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw); + ensoniq->subsystem_device_id = cmdw; + snd_ensoniq_proc_init(ensoniq); +#ifdef CHIP1370 +#if 0 + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#else /* get microphone working */ + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#endif + ensoniq->sctrl = 0; + /* initialize the chips */ + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(ensoniq->bugbuf_addr, ES_REG(ensoniq, PHANTOM_FRAME)); + outl(0, ES_REG(ensoniq, PHANTOM_COUNT)); +#else + ensoniq->ctrl = 0; + ensoniq->sctrl = 0; + ensoniq->cssr = 0; + for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++) + if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid && + ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) { + ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */ + break; + } + /* initialize the chips */ + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(0, ES_REG(ensoniq, 1371_LEGACY)); + for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++) + if (pci->vendor == es1371_ac97_reset_hack[idx].vid && + pci->device == es1371_ac97_reset_hack[idx].did && + ensoniq->rev == es1371_ac97_reset_hack[idx].rev) { + unsigned long tmo; + signed long tmo2; + + ensoniq->cssr |= ES_1371_ST_AC97_RST; + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + tmo = jiffies + (HZ / 50) + 1; + while (1) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(tmo2); + } + break; + } + /* AC'97 warm reset to start the bitclk */ + outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL)); + udelay(20); + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + /* Init the sample rate converter */ + snd_es1371_wait_src_ready(ensoniq); + outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE)); + for (idx = 0; idx < 0x80; idx++) + snd_es1371_src_write(ensoniq, idx, 0); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); + snd_es1371_adc_rate(ensoniq, 22050); + snd_es1371_dac1_rate(ensoniq, 22050); + snd_es1371_dac2_rate(ensoniq, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) - Thomas Sailer + */ + snd_es1371_wait_src_ready(ensoniq); + outl(0, ES_REG(ensoniq, 1371_SMPRATE)); + /* try reset codec directly */ + outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC)); +#endif + outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL)); + outb(0x00, ES_REG(ensoniq, UART_RES)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + init_MUTEX(&ensoniq->joy_sem); +#ifdef CHIP1371 + snd_ctl_add(card, snd_ctl_new1(&snd_es1371_joystick_addr, ensoniq)); +#endif + snd_ctl_add(card, snd_ctl_new1(&snd_ensoniq_control_joystick, ensoniq)); + ensoniq->gameport.io = 0x200; // FIXME: is ES1371 configured like this above ? +#endif + synchronize_irq(ensoniq->irq); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) { + snd_ensoniq_free(ensoniq); + return err; + } + + *rensoniq = ensoniq; + return 0; +} + +/* + * MIDI section + */ + +static void snd_ensoniq_midi_interrupt(ensoniq_t * ensoniq) +{ + snd_rawmidi_t * rmidi = ensoniq->rmidi; + unsigned char status, mask, byte; + + if (rmidi == NULL) + return; + /* do Rx at first */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + byte = inb(ES_REG(ensoniq, UART_DATA)); + spin_unlock(&ensoniq->reg_lock); + snd_rawmidi_receive(ensoniq->midi_input, &byte, 1); + spin_lock(&ensoniq->reg_lock); + } + spin_unlock(&ensoniq->reg_lock); + + /* do Tx at second */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + mask &= ~ES_TXRDY; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + spin_unlock(&ensoniq->reg_lock); +} + +static int snd_ensoniq_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->uartm |= ES_MODE_INPUT; + ensoniq->midi_input = substream; + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_input = NULL; + ensoniq->uartm &= ~ES_MODE_INPUT; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->uartm |= ES_MODE_OUTPUT; + ensoniq->midi_output = substream; + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_output = NULL; + ensoniq->uartm &= ~ES_MODE_OUTPUT; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static void snd_ensoniq_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + int idx; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if ((ensoniq->uartc & ES_RXINTEN) == 0) { + /* empty input FIFO */ + for (idx = 0; idx < 32; idx++) + inb(ES_REG(ensoniq, UART_DATA)); + ensoniq->uartc |= ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ensoniq->uartc & ES_RXINTEN) { + ensoniq->uartc &= ~ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static void snd_ensoniq_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if (ES_TXINTENI(ensoniq->uartc) == 0) { + ensoniq->uartc |= ES_TXINTENO(1); + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while (ES_TXINTENI(ensoniq->uartc) == 1 && + (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ES_TXINTENI(ensoniq->uartc) == 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_ensoniq_midi_output = +{ + .open = snd_ensoniq_midi_output_open, + .close = snd_ensoniq_midi_output_close, + .trigger = snd_ensoniq_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_ensoniq_midi_input = +{ + .open = snd_ensoniq_midi_input_open, + .close = snd_ensoniq_midi_input_close, + .trigger = snd_ensoniq_midi_input_trigger, +}; + +static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0) + return err; +#ifdef CHIP1370 + strcpy(rmidi->name, "ES1370"); +#else + strcpy(rmidi->name, "ES1371"); +#endif + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = ensoniq; + ensoniq->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, dev_id, return); + unsigned int status, sctrl; + + if (ensoniq == NULL) + return; + + status = inl(ES_REG(ensoniq, STATUS)); + if (!(status & ES_INTR)) + return; + + spin_lock(&ensoniq->reg_lock); + sctrl = ensoniq->sctrl; + if (status & ES_DAC1) + sctrl &= ~ES_P1_INT_EN; + if (status & ES_DAC2) + sctrl &= ~ES_P2_INT_EN; + if (status & ES_ADC) + sctrl &= ~ES_R1_INT_EN; + outl(sctrl, ES_REG(ensoniq, SERIAL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + + if (status & ES_UART) + snd_ensoniq_midi_interrupt(ensoniq); + if ((status & ES_DAC2) && ensoniq->playback2_substream) + snd_pcm_period_elapsed(ensoniq->playback2_substream); + if ((status & ES_ADC) && ensoniq->capture_substream) + snd_pcm_period_elapsed(ensoniq->capture_substream); + if ((status & ES_DAC1) && ensoniq->playback1_substream) + snd_pcm_period_elapsed(ensoniq->playback1_substream); +} + +static int __devinit snd_audiopci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + ensoniq_t *ensoniq; + int err, pcm_devs[2]; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) { + snd_card_free(card); + return err; + } + + pcm_devs[0] = 0; pcm_devs[1] = 1; +#ifdef CHIP1370 + if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif +#ifdef CHIP1371 + if ((err = snd_ensoniq_1371_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif + if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef CHIP1370 + strcpy(card->driver, "ENS1370"); +#endif +#ifdef CHIP1371 + strcpy(card->driver, "ENS1371"); +#endif + strcpy(card->shortname, "Ensoniq AudioPCI"); + sprintf(card->longname, "%s %s at 0x%lx, irq %i", + card->shortname, + card->driver, + ensoniq->port, + ensoniq->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_audiopci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Ensoniq AudioPCI", + .id_table = snd_audiopci_ids, + .probe = snd_audiopci_probe, + .remove = __devexit_p(snd_audiopci_remove), +}; + +static int __init alsa_card_ens137x_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Ensoniq AudioPCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ens137x_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ens137x_init) +module_exit(alsa_card_ens137x_exit) + +#ifndef MODULE + +/* format is: snd-ens1370=enable,index,id */ + +static int __init alsa_card_ens137x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +#if defined(CHIP1370) +__setup("snd-ens1370=", alsa_card_ens137x_setup); +#elif defined(CHIP1371) +__setup("snd-ens1371=", alsa_card_ens137x_setup); +#endif + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ens1371.c linux/sound/pci/ens1371.c --- linux-2.4.21-rc1.orig/sound/pci/ens1371.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ens1371.c 2001-12-18 07:39:12.000000000 -0700 @@ -0,0 +1,2 @@ +#define CHIP1371 +#include "ens1370.c" diff -urN linux-2.4.21-rc1.orig/sound/pci/es1938.c linux/sound/pci/es1938.c --- linux-2.4.21-rc1.orig/sound/pci/es1938.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/es1938.c 2003-01-31 08:20:19.000000000 -0700 @@ -0,0 +1,1731 @@ +/* + * Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard + * Copyright (c) by Jaromir Koutek , + * Jaroslav Kysela , + * Thomas Sailer , + * Abramo Bagnara , + * Markus Gruber + * + * Rewritten from sonicvibes.c source. + * + * TODO: + * Rewrite better spinlocks + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - Capture data is written unaligned starting from dma_base + 1 so I need to + disable mmap and to add a copy callback. + - After several cycle of the following: + while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done + a "playback write error (DMA or IRQ trouble?)" may happen. + This is due to playback interrupts not generated. + I suspect a timing issue. + - Sometimes the interrupt handler is invoked wrongly during playback. + This generates some harmless "Unexpected hw_pointer: wrong interrupt + acknowledge". + I've seen that using small period sizes. + Reproducible with: + mpg123 test.mp3 & + hdparm -t -T /dev/hda +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#define chip_t es1938_t + +MODULE_AUTHOR("Jaromir Koutek "); +MODULE_DESCRIPTION("ESS Solo-1"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES1938}," + "{ESS,ES1946}," + "{ESS,ES1969}," + "{TerraTec,128i PCI}}"); + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_ES1938 +#define PCI_DEVICE_ID_ESS_ES1938 0x1969 +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x) + +#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x) + +#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x) + +#define SL_PCI_LEGACYCONTROL 0x40 +#define SL_PCI_CONFIG 0x50 +#define SL_PCI_DDMACONTROL 0x60 + +#define ESSIO_REG_AUDIO2DMAADDR 0 +#define ESSIO_REG_AUDIO2DMACOUNT 4 +#define ESSIO_REG_AUDIO2MODE 6 +#define ESSIO_REG_IRQCONTROL 7 + +#define ESSDM_REG_DMAADDR 0x00 +#define ESSDM_REG_DMACOUNT 0x04 +#define ESSDM_REG_DMACOMMAND 0x08 +#define ESSDM_REG_DMASTATUS 0x08 +#define ESSDM_REG_DMAMODE 0x0b +#define ESSDM_REG_DMACLEAR 0x0d +#define ESSDM_REG_DMAMASK 0x0f + +#define ESSSB_REG_FMLOWADDR 0x00 +#define ESSSB_REG_FMHIGHADDR 0x02 +#define ESSSB_REG_MIXERADDR 0x04 +#define ESSSB_REG_MIXERDATA 0x05 + +#define ESSSB_IREG_AUDIO1 0x14 +#define ESSSB_IREG_MICMIX 0x1a +#define ESSSB_IREG_RECSRC 0x1c +#define ESSSB_IREG_MASTER 0x32 +#define ESSSB_IREG_FM 0x36 +#define ESSSB_IREG_AUXACD 0x38 +#define ESSSB_IREG_AUXB 0x3a +#define ESSSB_IREG_PCSPEAKER 0x3c +#define ESSSB_IREG_LINE 0x3e +#define ESSSB_IREG_SPATCONTROL 0x50 +#define ESSSB_IREG_SPATLEVEL 0x52 +#define ESSSB_IREG_MASTER_LEFT 0x60 +#define ESSSB_IREG_MASTER_RIGHT 0x62 +#define ESSSB_IREG_MPU401CONTROL 0x64 +#define ESSSB_IREG_MICMIXRECORD 0x68 +#define ESSSB_IREG_AUDIO2RECORD 0x69 +#define ESSSB_IREG_AUXACDRECORD 0x6a +#define ESSSB_IREG_FMRECORD 0x6b +#define ESSSB_IREG_AUXBRECORD 0x6c +#define ESSSB_IREG_MONO 0x6d +#define ESSSB_IREG_LINERECORD 0x6e +#define ESSSB_IREG_MONORECORD 0x6f +#define ESSSB_IREG_AUDIO2SAMPLE 0x70 +#define ESSSB_IREG_AUDIO2MODE 0x71 +#define ESSSB_IREG_AUDIO2FILTER 0x72 +#define ESSSB_IREG_AUDIO2TCOUNTL 0x74 +#define ESSSB_IREG_AUDIO2TCOUNTH 0x76 +#define ESSSB_IREG_AUDIO2CONTROL1 0x78 +#define ESSSB_IREG_AUDIO2CONTROL2 0x7a +#define ESSSB_IREG_AUDIO2 0x7c + +#define ESSSB_REG_RESET 0x06 + +#define ESSSB_REG_READDATA 0x0a +#define ESSSB_REG_WRITEDATA 0x0c +#define ESSSB_REG_READSTATUS 0x0c + +#define ESSSB_REG_STATUS 0x0e + +#define ESS_CMD_EXTSAMPLERATE 0xa1 +#define ESS_CMD_FILTERDIV 0xa2 +#define ESS_CMD_DMACNTRELOADL 0xa4 +#define ESS_CMD_DMACNTRELOADH 0xa5 +#define ESS_CMD_ANALOGCONTROL 0xa8 +#define ESS_CMD_IRQCONTROL 0xb1 +#define ESS_CMD_DRQCONTROL 0xb2 +#define ESS_CMD_RECLEVEL 0xb4 +#define ESS_CMD_SETFORMAT 0xb6 +#define ESS_CMD_SETFORMAT2 0xb7 +#define ESS_CMD_DMACONTROL 0xb8 +#define ESS_CMD_DMATYPE 0xb9 +#define ESS_CMD_OFFSETLEFT 0xba +#define ESS_CMD_OFFSETRIGHT 0xbb +#define ESS_CMD_READREG 0xc0 +#define ESS_CMD_ENABLEEXT 0xc6 +#define ESS_CMD_PAUSEDMA 0xd0 +#define ESS_CMD_ENABLEAUDIO1 0xd1 +#define ESS_CMD_STOPAUDIO1 0xd3 +#define ESS_CMD_AUDIO1STATUS 0xd8 +#define ESS_CMD_CONTDMA 0xd4 +#define ESS_CMD_TESTIRQ 0xf2 + +#define ESS_RECSRC_MIC 0 +#define ESS_RECSRC_AUXACD 2 +#define ESS_RECSRC_AUXB 5 +#define ESS_RECSRC_LINE 6 +#define ESS_RECSRC_NONE 7 + +#define DAC1 0x01 +#define ADC1 0x02 +#define DAC2 0x04 + +/* + + */ + +typedef struct _snd_es1938 es1938_t; + +struct _snd_es1938 { + int irq; + + unsigned long io_port; + struct resource *res_io_port; + unsigned long sb_port; + struct resource *res_sb_port; + unsigned long vc_port; + struct resource *res_vc_port; + unsigned long mpu_port; + struct resource *res_mpu_port; + unsigned long game_port; + struct resource *res_game_port; + unsigned long ddma_port; + + unsigned char irqmask; + unsigned char revision; + + snd_kcontrol_t *hw_volume; + snd_kcontrol_t *hw_switch; + snd_kcontrol_t *master_volume; + snd_kcontrol_t *master_switch; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback1_substream; + snd_pcm_substream_t *playback2_substream; + snd_kmixer_t *mixer; + snd_rawmidi_t *rmidi; + + unsigned int dma1_size; + unsigned int dma2_size; + unsigned int dma1_start; + unsigned int dma2_start; + unsigned int dma1_shift; + unsigned int dma2_shift; + unsigned int active; + + spinlock_t reg_lock; + spinlock_t mixer_lock; + snd_info_entry_t *proc_entry; + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + struct gameport gameport; +#endif +}; + +static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_es1938_ids[] __devinitdata = { + { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Solo-1 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_es1938_ids); + +#define RESET_LOOP_TIMEOUT 0x10000 +#define WRITE_LOOP_TIMEOUT 0x10000 +#define GET_LOOP_TIMEOUT 0x01000 + +#undef REG_DEBUG +/* ----------------------------------------------------------------- + * Write to a mixer register + * -----------------------------------------------------------------*/ +static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val) +{ + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + outb(val, SLSB_REG(chip, MIXERDATA)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x set to %02x\n", reg, val); +#endif +} + +/* ----------------------------------------------------------------- + * Read from a mixer register + * -----------------------------------------------------------------*/ +static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg) +{ + int data; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + data = inb(SLSB_REG(chip, MIXERDATA)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x now is %02x\n", reg, data); +#endif + return data; +} + +/* ----------------------------------------------------------------- + * Write to some bits of a mixer register (return old value) + * -----------------------------------------------------------------*/ +static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + unsigned long flags; + unsigned char old, new, oval; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + old = inb(SLSB_REG(chip, MIXERDATA)); + oval = old & mask; + if (val != oval) { + new = (old & ~mask) | (val & mask); + outb(new, SLSB_REG(chip, MIXERDATA)); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return oval; +} + +/* ----------------------------------------------------------------- + * Write command to Controller Registers + * -----------------------------------------------------------------*/ +static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd) +{ + int i; + unsigned char v; + for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) { + if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) { + outb(cmd, SLSB_REG(chip, WRITEDATA)); + return; + } + } + printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v); +} + +/* ----------------------------------------------------------------- + * Read the Read Data Buffer + * -----------------------------------------------------------------*/ +static int snd_es1938_get_byte(es1938_t *chip) +{ + int i; + unsigned char v; + for (i = GET_LOOP_TIMEOUT; i; i--) + if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80) + return inb(SLSB_REG(chip, READDATA)); + snd_printk("get_byte timeout: status 0x02%x\n", v); + return -ENODEV; +} + +/* ----------------------------------------------------------------- + * Write value cmd register + * -----------------------------------------------------------------*/ +static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, reg); + snd_es1938_write_cmd(chip, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x set to %02x\n", reg, val); +#endif +} + +/* ----------------------------------------------------------------- + * Read data from cmd register and return it + * -----------------------------------------------------------------*/ +static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg) +{ + unsigned char val; + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, ESS_CMD_READREG); + snd_es1938_write_cmd(chip, reg); + val = snd_es1938_get_byte(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x now is %02x\n", reg, val); +#endif + return val; +} + +/* ----------------------------------------------------------------- + * Write data to cmd register and return old value + * -----------------------------------------------------------------*/ +static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + unsigned long flags; + unsigned char old, new, oval; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, ESS_CMD_READREG); + snd_es1938_write_cmd(chip, reg); + old = snd_es1938_get_byte(chip); + oval = old & mask; + if (val != oval) { + snd_es1938_write_cmd(chip, reg); + new = (old & ~mask) | (val & mask); + snd_es1938_write_cmd(chip, new); +#ifdef REG_DEBUG + snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return oval; +} + +/* -------------------------------------------------------------------- + * Reset the chip + * --------------------------------------------------------------------*/ +static void snd_es1938_reset(es1938_t *chip) +{ + int i; + + outb(3, SLSB_REG(chip, RESET)); + inb(SLSB_REG(chip, RESET)); + outb(0, SLSB_REG(chip, RESET)); + for (i = 0; i < RESET_LOOP_TIMEOUT; i++) { + if (inb(SLSB_REG(chip, STATUS)) & 0x80) { + if (inb(SLSB_REG(chip, READDATA)) == 0xaa) + goto __next; + } + } + snd_printk("ESS Solo-1 reset failed\n"); + + __next: + snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT); + + /* Demand transfer DMA: 4 bytes per DMA request */ + snd_es1938_write(chip, ESS_CMD_DMATYPE, 2); + + /* Change behaviour of register A1 + 4x oversampling + 2nd channel DAC asynchronous */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32); + /* enable/select DMA channel and IRQ channel */ + snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50); + snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50); + snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1); + /* Set spatializer parameters to recommended values */ + snd_es1938_mixer_write(chip, 0x54, 0x8f); + snd_es1938_mixer_write(chip, 0x56, 0x95); + snd_es1938_mixer_write(chip, 0x58, 0x94); + snd_es1938_mixer_write(chip, 0x5a, 0x80); +} + +/* -------------------------------------------------------------------- + * Reset the FIFOs + * --------------------------------------------------------------------*/ +static void snd_es1938_reset_fifo(es1938_t *chip) +{ + outb(2, SLSB_REG(chip, RESET)); + outb(0, SLSB_REG(chip, RESET)); +} + +static ratnum_t clocks[2] = { + { + .num = 793800, + .den_min = 1, + .den_max = 128, + .den_step = 1, + }, + { + .num = 768000, + .den_min = 1, + .den_max = 128, + .den_step = 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + .nrats = 2, + .rats = clocks, +}; + + +static void snd_es1938_rate_set(es1938_t *chip, + snd_pcm_substream_t *substream, + int mode) +{ + unsigned int bits, div0; + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->rate_num == clocks[0].num) + bits = 128 - runtime->rate_den; + else + bits = 256 - runtime->rate_den; + + /* set filter register */ + div0 = 256 - 7160000*20/(8*82*runtime->rate); + + if (mode == DAC2) { + snd_es1938_mixer_write(chip, 0x70, bits); + snd_es1938_mixer_write(chip, 0x72, div0); + } else { + snd_es1938_write(chip, 0xA1, bits); + snd_es1938_write(chip, 0xA2, div0); + } +} + +/* -------------------------------------------------------------------- + * Configure Solo1 builtin DMA Controller + * --------------------------------------------------------------------*/ + +static void snd_es1938_playback1_setdma(es1938_t *chip) +{ + outb(0x00, SLIO_REG(chip, AUDIO2MODE)); + outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR)); + outw(0, SLIO_REG(chip, AUDIO2DMACOUNT)); + outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT)); +} + +static void snd_es1938_playback2_setdma(es1938_t *chip) +{ + /* Enable DMA controller */ + outb(0xc4, SLDM_REG(chip, DMACOMMAND)); + /* 1. Master reset */ + outb(0, SLDM_REG(chip, DMACLEAR)); + /* 2. Mask DMA */ + outb(1, SLDM_REG(chip, DMAMASK)); + outb(0x18, SLDM_REG(chip, DMAMODE)); + outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); + /* 3. Unmask DMA */ + outb(0, SLDM_REG(chip, DMAMASK)); +} + +static void snd_es1938_capture_setdma(es1938_t *chip) +{ + /* Enable DMA controller */ + outb(0xc4, SLDM_REG(chip, DMACOMMAND)); + /* 1. Master reset */ + outb(0, SLDM_REG(chip, DMACLEAR)); + /* 2. Mask DMA */ + outb(1, SLDM_REG(chip, DMAMASK)); + outb(0x14, SLDM_REG(chip, DMAMODE)); + outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); + /* 3. Unmask DMA */ + outb(0, SLDM_REG(chip, DMAMASK)); +} + +/* ---------------------------------------------------------------------- + * + * *** PCM part *** + */ + +static int snd_es1938_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + int val; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = 0x0f; + chip->active |= ADC1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = 0x00; + chip->active &= ~ADC1; + break; + default: + return -EINVAL; + } + snd_es1938_write(chip, ESS_CMD_DMACONTROL, val); + return 0; +} + +static int snd_es1938_playback1_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* According to the documentation this should be: + 0x13 but that value may randomly swap stereo channels */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93); + outb(0x0a, SLIO_REG(chip, AUDIO2MODE)); + chip->active |= DAC2; + break; + case SNDRV_PCM_TRIGGER_STOP: + outb(0, SLIO_REG(chip, AUDIO2MODE)); + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0); + chip->active &= ~DAC2; + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_es1938_playback2_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + int val; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = 5; + chip->active |= DAC1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = 0; + chip->active &= ~DAC1; + break; + default: + return -EINVAL; + } + snd_es1938_write(chip, ESS_CMD_DMACONTROL, val); + return 0; +} + +static int snd_es1938_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_trigger(substream, cmd); + case 1: + return snd_es1938_playback2_trigger(substream, cmd); + } + snd_BUG(); + return -EINVAL; +} + +/* -------------------------------------------------------------------- + * First channel for Extended Mode Audio 1 ADC Operation + * --------------------------------------------------------------------*/ +static int snd_es1938_capture_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + chip->dma1_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma1_shift = 2 - mono - is8; + + snd_es1938_reset_fifo(chip); + + /* program type */ + snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1)); + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, ADC1); + + count = 0x10000 - count; + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8); + + /* initialize and configure ADC */ + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71); + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 | + (u ? 0x00 : 0x20) | + (is8 ? 0x00 : 0x04) | + (mono ? 0x40 : 0x08)); + + // snd_es1938_reset_fifo(chip); + + /* 11. configure system interrupt controller and DMA controller */ + snd_es1938_capture_setdma(chip); + + return 0; +} + + +/* ------------------------------------------------------------------------------ + * Second Audio channel DAC Operation + * ------------------------------------------------------------------------------*/ +static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma2_size = size; + chip->dma2_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma2_shift = 2 - mono - is8; + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, DAC2); + + count >>= 1; + count = 0x10000 - count; + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff); + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8); + + /* initialize and configure Audio 2 DAC */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1)); + + /* program DMA */ + snd_es1938_playback1_setdma(chip); + + return 0; +} + +static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + chip->dma1_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma1_shift = 2 - mono - is8; + + count = 0x10000 - count; + + /* reset */ + snd_es1938_reset_fifo(chip); + + snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1)); + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, DAC1); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8); + + /* initialized and configure DAC */ + snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00); + snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71); + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, + 0x90 | (mono ? 0x40 : 0x08) | + (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20)); + + /* program DMA */ + snd_es1938_playback2_setdma(chip); + + return 0; +} + +static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_prepare(substream); + case 1: + return snd_es1938_playback2_prepare(substream); + } + snd_BUG(); + return -EINVAL; +} + +static snd_pcm_uframes_t snd_es1938_capture_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + size_t old, new; +#if 1 + /* This stuff is *needed*, don't ask why - AB */ + old = inw(SLDM_REG(chip, DMACOUNT)); + while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) + old = new; + ptr = chip->dma1_size - 1 - new; +#else + ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; +#endif + return ptr >> chip->dma1_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback1_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; +#if 1 + ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT)); +#else + ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start; +#endif + return ptr >> chip->dma2_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback2_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + size_t old, new; +#if 1 + /* This stuff is *needed*, don't ask why - AB */ + old = inw(SLDM_REG(chip, DMACOUNT)); + while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) + old = new; + ptr = chip->dma1_size - 1 - new; +#else + ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; +#endif + return ptr >> chip->dma1_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback_pointer(snd_pcm_substream_t *substream) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_pointer(substream); + case 1: + return snd_es1938_playback2_pointer(substream); + } + snd_BUG(); + return -EINVAL; +} + +static int snd_es1938_capture_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es1938_t *chip = snd_pcm_substream_chip(substream); + pos <<= chip->dma1_shift; + count <<= chip->dma1_shift; + snd_assert(pos + count <= chip->dma1_size, return -EINVAL); + if (pos + count < chip->dma1_size) + memcpy(dst, runtime->dma_area + pos + 1, count); + else { + memcpy(dst, runtime->dma_area + pos + 1, count - 1); + ((unsigned char *)dst)[count - 1] = runtime->dma_area[0]; + } + return 0; +} + +/* + * buffer management + */ +static int snd_es1938_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) + +{ + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es1938_pcm_hw_free(snd_pcm_substream_t *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* ---------------------------------------------------------------------- + * Audio1 Capture (ADC) + * ----------------------------------------------------------------------*/ +static snd_pcm_hardware_t snd_es1938_capture = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 6000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 256, +}; + +/* ----------------------------------------------------------------------- + * Audio2 Playback (DAC) + * -----------------------------------------------------------------------*/ +static snd_pcm_hardware_t snd_es1938_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 6000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 64, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 256, +}; + +static int snd_es1938_capture_open(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->playback2_substream) + return -EAGAIN; + chip->capture_substream = substream; + runtime->hw = snd_es1938_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00); + return 0; +} + +static int snd_es1938_playback_open(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + switch (substream->number) { + case 0: + chip->playback1_substream = substream; + break; + case 1: + if (chip->capture_substream) + return -EAGAIN; + chip->playback2_substream = substream; + break; + default: + snd_BUG(); + return -EINVAL; + } + runtime->hw = snd_es1938_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00); + return 0; +} + +static int snd_es1938_capture_close(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_substream = NULL; + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + return 0; +} + +static int snd_es1938_playback_close(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + switch (substream->number) { + case 0: + chip->playback1_substream = NULL; + break; + case 1: + chip->playback2_substream = NULL; + break; + default: + snd_BUG(); + return -EINVAL; + } + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + return 0; +} + +static snd_pcm_ops_t snd_es1938_playback_ops = { + .open = snd_es1938_playback_open, + .close = snd_es1938_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1938_pcm_hw_params, + .hw_free = snd_es1938_pcm_hw_free, + .prepare = snd_es1938_playback_prepare, + .trigger = snd_es1938_playback_trigger, + .pointer = snd_es1938_playback_pointer, +}; + +static snd_pcm_ops_t snd_es1938_capture_ops = { + .open = snd_es1938_capture_open, + .close = snd_es1938_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1938_pcm_hw_params, + .hw_free = snd_es1938_pcm_hw_free, + .prepare = snd_es1938_capture_prepare, + .trigger = snd_es1938_capture_trigger, + .pointer = snd_es1938_capture_pointer, + .copy = snd_es1938_capture_copy, +}; + +static void snd_es1938_free_pcm(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_es1938_free_pcm; + pcm->info_flags = 0; + strcpy(pcm->name, "ESS Solo-1"); + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* ------------------------------------------------------------------- + * + * *** Mixer part *** + */ + +static int snd_es1938_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es1938_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07; + return 0; +} + +static int snd_es1938_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = ucontrol->value.enumerated.item[0]; + + if (val > 7) + return -EINVAL; + return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val; +} + +static int snd_es1938_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1938_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = snd_es1938_mixer_read(chip, 0x50); + ucontrol->value.integer.value[0] = !!(val & 8); + return 0; +} + +static int snd_es1938_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char oval, nval; + int change; + nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; + oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c; + change = nval != oval; + if (change) { + snd_es1938_mixer_write(chip, 0x50, nval & ~0x04); + snd_es1938_mixer_write(chip, 0x50, nval); + } + return change; +} + +static int snd_es1938_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 63; + return 0; +} + +static int snd_es1938_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f; + ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f; + return 0; +} + +static int snd_es1938_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1938_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40); + ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40); + return 0; +} + +static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol) +{ + es1938_t *chip = snd_magic_cast(es1938_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_volume = NULL; + chip->master_switch = NULL; + chip->hw_volume = NULL; + chip->hw_switch = NULL; +} + +static int snd_es1938_reg_bits(es1938_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg < 0xa0) + return snd_es1938_mixer_bits(chip, reg, mask, val); + else + return snd_es1938_bits(chip, reg, mask, val); +} + +static int snd_es1938_reg_read(es1938_t *chip, unsigned char reg) +{ + if (reg < 0xa0) + return snd_es1938_mixer_read(chip, reg); + else + return snd_es1938_read(chip, reg); +} + +#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_es1938_info_single, \ + .get = snd_es1938_get_single, .put = snd_es1938_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1938_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int val; + + val = snd_es1938_reg_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es1938_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + mask <<= shift; + val <<= shift; + return snd_es1938_reg_bits(chip, reg, mask, val) != val; +} + +#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_es1938_info_double, \ + .get = snd_es1938_get_double, .put = snd_es1938_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1938_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + left = snd_es1938_reg_read(chip, left_reg); + if (left_reg != right_reg) + right = snd_es1938_reg_read(chip, right_reg); + else + right = left; + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es1938_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, mask1, mask2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + mask1 = mask << shift_left; + mask2 = mask << shift_right; + if (left_reg != right_reg) { + change = 0; + if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1) + change = 1; + if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2) + change = 1; + } else { + change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2, + val1 | val2) != (val1 | val2)); + } + return change; +} + +static snd_kcontrol_new_t snd_es1938_controls[] = { +ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Hardware Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_es1938_info_hw_volume, + .get = snd_es1938_get_hw_volume, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Hardware Master Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_es1938_info_hw_switch, + .get = snd_es1938_get_hw_switch, +}, +ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0), +ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), +ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), +ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), +ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), +ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), +ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), +ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_es1938_info_mux, + .get = snd_es1938_get_mux, + .put = snd_es1938_put_mux, +}, +ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), +ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), +ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), +ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), +ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), +ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), +ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0), +ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "3D Control - Switch", + .info = snd_es1938_info_spatializer_enable, + .get = snd_es1938_get_spatializer_enable, + .put = snd_es1938_put_spatializer_enable, +}, +ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0) +}; + + +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ + +static int snd_es1938_free(es1938_t *chip) +{ + /*if (chip->rmidi) + snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0);*/ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + if (chip->gameport.io) + gameport_unregister_port(&chip->gameport); +#endif + if (chip->res_io_port) { + release_resource(chip->res_io_port); + kfree_nocheck(chip->res_io_port); + } + if (chip->res_sb_port) { + release_resource(chip->res_sb_port); + kfree_nocheck(chip->res_sb_port); + } + if (chip->res_vc_port) { + release_resource(chip->res_vc_port); + kfree_nocheck(chip->res_vc_port); + } + if (chip->res_mpu_port) { + release_resource(chip->res_mpu_port); + kfree_nocheck(chip->res_mpu_port); + } + if (chip->res_game_port) { + release_resource(chip->res_game_port); + kfree_nocheck(chip->res_game_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1938_dev_free(snd_device_t *device) +{ + es1938_t *chip = snd_magic_cast(es1938_t, device->device_data, return -ENXIO); + return snd_es1938_free(chip); +} + +static int __devinit snd_es1938_create(snd_card_t * card, + struct pci_dev * pci, + es1938_t ** rchip) +{ + es1938_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_es1938_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + chip = snd_magic_kcalloc(es1938_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + chip->card = card; + chip->pci = pci; + chip->io_port = pci_resource_start(pci, 0); + if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 8 - 1); + return -EBUSY; + } + chip->sb_port = pci_resource_start(pci, 1); + if ((chip->res_sb_port = request_region(chip->sb_port, 0x10, "ESS Solo-1 SB")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab SB region 0x%lx-0x%lx\n", chip->sb_port, chip->sb_port + 0x10 - 1); + return -EBUSY; + } + chip->vc_port = pci_resource_start(pci, 2); + if ((chip->res_vc_port = request_region(chip->vc_port, 0x10, "ESS Solo-1 VC (DMA)")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab VC (DMA) region 0x%lx-0x%lx\n", chip->vc_port, chip->vc_port + 0x10 - 1); + return -EBUSY; + } + chip->mpu_port = pci_resource_start(pci, 3); + if ((chip->res_mpu_port = request_region(chip->mpu_port, 4, "ESS Solo-1 MIDI")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab MIDI region 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 4 - 1); + return -EBUSY; + } + chip->game_port = pci_resource_start(pci, 4); + if ((chip->res_game_port = request_region(chip->game_port, 4, "ESS Solo-1 GAME")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab GAME region 0x%lx-0x%lx\n", chip->game_port, chip->game_port + 4 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) { + snd_es1938_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; +#ifdef ES1938_DDEBUG + snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n", + chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port); +#endif + /* reset chip */ + snd_es1938_reset(chip); + + /* configure native mode */ + + /* enable bus master */ + pci_set_master(pci); + + /* disable legacy audio */ + pci_write_config_word(pci, SL_PCI_LEGACYCONTROL, 0x805f); + + /* set DDMA base */ + chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */ + pci_write_config_word(pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1); + + /* set DMA/IRQ policy */ + pci_write_config_dword(pci, SL_PCI_CONFIG, 0); + + /* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/ + outb(0xf0, SLIO_REG(chip, IRQCONTROL)); + + /* reset DMA */ + outb(0, SLDM_REG(chip, DMACLEAR)); + + /* enable bus mastering */ + pci_set_master(pci); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1938_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +/* -------------------------------------------------------------------- + * Interrupt handler + * -------------------------------------------------------------------- */ +static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1938_t *chip = snd_magic_cast(es1938_t, dev_id, return); + unsigned char status, audiostatus; + + status = inb(SLIO_REG(chip, IRQCONTROL)); +#if 0 + printk("Es1938debug - interrupt status: =0x%x\n", status); +#endif + + /* AUDIO 1 */ + if (status & 0x10) { +#if 0 + printk("Es1938debug - AUDIO channel 1 interrupt\n"); + printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT))); + printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR))); + printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS))); +#endif + /* clear irq */ + audiostatus = inb(SLSB_REG(chip, STATUS)); + if (chip->active & ADC1) + snd_pcm_period_elapsed(chip->capture_substream); + else if (chip->active & DAC1) + snd_pcm_period_elapsed(chip->playback2_substream); + } + + /* AUDIO 2 */ + if (status & 0x20) { +#if 0 + printk("Es1938debug - AUDIO channel 2 interrupt\n"); + printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT))); + printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR))); + +#endif + /* clear irq */ + snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0); + if (chip->active & DAC2) + snd_pcm_period_elapsed(chip->playback1_substream); + } + + /* Hardware volume */ + if (status & 0x40) { + int split = snd_es1938_mixer_read(chip, 0x64) & 0x80; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); + if (!split) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + /* ack interrupt */ + snd_es1938_mixer_write(chip, 0x66, 0x00); + } + + /* MPU401 */ + if (status & 0x80) { + // snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); /* ack? */ + if (chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } +} + +#define ES1938_DMA_SIZE 64 + +static int __devinit snd_es1938_mixer(snd_pcm_t *pcm) +{ + snd_card_t *card; + es1938_t *chip; + unsigned int idx; + int err; + + snd_assert(pcm != NULL && pcm->card != NULL, return -EINVAL); + + card = pcm->card; + chip = snd_pcm_chip(pcm); + + strcpy(card->mixername, pcm->name); + + for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip); + switch (idx) { + case 0: + chip->master_volume = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 1: + chip->master_switch = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 2: + chip->hw_volume = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 3: + chip->hw_switch = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + } + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + return 0; +} + + +static int __devinit snd_es1938_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + es1938_t *chip; + snd_pcm_t *pcm; + opl3_t *opl3; + int idx, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + for (idx = 0; idx < 5; idx++) { + if (pci_resource_start(pci, idx) == 0 || + !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { + snd_card_free(card); + return -ENODEV; + } + } + if ((err = snd_es1938_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1938_new_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1938_mixer(pcm)) < 0) { + snd_card_free(card); + return err; + } + if (snd_opl3_create(card, + SLSB_REG(chip, FMLOWADDR), + SLSB_REG(chip, FMHIGHADDR), + OPL3_HW_OPL3, 1, &opl3) < 0) { + printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n", + SLSB_REG(chip, FMLOWADDR)); + } else { + if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { + printk(KERN_ERR "es1938: unable to initialize MPU-401\n"); + } /*else + snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);*/ + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + chip->gameport.io = chip->game_port; + gameport_register_port(&chip->gameport); +#endif + + strcpy(card->driver, "ES1938"); + strcpy(card->shortname, "ESS ES1938 (Solo-1)"); + sprintf(card->longname, "%s rev %i, irq %i", + card->shortname, + chip->revision, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_es1938_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ESS ES1938 (Solo-1)", + .id_table = snd_es1938_ids, + .probe = snd_es1938_probe, + .remove = __devexit_p(snd_es1938_remove), +}; + +static int __init alsa_card_es1938_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ESS Solo-1 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_es1938_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_es1938_init) +module_exit(alsa_card_es1938_exit) + +#ifndef MODULE + +/* format is: snd-es1938=enable,index,id */ + +static int __init alsa_card_es1938_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1938=", alsa_card_es1938_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/es1968.c linux/sound/pci/es1968.c --- linux-2.4.21-rc1.orig/sound/pci/es1968.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/es1968.c 2003-03-19 05:35:20.000000000 -0700 @@ -0,0 +1,2875 @@ +/* + * Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99) + * Copyright (c) by Matze Braun . + * Takashi Iwai + * + * Most of the driver code comes from Zach Brown(zab@redhat.com) + * Alan Cox OSS Driver + * Rewritted from card-es1938.c source. + * + * TODO: + * Perhaps Synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Notes from Zach Brown about the driver code + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + */ + +#define __SND_OSS_COMPAT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t es1968_t + +#define CARD_NAME "ESS Maestro1/2" +#define DRIVER_NAME "ES1968" + +MODULE_DESCRIPTION("ESS Maestro"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +MODULE_DEVICES("{{ESS,Maestro 2e}," + "{ESS,Maestro 2}," + "{ESS,Maestro 1}," + "{TerraTec,DMX}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 }; +static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 }; +static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 }; +static int clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(total_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(total_bufsize, "Total buffer size in kB."); +MODULE_PARM_SYNTAX(total_bufsize, SNDRV_ENABLED ",allows:{{1,4096}},skill:advanced"); +MODULE_PARM(pcm_substreams_p, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(pcm_substreams_p, SNDRV_ENABLED ",allows:{{1,8}}"); +MODULE_PARM(pcm_substreams_c, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(pcm_substreams_c, SNDRV_ENABLED ",allows:{{0,8}}"); +MODULE_PARM(clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(clock, "Clock on " CARD_NAME " soundcard. (0 = auto-detect)"); +MODULE_PARM_SYNTAX(clock, SNDRV_ENABLED); +MODULE_PARM(use_pm, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(use_pm, "Toggle power-management. (0 = off, 1 = on, 2 = auto)"); +MODULE_PARM_SYNTAX(use_pm, SNDRV_ENABLED ",allows:{{0,1,2}},skill:advanced"); + + +/* PCI Dev ID's */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125D +#endif + +#define PCI_VENDOR_ID_ESS_OLD 0x1285 /* Platform Tech, the people the ESS + was bought form */ + +#ifndef PCI_DEVICE_ID_ESS_M2E +#define PCI_DEVICE_ID_ESS_M2E 0x1978 +#endif +#ifndef PCI_DEVICE_ID_ESS_M2 +#define PCI_DEVICE_ID_ESS_M2 0x1968 +#endif +#ifndef PCI_DEVICE_ID_ESS_M1 +#define PCI_DEVICE_ID_ESS_M1 0x0100 +#endif + +#define NR_APUS 64 +#define NR_APU_REGS 16 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + +/* Mode Flags */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +/* Values for the ESM_LEGACY_AUDIO_CONTROL */ + +#define ESS_ENABLE_AUDIO 0x8000 +#define ESS_ENABLE_SERIAL_IRQ 0x4000 +#define IO_ADRESS_ALIAS 0x0020 +#define MPU401_IRQ_ENABLE 0x0010 +#define MPU401_IO_ENABLE 0x0008 +#define GAME_IO_ENABLE 0x0004 +#define FM_IO_ENABLE 0x0002 +#define SB_IO_ENABLE 0x0001 + +/* Values for the ESM_CONFIG_A */ + +#define PIC_SNOOP1 0x4000 +#define PIC_SNOOP2 0x2000 +#define SAFEGUARD 0x0800 +#define DMA_CLEAR 0x0700 +#define DMA_DDMA 0x0000 +#define DMA_TDMA 0x0100 +#define DMA_PCPCI 0x0200 +#define POST_WRITE 0x0080 +#define ISA_TIMING 0x0040 +#define SWAP_LR 0x0020 +#define SUBTR_DECODE 0x0002 + +/* Values for the ESM_CONFIG_B */ + +#define SPDIF_CONFB 0x0100 +#define HWV_CONFB 0x0080 +#define DEBOUNCE 0x0040 +#define GPIO_CONFB 0x0020 +#define CHI_CONFB 0x0010 +#define IDMA_CONFB 0x0008 /*undoc */ +#define MIDI_FIX 0x0004 /*undoc */ +#define IRQ_TO_ISA 0x0001 /*undoc */ + +/* Values for Ring Bus Control B */ +#define RINGB_2CODEC_ID_MASK 0x0003 +#define RINGB_DIS_VALIDATION 0x0008 +#define RINGB_EN_SPDIF 0x0010 +#define RINGB_EN_2CODEC 0x0020 +#define RINGB_SING_BIT_DUAL 0x0040 + +/* ****Port Adresses**** */ + +/* Write & Read */ +#define ESM_INDEX 0x02 +#define ESM_DATA 0x00 + +/* AC97 + RingBus */ +#define ESM_AC97_INDEX 0x30 +#define ESM_AC97_DATA 0x32 +#define ESM_RING_BUS_DEST 0x34 +#define ESM_RING_BUS_CONTR_A 0x36 +#define ESM_RING_BUS_CONTR_B 0x38 +#define ESM_RING_BUS_SDO 0x3A + +/* WaveCache*/ +#define WC_INDEX 0x10 +#define WC_DATA 0x12 +#define WC_CONTROL 0x14 + +/* ASSP*/ +#define ASSP_INDEX 0x80 +#define ASSP_MEMORY 0x82 +#define ASSP_DATA 0x84 +#define ASSP_CONTROL_A 0xA2 +#define ASSP_CONTROL_B 0xA4 +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOSTW_INDEX 0xA8 +#define ASSP_HOSTW_DATA 0xAA +#define ASSP_HOSTW_IRQ 0xAC +/* Midi */ +#define ESM_MPU401_PORT 0x98 +/* Others */ +#define ESM_PORT_HOST_IRQ 0x18 + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* PCI Register */ + +#define ESM_LEGACY_AUDIO_CONTROL 0x40 +#define ESM_ACPI_COMMAND 0x54 +#define ESM_CONFIG_A 0x50 +#define ESM_CONFIG_B 0x52 +#define ESM_DDMA 0x60 + +/* Bob Bits */ +#define ESM_BOB_ENABLE 0x0001 +#define ESM_BOB_START 0x0001 + +/* Host IRQ Control Bits */ +#define ESM_RESET_MAESTRO 0x8000 +#define ESM_RESET_DIRECTSOUND 0x4000 +#define ESM_HIRQ_ClkRun 0x0100 +#define ESM_HIRQ_HW_VOLUME 0x0040 +#define ESM_HIRQ_HARPO 0x0030 /* What's that? */ +#define ESM_HIRQ_ASSP 0x0010 +#define ESM_HIRQ_DSIE 0x0004 +#define ESM_HIRQ_MPU401 0x0002 +#define ESM_HIRQ_SB 0x0001 + +/* Host IRQ Status Bits */ +#define ESM_MPU401_IRQ 0x02 +#define ESM_SB_IRQ 0x01 +#define ESM_SOUND_IRQ 0x04 +#define ESM_ASSP_IRQ 0x10 +#define ESM_HWVOL_IRQ 0x40 + +#define ESS_SYSCLK 50000000 +#define ESM_BOB_FREQ 200 +#define ESM_BOB_FREQ_MAX 800 + +#define ESM_FREQ_ESM1 (49152000L / 1024L) /* default rate 48000 */ +#define ESM_FREQ_ESM2 (50000000L / 1024L) + +/* APU Modes: reg 0x00, bit 4-7 */ +#define ESM_APU_MODE_SHIFT 4 +#define ESM_APU_MODE_MASK (0xf << 4) +#define ESM_APU_OFF 0x00 +#define ESM_APU_16BITLINEAR 0x01 /* 16-Bit Linear Sample Player */ +#define ESM_APU_16BITSTEREO 0x02 /* 16-Bit Stereo Sample Player */ +#define ESM_APU_8BITLINEAR 0x03 /* 8-Bit Linear Sample Player */ +#define ESM_APU_8BITSTEREO 0x04 /* 8-Bit Stereo Sample Player */ +#define ESM_APU_8BITDIFF 0x05 /* 8-Bit Differential Sample Playrer */ +#define ESM_APU_DIGITALDELAY 0x06 /* Digital Delay Line */ +#define ESM_APU_DUALTAP 0x07 /* Dual Tap Reader */ +#define ESM_APU_CORRELATOR 0x08 /* Correlator */ +#define ESM_APU_INPUTMIXER 0x09 /* Input Mixer */ +#define ESM_APU_WAVETABLE 0x0A /* Wave Table Mode */ +#define ESM_APU_SRCONVERTOR 0x0B /* Sample Rate Convertor */ +#define ESM_APU_16BITPINGPONG 0x0C /* 16-Bit Ping-Pong Sample Player */ +#define ESM_APU_RESERVED1 0x0D /* Reserved 1 */ +#define ESM_APU_RESERVED2 0x0E /* Reserved 2 */ +#define ESM_APU_RESERVED3 0x0F /* Reserved 3 */ + +/* reg 0x00 */ +#define ESM_APU_FILTER_Q_SHIFT 0 +#define ESM_APU_FILTER_Q_MASK (3 << 0) +/* APU Filtey Q Control */ +#define ESM_APU_FILTER_LESSQ 0x00 +#define ESM_APU_FILTER_MOREQ 0x03 + +#define ESM_APU_FILTER_TYPE_SHIFT 2 +#define ESM_APU_FILTER_TYPE_MASK (3 << 2) +#define ESM_APU_ENV_TYPE_SHIFT 8 +#define ESM_APU_ENV_TYPE_MASK (3 << 8) +#define ESM_APU_ENV_STATE_SHIFT 10 +#define ESM_APU_ENV_STATE_MASK (3 << 10) +#define ESM_APU_END_CURVE (1 << 12) +#define ESM_APU_INT_ON_LOOP (1 << 13) +#define ESM_APU_DMA_ENABLE (1 << 14) + +/* reg 0x02 */ +#define ESM_APU_SUBMIX_GROUP_SHIRT 0 +#define ESM_APU_SUBMIX_GROUP_MASK (7 << 0) +#define ESM_APU_SUBMIX_MODE (1 << 3) +#define ESM_APU_6dB (1 << 4) +#define ESM_APU_DUAL_EFFECT (1 << 5) +#define ESM_APU_EFFECT_CHANNELS_SHIFT 6 +#define ESM_APU_EFFECT_CHANNELS_MASK (3 << 6) + +/* reg 0x03 */ +#define ESM_APU_STEP_SIZE_MASK 0x0fff + +/* reg 0x04 */ +#define ESM_APU_PHASE_SHIFT 0 +#define ESM_APU_PHASE_MASK (0xff << 0) +#define ESM_APU_WAVE64K_PAGE_SHIFT 8 /* most 8bit of wave start offset */ +#define ESM_APU_WAVE64K_PAGE_MASK (0xff << 8) + +/* reg 0x05 - wave start offset */ +/* reg 0x06 - wave end offset */ +/* reg 0x07 - wave loop length */ + +/* reg 0x08 */ +#define ESM_APU_EFFECT_GAIN_SHIFT 0 +#define ESM_APU_EFFECT_GAIN_MASK (0xff << 0) +#define ESM_APU_TREMOLO_DEPTH_SHIFT 8 +#define ESM_APU_TREMOLO_DEPTH_MASK (0xf << 8) +#define ESM_APU_TREMOLO_RATE_SHIFT 12 +#define ESM_APU_TREMOLO_RATE_MASK (0xf << 12) + +/* reg 0x09 */ +/* bit 0-7 amplitude dest? */ +#define ESM_APU_AMPLITUDE_NOW_SHIFT 8 +#define ESM_APU_AMPLITUDE_NOW_MASK (0xff << 8) + +/* reg 0x0a */ +#define ESM_APU_POLAR_PAN_SHIFT 0 +#define ESM_APU_POLAR_PAN_MASK (0x3f << 0) +/* Polar Pan Control */ +#define ESM_APU_PAN_CENTER_CIRCLE 0x00 +#define ESM_APU_PAN_MIDDLE_RADIUS 0x01 +#define ESM_APU_PAN_OUTSIDE_RADIUS 0x02 + +#define ESM_APU_FILTER_TUNING_SHIFT 8 +#define ESM_APU_FILTER_TUNING_MASK (0xff << 8) + +/* reg 0x0b */ +#define ESM_APU_DATA_SRC_A_SHIFT 0 +#define ESM_APU_DATA_SRC_A_MASK (0x7f << 0) +#define ESM_APU_INV_POL_A (1 << 7) +#define ESM_APU_DATA_SRC_B_SHIFT 8 +#define ESM_APU_DATA_SRC_B_MASK (0x7f << 8) +#define ESM_APU_INV_POL_B (1 << 15) + +#define ESM_APU_VIBRATO_RATE_SHIFT 0 +#define ESM_APU_VIBRATO_RATE_MASK (0xf << 0) +#define ESM_APU_VIBRATO_DEPTH_SHIFT 4 +#define ESM_APU_VIBRATO_DEPTH_MASK (0xf << 4) +#define ESM_APU_VIBRATO_PHASE_SHIFT 8 +#define ESM_APU_VIBRATO_PHASE_MASK (0xff << 8) + +/* reg 0x0c */ +#define ESM_APU_RADIUS_SELECT (1 << 6) + +/* APU Filter Control */ +#define ESM_APU_FILTER_2POLE_LOPASS 0x00 +#define ESM_APU_FILTER_2POLE_BANDPASS 0x01 +#define ESM_APU_FILTER_2POLE_HIPASS 0x02 +#define ESM_APU_FILTER_1POLE_LOPASS 0x03 +#define ESM_APU_FILTER_1POLE_HIPASS 0x04 +#define ESM_APU_FILTER_OFF 0x05 + +/* APU ATFP Type */ +#define ESM_APU_ATFP_AMPLITUDE 0x00 +#define ESM_APU_ATFP_TREMELO 0x01 +#define ESM_APU_ATFP_FILTER 0x02 +#define ESM_APU_ATFP_PAN 0x03 + +/* APU ATFP Flags */ +#define ESM_APU_ATFP_FLG_OFF 0x00 +#define ESM_APU_ATFP_FLG_WAIT 0x01 +#define ESM_APU_ATFP_FLG_DONE 0x02 +#define ESM_APU_ATFP_FLG_INPROCESS 0x03 + + +/* capture mixing buffer size */ +#define ESM_MIXBUF_SIZE 512 + +#define ESM_MODE_PLAY 0 +#define ESM_MODE_CAPTURE 1 + +/* acpi states */ +enum { + ACPI_D0=0, + ACPI_D1, + ACPI_D2, + ACPI_D3 +}; + +/* bits in the acpi masks */ +#define ACPI_12MHZ ( 1 << 15) +#define ACPI_24MHZ ( 1 << 14) +#define ACPI_978 ( 1 << 13) +#define ACPI_SPDIF ( 1 << 12) +#define ACPI_GLUE ( 1 << 11) +#define ACPI__10 ( 1 << 10) /* reserved */ +#define ACPI_PCIINT ( 1 << 9) +#define ACPI_HV ( 1 << 8) /* hardware volume */ +#define ACPI_GPIO ( 1 << 7) +#define ACPI_ASSP ( 1 << 6) +#define ACPI_SB ( 1 << 5) /* sb emul */ +#define ACPI_FM ( 1 << 4) /* fm emul */ +#define ACPI_RB ( 1 << 3) /* ringbus / aclink */ +#define ACPI_MIDI ( 1 << 2) +#define ACPI_GP ( 1 << 1) /* game port */ +#define ACPI_WP ( 1 << 0) /* wave processor */ + +#define ACPI_ALL (0xffff) +#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \ + ACPI_MIDI|ACPI_GP|ACPI_WP)) +#define ACPI_NONE (ACPI__10) + +/* these masks indicate which units we care about at + which states */ +static u16 acpi_state_mask[] = { + [ACPI_D0] = ACPI_ALL, + [ACPI_D1] = ACPI_SLEEP, + [ACPI_D2] = ACPI_SLEEP, + [ACPI_D3] = ACPI_NONE +}; + + +typedef struct snd_es1968 es1968_t; +typedef struct snd_esschan esschan_t; +typedef struct snd_esm_memory esm_memory_t; + +/* APU use in the driver */ +enum snd_enum_apu_type { + ESM_APU_PCM_PLAY, + ESM_APU_PCM_CAPTURE, + ESM_APU_PCM_RATECONV, + ESM_APU_FREE +}; + +/* chip type */ +enum { + TYPE_MAESTRO, TYPE_MAESTRO2, TYPE_MAESTRO2E +}; + +/* DMA Hack! */ +struct snd_esm_memory { + char *buf; + unsigned long addr; + int size; + int empty; /* status */ + struct list_head list; +}; + +/* Playback Channel */ +struct snd_esschan { + int running; + + u8 apu[4]; + u8 apu_mode[4]; + + /* playback/capture pcm buffer */ + esm_memory_t *memory; + /* capture mixer buffer */ + esm_memory_t *mixbuf; + + unsigned int hwptr; /* current hw pointer in bytes */ + unsigned int count; /* sample counter in bytes */ + unsigned int dma_size; /* total buffer size in bytes */ + unsigned int frag_size; /* period size in bytes */ + unsigned int wav_shift; + u16 base[4]; /* offset for ptr */ + + /* stereo/16bit flag */ + unsigned char fmt; + int mode; /* playback / capture */ + + int bob_freq; /* required timer frequency */ + + snd_pcm_substream_t *substream; + + /* linked list */ + struct list_head list; + +#ifdef CONFIG_PM + u16 wc_map[4]; +#endif +}; + +struct snd_es1968 { + /* Module Config */ + int total_bufsize; /* in bytes */ + + int playback_streams, capture_streams; + + unsigned int clock; /* clock */ + + /* buffer */ + struct snd_dma_device dma_dev; + struct snd_dma_buffer dma; + + /* Resources... */ + int irq; + unsigned long io_port; + struct resource *res_io_port; + int type; + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + int do_pm; /* power-management enabled */ + + /* DMA memory block */ + struct list_head buf_list; + + /* ALSA Stuff */ + ac97_t *ac97; + snd_kcontrol_t *master_switch; /* for h/w volume control */ + snd_kcontrol_t *master_volume; + + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + struct tasklet_struct hwvol_tq; + + /* Maestro Stuff */ + u16 maestro_map[32]; + atomic_t bobclient; /* active timer instancs */ + int bob_freq; /* timer frequency */ + spinlock_t bob_lock; + struct semaphore memory_mutex; /* memory lock */ + + /* APU states */ + unsigned char apu[NR_APUS]; + + /* active substreams */ + struct list_head substream_list; + spinlock_t substream_lock; + +#ifdef CONFIG_PM + u16 apu_map[NR_APUS][NR_APU_REGS]; +#endif +}; + +static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_es1968_ids[] __devinitdata = { + /* Maestro 1 */ + { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO }, + /* Maestro 2 */ + { 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2 }, + /* Maestro 2E */ + { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2E }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_es1968_ids); + +/* ********************* + * Low Level Funcs! * + *********************/ + +/* no spinlock */ +static void __maestro_write(es1968_t *chip, u16 reg, u16 data) +{ + outw(reg, chip->io_port + ESM_INDEX); + outw(data, chip->io_port + ESM_DATA); + chip->maestro_map[reg] = data; +} + +inline static void maestro_write(es1968_t *chip, u16 reg, u16 data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __maestro_write(chip, reg, data); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* no spinlock */ +static u16 __maestro_read(es1968_t *chip, u16 reg) +{ + if (READABLE_MAP & (1 << reg)) { + outw(reg, chip->io_port + ESM_INDEX); + chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA); + } + return chip->maestro_map[reg]; +} + +inline static u16 maestro_read(es1968_t *chip, u16 reg) +{ + unsigned long flags; + u16 result; + spin_lock_irqsave(&chip->reg_lock, flags); + result = __maestro_read(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* Wait for the codec bus to be free */ +static int snd_es1968_ac97_wait(es1968_t *chip) +{ + int timeout = 100000; + + while (timeout-- > 0) { + if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) + return 0; + } + snd_printd("es1968: ac97 timeout\n"); + return 1; /* timeout */ +} + +static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_es1968_ac97_wait(chip); + + /* Write the bus */ + outw(val, chip->io_port + ESM_AC97_DATA); + mdelay(1); + outb(reg, chip->io_port + ESM_AC97_INDEX); + mdelay(1); + + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg) +{ + u16 data = 0; + es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return 0); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_es1968_ac97_wait(chip); + + outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); + mdelay(1); + + if (! snd_es1968_ac97_wait(chip)) { + data = inw(chip->io_port + ESM_AC97_DATA); + mdelay(1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return data; +} + +/* no spinlock */ +static void apu_index_set(es1968_t *chip, u16 index) +{ + int i; + __maestro_write(chip, IDR1_CRAM_POINTER, index); + for (i = 0; i < 1000; i++) + if (__maestro_read(chip, IDR1_CRAM_POINTER) == index) + return; + snd_printd("es1968: APU register select failed. (Timeout)\n"); +} + +/* no spinlock */ +static void apu_data_set(es1968_t *chip, u16 data) +{ + int i; + for (i = 0; i < 1000; i++) { + if (__maestro_read(chip, IDR0_DATA_PORT) == data) + return; + __maestro_write(chip, IDR0_DATA_PORT, data); + } + snd_printd("es1968: APU register set probably failed (Timeout)!\n"); +} + +/* no spinlock */ +static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +{ + snd_assert(channel < NR_APUS, return); +#ifdef CONFIG_PM + chip->apu_map[channel][reg] = data; +#endif + reg |= (channel << 4); + apu_index_set(chip, reg); + apu_data_set(chip, data); +} + +inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __apu_set_register(chip, channel, reg, data); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg) +{ + snd_assert(channel < NR_APUS, return 0); + reg |= (channel << 4); + apu_index_set(chip, reg); + return __maestro_read(chip, IDR0_DATA_PORT); +} + +inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + spin_lock_irqsave(&chip->reg_lock, flags); + v = __apu_get_register(chip, channel, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return v; +} + +#if 0 /* ASSP is not supported */ + +static void assp_set_register(es1968_t *chip, u32 reg, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + outl(reg, chip->io_port + ASSP_INDEX); + outl(value, chip->io_port + ASSP_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u32 assp_get_register(es1968_t *chip, u32 reg) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&chip->reg_lock, flags); + outl(reg, chip->io_port + ASSP_INDEX); + value = inl(chip->io_port + ASSP_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return value; +} + +#endif + +static void wave_set_register(es1968_t *chip, u16 reg, u16 value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + outw(reg, chip->io_port + WC_INDEX); + outw(value, chip->io_port + WC_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u16 wave_get_register(es1968_t *chip, u16 reg) +{ + unsigned long flags; + u16 value; + + spin_lock_irqsave(&chip->reg_lock, flags); + outw(reg, chip->io_port + WC_INDEX); + value = inw(chip->io_port + WC_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return value; +} + +/* ******************* + * Bob the Timer! * + *******************/ + +static void snd_es1968_bob_stop(es1968_t *chip) +{ + u16 reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = __maestro_read(chip, 0x11); + reg &= ~ESM_BOB_ENABLE; + __maestro_write(chip, 0x11, reg); + reg = __maestro_read(chip, 0x17); + reg &= ~ESM_BOB_START; + __maestro_write(chip, 0x17, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_bob_start(es1968_t *chip) +{ + int prescale; + int divide; + unsigned long flags; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for (prescale = 5; prescale < 12; prescale++) + if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide = 1; + while ((prescale > 5) && (divide < 32)) { + prescale--; + divide <<= 1; + } + divide >>= 1; + + /* now fine-tune the divider for best match */ + for (; divide < 31; divide++) + if (chip->bob_freq > + ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if (divide == 0) { + divide++; + if (prescale > 5) + prescale--; + } else if (divide > 1) + divide--; + + spin_lock_irqsave(&chip->reg_lock, flags); + __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1); + __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_bob_inc(es1968_t *chip, int freq) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->bob_lock, flags); + atomic_inc(&chip->bobclient); + if (atomic_read(&chip->bobclient) == 1) { + chip->bob_freq = freq; + snd_es1968_bob_start(chip); + } else if (chip->bob_freq < freq) { + snd_es1968_bob_stop(chip); + chip->bob_freq = freq; + snd_es1968_bob_start(chip); + } + spin_unlock_irqrestore(&chip->bob_lock, flags); +} + +static void snd_es1968_bob_dec(es1968_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->bob_lock, flags); + atomic_dec(&chip->bobclient); + if (atomic_read(&chip->bobclient) <= 0) + snd_es1968_bob_stop(chip); + else if (chip->bob_freq > ESM_BOB_FREQ) { + /* check reduction of timer frequency */ + struct list_head *p; + int max_freq = ESM_BOB_FREQ; + spin_lock(&chip->substream_lock); + list_for_each(p, &chip->substream_list) { + esschan_t *es = list_entry(p, esschan_t, list); + if (max_freq < es->bob_freq) + max_freq = es->bob_freq; + } + spin_unlock(&chip->substream_lock); + if (max_freq != chip->bob_freq) { + snd_es1968_bob_stop(chip); + chip->bob_freq = max_freq; + snd_es1968_bob_start(chip); + } + } + spin_unlock_irqrestore(&chip->bob_lock, flags); +} + +static int +snd_es1968_calc_bob_rate(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + /* we acquire 4 interrupts per period for precise control.. */ + int freq = runtime->rate * 4; + if (es->fmt & ESS_FMT_STEREO) + freq <<= 1; + if (es->fmt & ESS_FMT_16BIT) + freq <<= 1; + freq /= es->frag_size; + if (freq < ESM_BOB_FREQ) + freq = ESM_BOB_FREQ; + else if (freq > ESM_BOB_FREQ_MAX) + freq = ESM_BOB_FREQ_MAX; + return freq; +} + + +/************* + * PCM Part * + *************/ + +static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq) +{ + u32 rate = (freq << 16) / chip->clock; +#if 0 /* XXX: do we need this? */ + if (rate > 0x10000) + rate = 0x10000; +#endif + return rate; +} + +/* get current pointer */ +inline static unsigned int +snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es) +{ + unsigned int offset; + + offset = apu_get_register(chip, es->apu[0], 5); + + offset -= es->base[0]; + + return (offset & 0xFFFE); /* hardware is in words */ +} + +static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq) +{ + apu_set_register(chip, apu, 2, + (apu_get_register(chip, apu, 2) & 0x00FF) | + ((freq & 0xff) << 8) | 0x10); + apu_set_register(chip, apu, 3, freq >> 8); +} + +/* spin lock held */ +inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode) +{ + /* dma on, no envelopes, filter to all 1s) */ + __apu_set_register(esm, apu, 0, 0x400f | mode); +} + +static void snd_es1968_pcm_start(es1968_t *chip, esschan_t *es) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (es->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + __apu_set_register(chip, es->apu[0], 5, es->base[0]); + snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]); + if (es->mode == ESM_MODE_CAPTURE) { + __apu_set_register(chip, es->apu[2], 5, es->base[2]); + snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]); + } + if (es->fmt & ESS_FMT_STEREO) { + __apu_set_register(chip, es->apu[1], 5, es->base[1]); + snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]); + if (es->mode == ESM_MODE_CAPTURE) { + __apu_set_register(chip, es->apu[3], 5, es->base[3]); + snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]); + } + } + es->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_pcm_stop(es1968_t *chip, esschan_t *es) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (! es->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_es1968_trigger_apu(chip, es->apu[0], 0); + snd_es1968_trigger_apu(chip, es->apu[1], 0); + if (es->mode == ESM_MODE_CAPTURE) { + snd_es1968_trigger_apu(chip, es->apu[2], 0); + snd_es1968_trigger_apu(chip, es->apu[3], 0); + } + es->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* set the wavecache control reg */ +static void snd_es1968_program_wavecache(es1968_t *chip, esschan_t *es, + int channel, u32 addr, int capture) +{ + u32 tmpval = (addr - 0x10) & 0xFFF8; + + if (! capture) { + if (!(es->fmt & ESS_FMT_16BIT)) + tmpval |= 4; /* 8bit */ + if (es->fmt & ESS_FMT_STEREO) + tmpval |= 2; /* stereo */ + } + + /* set the wavecache control reg */ + wave_set_register(chip, es->apu[channel] << 3, tmpval); + +#ifdef CONFIG_PM + es->wc_map[channel] = tmpval; +#endif +} + + +static void snd_es1968_playback_setup(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + u32 pa; + int high_apu = 0; + int channel, apu; + int i, size; + unsigned long flags; + u32 freq; + + size = es->dma_size >> es->wav_shift; + + if (es->fmt & ESS_FMT_STEREO) + high_apu++; + + for (channel = 0; channel <= high_apu; channel++) { + apu = es->apu[channel]; + + snd_es1968_program_wavecache(chip, es, channel, es->memory->addr, 0); + + /* Offset to PCMBAR */ + pa = es->memory->addr; + pa -= chip->dma.addr; + pa >>= 1; /* words */ + + pa |= 0x00400000; /* System RAM (Bit 22) */ + + if (es->fmt & ESS_FMT_STEREO) { + /* Enable stereo */ + if (channel) + pa |= 0x00800000; /* (Bit 23) */ + if (es->fmt & ESS_FMT_16BIT) + pa >>= 1; + } + + /* base offset of dma calcs when reading the pointer + on this left one */ + es->base[channel] = pa & 0xFFFF; + + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + /* Load the buffer into the wave engine */ + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); + apu_set_register(chip, apu, 5, pa & 0xFFFF); + apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF); + /* setting loop == sample len */ + apu_set_register(chip, apu, 7, size); + + /* clear effects/env.. */ + apu_set_register(chip, apu, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(chip, apu, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(chip, apu, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + // apu_set_register(chip, apu, 0, 0x400F); + + if (es->fmt & ESS_FMT_16BIT) + es->apu_mode[channel] = 0x10; /* 16bit mono */ + else + es->apu_mode[channel] = 0x30; /* 8bit mono */ + + if (es->fmt & ESS_FMT_STEREO) { + /* set panning: left or right */ + /* Check: different panning. On my Canyon 3D Chipset the + Channels are swapped. I don't know, about the output + to the SPDif Link. Perhaps you have to change this + and not the APU Regs 4-5. */ + apu_set_register(chip, apu, 10, + 0x8F00 | (channel ? 0 : 0x10)); + es->apu_mode[channel] += 0x10; /* stereo */ + } else + apu_set_register(chip, apu, 10, 0x8F08); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + /* clear WP interrupts */ + outw(1, chip->io_port + 0x04); + /* enable WP ints */ + outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + freq = runtime->rate; + /* set frequency */ + if (freq > 48000) + freq = 48000; + if (freq < 4000) + freq = 4000; + + /* hmmm.. */ + if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO)) + freq >>= 1; + + freq = snd_es1968_compute_rate(chip, freq); + + /* Load the frequency, turn on 6dB */ + snd_es1968_apu_set_freq(chip, es->apu[0], freq); + snd_es1968_apu_set_freq(chip, es->apu[1], freq); +} + +static void snd_es1968_capture_setup(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + int apu_step = 2; + int channel, apu; + int i, size; + u32 freq; + unsigned long flags; + + size = es->dma_size >> es->wav_shift; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if (es->fmt & ESS_FMT_STEREO) + apu_step = 1; + + /* APU assignments: + 0 = mono/left SRC + 1 = right SRC + 2 = mono/left Input Mixer + 3 = right Input Mixer */ + for (channel = 0; channel < 4; channel += apu_step) { + int bsize, route; + u32 pa; + + apu = es->apu[channel]; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if (channel & 2) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if (!(channel & 0x01)) { + pa = es->mixbuf->addr; + } else { + pa = es->mixbuf->addr + ESM_MIXBUF_SIZE / 2; + } + + /* we source from a 'magic' apu */ + bsize = ESM_MIXBUF_SIZE / 4; /* half of this channels alloc, in words */ + /* parallel in crap, see maestro reg 0xC [8-11] */ + route = 0x14 + channel - 2; + es->apu_mode[channel] = 0x90; /* Input Mixer */ + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if (!(channel & 0x01)) + pa = es->memory->addr; + else + pa = es->memory->addr + size * 2; /* size is in word */ + + es->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = es->apu[channel + 2]; + } + + /* set the wavecache control reg */ + snd_es1968_program_wavecache(chip, es, channel, pa, 1); + + /* Offset to PCMBAR */ + pa -= chip->dma.addr; + pa >>= 1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + es->base[channel] = pa & 0xFFFF; + + pa |= 0x00400000; /* bit 22 -> System RAM */ + + /* Begin loading the APU */ + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(chip, apu, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); + /* XXX reg is little endian.. */ + apu_set_register(chip, apu, 5, pa & 0xFFFF); + apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF); + apu_set_register(chip, apu, 7, bsize); +#if 0 + if (es->fmt & ESS_FMT_STEREO) /* ??? really ??? */ + apu_set_register(chip, apu, 7, bsize - 1); +#endif + + /* clear effects/env.. */ + apu_set_register(chip, apu, 8, 0x00F0); + /* amplitude now? sure. why not. */ + apu_set_register(chip, apu, 9, 0x0000); + /* set filter tune, radius, polar pan */ + apu_set_register(chip, apu, 10, 0x8F08); + /* route input */ + apu_set_register(chip, apu, 11, route); + /* dma on, no envelopes, filter to all 1s) */ + // apu_set_register(chip, apu, 0, 0x400F); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + /* clear WP interrupts */ + outw(1, chip->io_port + 0x04); + /* enable WP ints */ + outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + freq = runtime->rate; + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (freq > 47999) + freq = 47999; + if (freq < 4000) + freq = 4000; + + freq = snd_es1968_compute_rate(chip, freq); + + /* Load the frequency, turn on 6dB */ + snd_es1968_apu_set_freq(chip, es->apu[0], freq); + snd_es1968_apu_set_freq(chip, es->apu[1], freq); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + snd_es1968_apu_set_freq(chip, es->apu[2], freq); + snd_es1968_apu_set_freq(chip, es->apu[3], freq); +} + +/******************* + * ALSA Interface * + *******************/ + +static int snd_es1968_pcm_prepare(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + + es->dma_size = snd_pcm_lib_buffer_bytes(substream); + es->frag_size = snd_pcm_lib_period_bytes(substream); + + es->wav_shift = 1; /* maestro handles always 16bit */ + es->fmt = 0; + if (snd_pcm_format_width(runtime->format) == 16) + es->fmt |= ESS_FMT_16BIT; + if (runtime->channels > 1) { + es->fmt |= ESS_FMT_STEREO; + if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */ + es->wav_shift++; + } + es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime); + + switch (es->mode) { + case ESM_MODE_PLAY: + snd_es1968_playback_setup(chip, es, runtime); + break; + case ESM_MODE_CAPTURE: + snd_es1968_capture_setup(chip, es, runtime); + break; + } + + return 0; +} + +static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (es->running) + return 0; + snd_es1968_bob_inc(chip, es->bob_freq); + es->count = 0; + es->hwptr = 0; + snd_es1968_pcm_start(chip, es); + spin_lock_irqsave(&chip->substream_lock, flags); + list_add(&es->list, &chip->substream_list); + spin_unlock_irqrestore(&chip->substream_lock, flags); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (! es->running) + return 0; + snd_es1968_pcm_stop(chip, es); + spin_lock_irqsave(&chip->substream_lock, flags); + list_del(&es->list); + spin_unlock_irqrestore(&chip->substream_lock, flags); + snd_es1968_bob_dec(chip); + break; + } + return 0; +} + +static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + unsigned int ptr; + + ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; + + return bytes_to_frames(substream->runtime, ptr % es->dma_size); +} + +static snd_pcm_hardware_t snd_es1968_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 256, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_es1968_capture = { + .info = (SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 256, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* ************************* + * DMA memory management * + *************************/ + +/* Because the Maestro can only take addresses relative to the PCM base address + register :( */ + +static int calc_available_memory_size(es1968_t *chip) +{ + struct list_head *p; + int max_size = 0; + + down(&chip->memory_mutex); + list_for_each(p, &chip->buf_list) { + esm_memory_t *buf = list_entry(p, esm_memory_t, list); + if (buf->empty && buf->size > max_size) + max_size = buf->size; + } + up(&chip->memory_mutex); + if (max_size >= 128*1024) + max_size = 127*1024; + return max_size; +} + +/* allocate a new memory chunk with the specified size */ +static esm_memory_t *snd_es1968_new_memory(es1968_t *chip, int size) +{ + esm_memory_t *buf; + struct list_head *p; + + down(&chip->memory_mutex); + list_for_each(p, &chip->buf_list) { + buf = list_entry(p, esm_memory_t, list); + if (buf->empty && buf->size >= size) + goto __found; + } + up(&chip->memory_mutex); + return NULL; + +__found: + if (buf->size > size) { + esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) { + up(&chip->memory_mutex); + return NULL; + } + chunk->size = buf->size - size; + chunk->buf = buf->buf + size; + chunk->addr = buf->addr + size; + chunk->empty = 1; + buf->size = size; + list_add(&chunk->list, &buf->list); + } + buf->empty = 0; + up(&chip->memory_mutex); + return buf; +} + +/* free a memory chunk */ +static void snd_es1968_free_memory(es1968_t *chip, esm_memory_t *buf) +{ + esm_memory_t *chunk; + + down(&chip->memory_mutex); + buf->empty = 1; + if (buf->list.prev != &chip->buf_list) { + chunk = list_entry(buf->list.prev, esm_memory_t, list); + if (chunk->empty) { + chunk->size += buf->size; + list_del(&buf->list); + kfree(buf); + buf = chunk; + } + } + if (buf->list.next != &chip->buf_list) { + chunk = list_entry(buf->list.next, esm_memory_t, list); + if (chunk->empty) { + buf->size += chunk->size; + list_del(&chunk->list); + kfree(chunk); + } + } + up(&chip->memory_mutex); +} + +static void snd_es1968_free_dmabuf(es1968_t *chip) +{ + struct list_head *p; + + if (! chip->dma.area) + return; + snd_dma_free_reserved(&chip->dma_dev); + while ((p = chip->buf_list.next) != &chip->buf_list) { + esm_memory_t *chunk = list_entry(p, esm_memory_t, list); + list_del(p); + kfree(chunk); + } +} + +static int __devinit +snd_es1968_init_dmabuf(es1968_t *chip) +{ + esm_memory_t *chunk; + + snd_dma_device_pci(&chip->dma_dev, chip->pci, 0); + if (! snd_dma_get_reserved(&chip->dma_dev, &chip->dma)) { + chip->dma.area = snd_malloc_pci_pages_fallback(chip->pci, chip->total_bufsize, + &chip->dma.addr, &chip->dma.bytes); + if (chip->dma.area == NULL) { + snd_printk("es1968: can't allocate dma pages for size %d\n", + chip->total_bufsize); + return -ENOMEM; + } + if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) { + snd_dma_free_pages(&chip->dma_dev, &chip->dma); + snd_printk("es1968: DMA buffer beyond 256MB.\n"); + return -ENOMEM; + } + snd_dma_set_reserved(&chip->dma_dev, &chip->dma); + } + + INIT_LIST_HEAD(&chip->buf_list); + /* allocate an empty chunk */ + chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) { + snd_es1968_free_dmabuf(chip); + return -ENOMEM; + } + memset(chip->dma.area, 0, 512); + chunk->buf = chip->dma.area + 512; + chunk->addr = chip->dma.addr + 512; + chunk->size = chip->dma.bytes - 512; + chunk->empty = 1; + list_add(&chunk->list, &chip->buf_list); + + return 0; +} + +/* setup the dma_areas */ +/* buffer is extracted from the pre-allocated memory chunk */ +static int snd_es1968_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + int size = params_buffer_bytes(hw_params); + + if (chan->memory) { + if (chan->memory->size >= size) { + runtime->dma_bytes = size; + return 0; + } + snd_es1968_free_memory(chip, chan->memory); + } + chan->memory = snd_es1968_new_memory(chip, size); + if (chan->memory == NULL) { + // snd_printd("cannot allocate dma buffer: size = %d\n", size); + return -ENOMEM; + } + runtime->dma_bytes = size; + runtime->dma_area = chan->memory->buf; + runtime->dma_addr = chan->memory->addr; + return 1; /* area was changed */ +} + +/* remove dma areas if allocated */ +static int snd_es1968_hw_free(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *chan; + + if (runtime->private_data == NULL) + return 0; + chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + if (chan->memory) { + snd_es1968_free_memory(chip, chan->memory); + chan->memory = NULL; + } + return 0; +} + + +/* + * allocate APU pair + */ +static int snd_es1968_alloc_apu_pair(es1968_t *chip, int type) +{ + int apu; + + for (apu = 0; apu < NR_APUS; apu += 2) { + if (chip->apu[apu] == ESM_APU_FREE && + chip->apu[apu + 1] == ESM_APU_FREE) { + chip->apu[apu] = chip->apu[apu + 1] = type; + return apu; + } + } + return -EBUSY; +} + +/* + * release APU pair + */ +static void snd_es1968_free_apu_pair(es1968_t *chip, int apu) +{ + chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE; +} + + +/****************** + * PCM open/close * + ******************/ + +static int snd_es1968_playback_open(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es; + int apu1; + + /* search 2 APUs */ + apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY); + if (apu1 < 0) + return apu1; + + es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + if (!es) { + snd_es1968_free_apu_pair(chip, apu1); + return -ENOMEM; + } + + es->apu[0] = apu1; + es->apu[1] = apu1 + 1; + es->apu_mode[0] = 0; + es->apu_mode[1] = 0; + es->running = 0; + es->substream = substream; + es->mode = ESM_MODE_PLAY; + INIT_LIST_HEAD(&es->list); + + runtime->private_data = es; + runtime->hw = snd_es1968_playback; + runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = + calc_available_memory_size(chip); + + return 0; +} + +static int snd_es1968_capture_copy(snd_pcm_substream_t *substream, + int channel, snd_pcm_uframes_t pos, + void *buf, snd_pcm_uframes_t count) +{ + //es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + char *src = runtime->dma_area; + + if (runtime->channels == 1) + return copy_to_user(buf, src + pos, count) ? -EFAULT : 0; + else { + count /= 2; + pos /= 2; + if (copy_to_user(buf, src + pos, count)) + return -EFAULT; + if (copy_to_user(buf + count, src + pos + es->dma_size/2, count)) + return -EFAULT; + return 0; + } +} + +static int snd_es1968_capture_open(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + int apu1, apu2; + + apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE); + if (apu1 < 0) + return apu1; + apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV); + if (apu2 < 0) { + snd_es1968_free_apu_pair(chip, apu1); + return apu2; + } + + es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + if (!es) { + snd_es1968_free_apu_pair(chip, apu1); + snd_es1968_free_apu_pair(chip, apu2); + return -ENOMEM; + } + + es->apu[0] = apu1; + es->apu[1] = apu1 + 1; + es->apu[2] = apu2; + es->apu[3] = apu2 + 1; + es->apu_mode[0] = 0; + es->apu_mode[1] = 0; + es->apu_mode[2] = 0; + es->apu_mode[3] = 0; + es->running = 0; + es->substream = substream; + es->mode = ESM_MODE_CAPTURE; + INIT_LIST_HEAD(&es->list); + + /* get mixbuffer */ + if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) { + snd_es1968_free_apu_pair(chip, apu1); + snd_es1968_free_apu_pair(chip, apu2); + snd_magic_kfree(es); + return -ENOMEM; + } + + runtime->private_data = es; + runtime->hw = snd_es1968_capture; + runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = + calc_available_memory_size(chip) - 1024; + + return 0; +} + +static int snd_es1968_playback_close(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + if (substream->runtime->private_data == NULL) + return 0; + es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + snd_es1968_free_apu_pair(chip, es->apu[0]); + snd_magic_kfree(es); + + return 0; +} + +static int snd_es1968_capture_close(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + if (substream->runtime->private_data == NULL) + return 0; + es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + snd_es1968_free_memory(chip, es->mixbuf); + snd_es1968_free_apu_pair(chip, es->apu[0]); + snd_es1968_free_apu_pair(chip, es->apu[2]); + snd_magic_kfree(es); + + return 0; +} + +static snd_pcm_ops_t snd_es1968_playback_ops = { + .open = snd_es1968_playback_open, + .close = snd_es1968_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1968_hw_params, + .hw_free = snd_es1968_hw_free, + .prepare = snd_es1968_pcm_prepare, + .trigger = snd_es1968_pcm_trigger, + .pointer = snd_es1968_pcm_pointer, +}; + +static snd_pcm_ops_t snd_es1968_capture_ops = { + .open = snd_es1968_capture_open, + .close = snd_es1968_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1968_hw_params, + .hw_free = snd_es1968_hw_free, + .prepare = snd_es1968_pcm_prepare, + .trigger = snd_es1968_pcm_trigger, + .pointer = snd_es1968_pcm_pointer, + .copy = snd_es1968_capture_copy, +}; + + +/* + * measure clock + */ +#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */ + +static void __devinit es1968_measure_clock(es1968_t *chip) +{ + int i, apu; + unsigned int pa, offset, t; + esm_memory_t *memory; + unsigned long flags; + struct timeval start_time, stop_time; + + if (chip->clock == 0) + chip->clock = 48000; /* default clock value */ + + /* search 2 APUs (although one apu is enough) */ + if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) { + snd_printk("Hmm, cannot find empty APU pair!?\n"); + return; + } + if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) { + snd_printk("cannot allocate dma buffer - using default clock %d\n", chip->clock); + snd_es1968_free_apu_pair(chip, apu); + return; + } + + memset(memory->buf, 0, CLOCK_MEASURE_BUFSIZE); + + wave_set_register(chip, apu << 3, (memory->addr - 0x10) & 0xfff8); + + pa = (unsigned int)((memory->addr - chip->dma.addr) >> 1); + pa |= 0x00400000; /* System RAM (Bit 22) */ + + /* initialize apu */ + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8); + apu_set_register(chip, apu, 5, pa & 0xffff); + apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff); + apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2); + apu_set_register(chip, apu, 8, 0x0000); + apu_set_register(chip, apu, 9, 0xD000); + apu_set_register(chip, apu, 10, 0x8F08); + apu_set_register(chip, apu, 11, 0x0000); + spin_lock_irqsave(&chip->reg_lock, flags); + outw(1, chip->io_port + 0x04); /* clear WP interrupts */ + outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */ + + spin_lock_irqsave(&chip->reg_lock, flags); + __apu_set_register(chip, apu, 5, pa & 0xffff); + snd_es1968_trigger_apu(chip, apu, 0x10); /* 16bit mono */ + do_gettimeofday(&start_time); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); /* 50 msec */ +#else + /* FIXME: + * schedule() above may be too inaccurate and the pointer can + * overlap the boundary.. + */ + mdelay(50); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + offset = __apu_get_register(chip, apu, 5); + do_gettimeofday(&stop_time); + snd_es1968_trigger_apu(chip, apu, 0); /* stop */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* check the current position */ + offset -= (pa & 0xffff); + offset &= 0xfffe; + + t = stop_time.tv_sec - start_time.tv_sec; + t *= 1000000; + if (stop_time.tv_usec < start_time.tv_usec) + t -= start_time.tv_usec - stop_time.tv_usec; + else + t += stop_time.tv_usec - start_time.tv_usec; + if (t == 0) { + snd_printk("?? calculation error..\n"); + } else { + offset *= 1000; + offset = (offset / t) * 1000 + ((offset % t) * 1000) / t; + if (offset < 47500 || offset > 48500) { + if (offset >= 40000 && offset <= 50000) + chip->clock = (chip->clock * offset) / 48000; + } + printk(KERN_INFO "es1968: clocking to %d\n", chip->clock); + } + snd_es1968_free_memory(chip, memory); + snd_es1968_free_apu_pair(chip, apu); +} + + +/* + */ + +static void snd_es1968_pcm_free(snd_pcm_t *pcm) +{ + es1968_t *esm = snd_magic_cast(es1968_t, pcm->private_data, return); + snd_es1968_free_dmabuf(esm); + esm->pcm = NULL; +} + +static int __devinit +snd_es1968_pcm(es1968_t *chip, int device) +{ + snd_pcm_t *pcm; + int err; + + /* get DMA buffer */ + if ((err = snd_es1968_init_dmabuf(chip)) < 0) + return err; + + /* set PCMBAR */ + wave_set_register(chip, 0x01FC, chip->dma.addr >> 12); + wave_set_register(chip, 0x01FD, chip->dma.addr >> 12); + wave_set_register(chip, 0x01FE, chip->dma.addr >> 12); + wave_set_register(chip, 0x01FF, chip->dma.addr >> 12); + + if ((err = snd_pcm_new(chip->card, "ESS Maestro", device, + chip->playback_streams, + chip->capture_streams, &pcm)) < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = snd_es1968_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops); + + pcm->info_flags = 0; + + strcpy(pcm->name, "ESS Maestro"); + + chip->pcm = pcm; + + return 0; +} + +/* + * update pointer + */ +static void snd_es1968_update_pcm(es1968_t *chip, esschan_t *es) +{ + unsigned int hwptr; + unsigned int diff; + snd_pcm_substream_t *subs = es->substream; + + if (subs == NULL || !es->running) + return; + + hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; + hwptr %= es->dma_size; + + diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size; + + es->hwptr = hwptr; + es->count += diff; + + if (es->count > es->frag_size) { + spin_unlock(&chip->substream_lock); + snd_pcm_period_elapsed(subs); + spin_lock(&chip->substream_lock); + es->count %= es->frag_size; + } +} + +/* + */ +static void es1968_update_hw_volume(unsigned long private_data) +{ + es1968_t *chip = snd_magic_cast(es1968_t, (void*)private_data, return); + int x, val; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(chip->io_port + 0x1c); + /* Reset the volume control registers. */ + outb(0x88, chip->io_port + 0x1c); + outb(0x88, chip->io_port + 0x1d); + outb(0x88, chip->io_port + 0x1e); + outb(0x88, chip->io_port + 0x1f); + + if (! chip->master_switch || ! chip->master_volume) + return; + + /* FIXME: more clean up is needed.. */ + val = chip->ac97->regs[AC97_MASTER]; + if (x & 1) { + /* mute */ + snd_ac97_write_cache(chip->ac97, AC97_MASTER, val ^ 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_switch->id); + } else { + val &= 0x7fff; + if (((x>>1) & 7) > 4) { + /* volume up */ + if ((val & 0xff) > 0) + val--; + if ((val & 0xff00) > 0) + val -= 0x0100; + } else { + /* volume down */ + if ((val & 0xff) < 0x1f) + val++; + if ((val & 0xff00) < 0x1f00) + val += 0x0100; + } + snd_ac97_write_cache(chip->ac97, AC97_MASTER, val); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); + } +} + +/* + * interrupt handler + */ +static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1968_t *chip = snd_magic_cast(es1968_t, dev_id, return); + u32 event; + + if (!(event = inb(chip->io_port + 0x1A))) + return; + + outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); + + if (event & ESM_HWVOL_IRQ) + tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */ + + /* else ack 'em all, i imagine */ + outb(0xFF, chip->io_port + 0x1A); + + if ((event & ESM_MPU401_IRQ) && chip->rmidi) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } + + if (event & ESM_SOUND_IRQ) { + struct list_head *p, *n; + spin_lock(&chip->substream_lock); + /* we need to use list_for_each_safe here since the substream + * can be deleted in period_elapsed(). + */ + list_for_each_safe(p, n, &chip->substream_list) { + esschan_t *es = list_entry(p, esschan_t, list); + snd_es1968_update_pcm(chip, es); + } + spin_unlock(&chip->substream_lock); + } +} + +/* + * Mixer stuff + */ + +static int __devinit +snd_es1968_mixer(es1968_t *chip) +{ + ac97_t ac97; + snd_ctl_elem_id_t id; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_es1968_ac97_write; + ac97.read = snd_es1968_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + /* attach master switch / volumes for h/w volume control */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &id); + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &id); + + return 0; +} + +/* + * reset ac97 codec + */ + +static void snd_es1968_ac97_reset(es1968_t *chip) +{ + unsigned long ioaddr = chip->io_port; + + unsigned short save_ringbus_a; + unsigned short save_68; + unsigned short w; + unsigned int vend; + + /* save configuration */ + save_ringbus_a = inw(ioaddr + 0x36); + + //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */ + /* set command/status address i/o to 1st codec */ + outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* disable ac link */ + outw(0x0000, ioaddr + 0x36); + save_68 = inw(ioaddr + 0x68); + pci_read_config_word(chip->pci, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if (w & 1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* unmask gpio 0 */ + outw(0x0001, ioaddr + 0x68); /* gpio write */ + outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */ + udelay(20); + outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */ + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38); + outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a); + outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c); + + /* now the second codec */ + /* disable ac link */ + outw(0x0000, ioaddr + 0x36); + outw(0xfff7, ioaddr + 0x64); /* unmask gpio 3 */ + save_68 = inw(ioaddr + 0x68); + outw(0x0009, ioaddr + 0x68); /* gpio write 0 & 3 ?? */ + outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */ + udelay(20); + outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */ + mdelay(500); /* .. ouch.. */ + //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + snd_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80 | 0x7c, ioaddr + 0x30); + for (w = 0;; w++) { + if ((inw(ioaddr + 0x30) & 1) == 0) { + if (inb(ioaddr + 0x32) != 0) + break; + + outb(0x80 | 0x7d, ioaddr + 0x30); + if (((inw(ioaddr + 0x30) & 1) == 0) + && (inb(ioaddr + 0x32) != 0)) + break; + outb(0x80 | 0x7f, ioaddr + 0x30); + if (((inw(ioaddr + 0x30) & 1) == 0) + && (inb(ioaddr + 0x32) != 0)) + break; + } + + if (w > 10000) { + outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb(inb(ioaddr + 0x37) & ~0x08, + ioaddr + 0x37); + udelay(1); + outw(0x80, ioaddr + 0x30); + for (w = 0; w < 10000; w++) { + if ((inw(ioaddr + 0x30) & 1) == 0) + break; + } + } + } +#endif + if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* restore.. */ + outw(save_ringbus_a, ioaddr + 0x36); + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} + +static void snd_es1968_reset(es1968_t *chip) +{ + /* Reset */ + outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND, + chip->io_port + ESM_PORT_HOST_IRQ); + udelay(10); + outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ); + udelay(10); +} + +/* + * power management + */ +static void snd_es1968_set_acpi(es1968_t *chip, int state) +{ + u16 active_mask = acpi_state_mask[state]; + + pci_set_power_state(chip->pci, state); + /* make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(chip->pci, 0x54, ~ active_mask); + pci_write_config_word(chip->pci, 0x56, ~ active_mask); +} + + +/* + * initialize maestro chip + */ +static void snd_es1968_chip_init(es1968_t *chip) +{ + struct pci_dev *pci = chip->pci; + int i; + unsigned long iobase = chip->io_port; + u16 w; + u32 n; + + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. + */ + + /* do config work at full power */ + snd_es1968_set_acpi(chip, ACPI_D0); + + /* Config Reg A */ + pci_read_config_word(pci, ESM_CONFIG_A, &w); + + /* Use TDMA for now. TDMA works on all boards, so while its + * not the most efficient its the simplest. */ + w &= ~DMA_CLEAR; /* Clear DMA bits */ + w |= DMA_TDMA; /* TDMA on */ + w &= ~(PIC_SNOOP1 | PIC_SNOOP2); /* Clear Pic Snoop Mode Bits */ + w &= ~SAFEGUARD; /* Safeguard off */ + w |= POST_WRITE; /* Posted write */ + w |= ISA_TIMING; /* ISA timing on */ + /* XXX huh? claims to be reserved.. */ + w &= ~SWAP_LR; /* swap left/right + seems to only have effect on SB + Emulation */ + w &= ~SUBTR_DECODE; /* Subtractive decode off */ + + pci_write_config_word(pci, ESM_CONFIG_A, w); + + /* Config Reg B */ + + pci_read_config_word(pci, ESM_CONFIG_B, &w); + + w &= ~(1 << 15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w &= ~(1 << 14); /* External clock */ + + w &= ~SPDIF_CONFB; /* disable S/PDIF output */ + w |= HWV_CONFB; /* HWV on */ + w |= DEBOUNCE; /* Debounce off: easier to push the HW buttons */ + w &= ~GPIO_CONFB; /* GPIO 4:5 */ + w |= CHI_CONFB; /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w &= ~IDMA_CONFB; /* IDMA off (undocumented) */ + w &= ~MIDI_FIX; /* MIDI fix off (undoc) */ + w &= ~(1 << 1); /* reserved, always write 0 */ + w &= ~IRQ_TO_ISA; /* IRQ to ISA off (undoc) */ + + pci_write_config_word(pci, ESM_CONFIG_B, w); + + /* DDMA off */ + + pci_read_config_word(pci, ESM_DDMA, &w); + w &= ~(1 << 0); + pci_write_config_word(pci, ESM_DDMA, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w); + + w &= ~ESS_ENABLE_AUDIO; /* Disable Legacy Audio */ + w &= ~ESS_ENABLE_SERIAL_IRQ; /* Disable SIRQ */ + w &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w); + + /* Set up 978 docking control chip. */ + pci_read_config_word(pci, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pci, 0x58, w); + + /* Sound Reset */ + + snd_es1968_reset(chip); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */ + udelay(20); + + /* + * Reset the CODEC + */ + + snd_es1968_ac97_reset(chip); + + /* Ring Bus Control B */ + + n = inl(iobase + ESM_RING_BUS_CONTR_B); + n &= ~RINGB_EN_SPDIF; /* SPDIF off */ + //w |= RINGB_EN_2CODEC; /* enable 2nd codec */ + outl(n, iobase + ESM_RING_BUS_CONTR_B); + + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase + ASSP_CONTROL_B); + outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */ + outb(0, iobase + ASSP_CONTROL_C); /* M: Disable ASSP, ASSP IRQ's and FM Port */ + + /* Enable IRQ's */ + w = ESM_HIRQ_DSIE | ESM_HIRQ_MPU401 | ESM_HIRQ_HW_VOLUME; + outw(w, iobase + ESM_PORT_HOST_IRQ); + + + /* + * set up wavecache + */ + for (i = 0; i < 16; i++) { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0 + i, iobase + WC_INDEX); + outw(0x0000, iobase + WC_DATA); + + /* The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too.*/ + outw(0x01D0 + i, iobase + WC_INDEX); + outw(0x0000, iobase + WC_DATA); + } + wave_set_register(chip, IDR7_WAVE_ROMRAM, + (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00)); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400); + + + maestro_write(chip, IDR2_CRAM_DATA, 0x0000); + /* Now back to the DirectSound stuff */ + /* audio serial configuration.. ? */ + maestro_write(chip, 0x08, 0xB004); + maestro_write(chip, 0x09, 0x001B); + maestro_write(chip, 0x0A, 0x8000); + maestro_write(chip, 0x0B, 0x3F37); + maestro_write(chip, 0x0C, 0x0098); + + /* parallel in, has something to do with recording :) */ + maestro_write(chip, 0x0C, + (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000); + /* parallel out */ + maestro_write(chip, 0x0C, + (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500); + + maestro_write(chip, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + w = inw(iobase + WC_CONTROL); + + w &= ~0xFA00; /* Seems to be reserved? I don't know */ + w |= 0xA000; /* reserved... I don't know */ + w &= ~0x0200; /* Channels 56,57,58,59 as Extra Play,Rec Channel enable + Seems to crash the Computer if enabled... */ + w |= 0x0100; /* Wave Cache Operation Enabled */ + w |= 0x0080; /* Channels 60/61 as Placback/Record enabled */ + w &= ~0x0060; /* Clear Wavtable Size */ + w |= 0x0020; /* Wavetable Size : 1MB */ + /* Bit 4 is reserved */ + w &= ~0x000C; /* DMA Stuff? I don't understand what the datasheet means */ + /* Bit 1 is reserved */ + w &= ~0x0001; /* Test Mode off */ + + outw(w, iobase + WC_CONTROL); + + /* Now clear the APU control ram */ + for (i = 0; i < NR_APUS; i++) { + for (w = 0; w < NR_APU_REGS; w++) + apu_set_register(chip, i, w, 0); + + } +} + +#ifdef CONFIG_PM +/* + * PM support + */ +static void es1968_suspend(es1968_t *chip) +{ + snd_card_t *card = chip->card; + + if (! chip->do_pm) + return; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + snd_pcm_suspend_all(chip->pcm); + snd_es1968_bob_stop(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void es1968_resume(es1968_t *chip) +{ + snd_card_t *card = chip->card; + + if (! chip->do_pm) + return; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + /* restore all our config */ + pci_enable_device(chip->pci); + snd_es1968_chip_init(chip); + + /* need to restore the base pointers.. */ + if (chip->dma.addr) { + /* set PCMBAR */ + wave_set_register(chip, 0x01FC, chip->dma.addr >> 12); + } + + /* restore ac97 state */ + snd_ac97_resume(chip->ac97); + + /* start timer again */ + if (atomic_read(&chip->bobclient)) + snd_es1968_bob_start(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_es1968_suspend(struct pci_dev *dev, u32 state) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO); + es1968_suspend(chip); + return 0; +} +static int snd_es1968_resume(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO); + es1968_resume(chip); + return 0; +} +#else +static void snd_es1968_suspend(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return); + es1968_suspend(chip); +} +static void snd_es1968_resume(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return); + es1968_resume(chip); +} +#endif + +/* callback */ +static int snd_es1968_set_power_state(snd_card_t *card, unsigned int power_state) +{ + es1968_t *chip = snd_magic_cast(es1968_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + es1968_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + es1968_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_es1968_free(es1968_t *chip) +{ + if (chip->res_io_port) + snd_es1968_reset(chip); + + snd_es1968_set_acpi(chip, ACPI_D3); + chip->master_switch = NULL; + chip->master_volume = NULL; + if (chip->res_io_port) { + release_resource(chip->res_io_port); + kfree_nocheck(chip->res_io_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1968_dev_free(snd_device_t *device) +{ + es1968_t *chip = snd_magic_cast(es1968_t, device->device_data, return -ENXIO); + return snd_es1968_free(chip); +} + +static int __devinit snd_es1968_create(snd_card_t * card, + struct pci_dev *pci, + int total_bufsize, + int play_streams, + int capt_streams, + int chip_type, + int do_pm, + es1968_t **chip_ret) +{ + static snd_device_ops_t ops = { + .dev_free = snd_es1968_dev_free, + }; + es1968_t *chip; + int i, err; + + *chip_ret = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = (es1968_t *) snd_magic_kcalloc(es1968_t, 0, GFP_KERNEL); + if (! chip) + return -ENOMEM; + + /* Set Vars */ + chip->type = chip_type; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->substream_lock); + spin_lock_init(&chip->bob_lock); + INIT_LIST_HEAD(&chip->buf_list); + INIT_LIST_HEAD(&chip->substream_list); + init_MUTEX(&chip->memory_mutex); + tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->total_bufsize = total_bufsize; /* in bytes */ + chip->playback_streams = play_streams; + chip->capture_streams = capt_streams; + + chip->io_port = pci_resource_start(pci, 0); + if ((chip->res_io_port = request_region(chip->io_port, 0x100, "ESS Maestro")) == NULL) { + snd_es1968_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 0x100 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ, + "ESS Maestro", (void*)chip)) { + snd_es1968_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + /* Clear Maestro_map */ + for (i = 0; i < 32; i++) + chip->maestro_map[i] = 0; + + /* Clear Apu Map */ + for (i = 0; i < NR_APUS; i++) + chip->apu[i] = ESM_APU_FREE; + + atomic_set(&chip->bobclient, 0); + + /* just to be sure */ + pci_set_master(pci); + + if (do_pm > 1) { + /* disable power-management if not maestro2e or + * if not on the whitelist + */ + unsigned int vend; + pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if (chip->type != TYPE_MAESTRO2E || (vend & 0xffff) != 0x1028) { + printk(KERN_INFO "es1968: not attempting power management.\n"); + do_pm = 0; + } + } + chip->do_pm = do_pm; + + snd_es1968_chip_init(chip); + +#ifdef CONFIG_PM + if (chip->do_pm) { + card->set_power_state = snd_es1968_set_power_state; + card->power_state_private_data = chip; + } +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1968_free(chip); + return err; + } + + *chip_ret = chip; + + return 0; +} + + +/* + * joystick + */ + +static int snd_es1968_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1968_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + es1968_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val); + ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0; + return 0; +} + +static int snd_es1968_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + es1968_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &oval); + val = oval & ~0x04; + if (ucontrol->value.integer.value[0]) + val |= 0x04; + if (val != oval) { + pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val); + return 1; + } + return 0; +} + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_es1968_control_switches[] __devinitdata = { + { + .name = "Joystick", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_es1968_joystick_info, + .get = snd_es1968_joystick_get, + .put = snd_es1968_joystick_put, + } +}; + +/* + */ +static int __devinit snd_es1968_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + es1968_t *chip; + unsigned int i; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (!card) + return -ENOMEM; + + if (total_bufsize[dev] < 128) + total_bufsize[dev] = 128; + if (total_bufsize[dev] > 4096) + total_bufsize[dev] = 4096; + if ((err = snd_es1968_create(card, pci, + total_bufsize[dev] * 1024, /* in bytes */ + pcm_substreams_p[dev], + pcm_substreams_c[dev], + pci_id->driver_data, + use_pm[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + switch (chip->type) { + case TYPE_MAESTRO2E: + strcpy(card->driver, "ES1978"); + strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); + break; + case TYPE_MAESTRO2: + strcpy(card->driver, "ES1968"); + strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); + break; + case TYPE_MAESTRO: + strcpy(card->driver, "ESM1"); + strcpy(card->shortname, "ESS Maestro 1"); + break; + } + + if ((err = snd_es1968_pcm(chip, 0)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_es1968_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->io_port + ESM_MPU401_PORT, 1, + chip->irq, 0, &chip->rmidi)) < 0) { + printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n"); + } + + /* card switches */ + for (i = 0; i < num_controls(snd_es1968_control_switches); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_es1968_control_switches[i], chip)); + if (err < 0) { + snd_card_free(card); + return err; + } + } + + chip->clock = clock[dev]; + if (! chip->clock) + es1968_measure_clock(chip); + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->io_port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_es1968_remove(struct pci_dev *pci) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ES1968 (ESS Maestro)", + .id_table = snd_es1968_ids, + .probe = snd_es1968_probe, + .remove = __devexit_p(snd_es1968_remove), +#ifdef CONFIG_PM + .suspend = snd_es1968_suspend, + .resume = snd_es1968_resume, +#endif +}; + +#if 0 // do we really need this? +static int snd_es1968_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + pci_unregister_driver(&driver); + return NOTIFY_OK; +} + +static struct notifier_block snd_es1968_nb = {snd_es1968_notifier, NULL, 0}; +#endif + +static int __init alsa_card_es1968_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ESS Maestro soundcard not found or device busy\n"); +#endif + return err; + } +#if 0 // do we really need this? + /* If this driver is not shutdown cleanly at reboot, it can + leave the speaking emitting an annoying noise, so we catch + shutdown events. */ + if (register_reboot_notifier(&snd_es1968_nb)) { + printk(KERN_ERR "reboot notifier registration failed; may make noise at shutdown.\n"); + } +#endif + return 0; +} + +static void __exit alsa_card_es1968_exit(void) +{ +#if 0 // do we really need this? + unregister_reboot_notifier(&snd_es1968_nb); +#endif + pci_unregister_driver(&driver); +} + +module_init(alsa_card_es1968_init) +module_exit(alsa_card_es1968_exit) + +#ifndef MODULE + +/* format is: snd-es1968=enable,index,id, + total_bufsize, + pcm_substreams_p, + pcm_substreams_c, + clock +*/ + +static int __init alsa_card_es1968_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&total_bufsize[nr_dev]) == 2 && + get_option(&str,&pcm_substreams_p[nr_dev]) == 2 && + get_option(&str,&pcm_substreams_c[nr_dev]) == 2 && + get_option(&str,&clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1968=", alsa_card_es1968_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/fm801.c linux/sound/pci/fm801.c --- linux-2.4.21-rc1.orig/sound/pci/fm801.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/fm801.c 2003-02-07 01:34:16.000000000 -0700 @@ -0,0 +1,1161 @@ +/* + * The driver for the ForteMedia FM801 based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#define chip_t fm801_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ForteMedia FM801"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ForteMedia,FM801}," + "{Genius,SoundMaker Live 5.1}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for the FM801 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +/* + * Direct registers + */ + +#define FM801_REG(chip, reg) (chip->port + FM801_##reg) + +#define FM801_PCM_VOL 0x00 /* PCM Output Volume */ +#define FM801_FM_VOL 0x02 /* FM Output Volume */ +#define FM801_I2S_VOL 0x04 /* I2S Volume */ +#define FM801_REC_SRC 0x06 /* Record Source */ +#define FM801_PLY_CTRL 0x08 /* Playback Control */ +#define FM801_PLY_COUNT 0x0a /* Playback Count */ +#define FM801_PLY_BUF1 0x0c /* Playback Bufer I */ +#define FM801_PLY_BUF2 0x10 /* Playback Buffer II */ +#define FM801_CAP_CTRL 0x14 /* Capture Control */ +#define FM801_CAP_COUNT 0x16 /* Capture Count */ +#define FM801_CAP_BUF1 0x18 /* Capture Buffer I */ +#define FM801_CAP_BUF2 0x1c /* Capture Buffer II */ +#define FM801_CODEC_CTRL 0x22 /* Codec Control */ +#define FM801_I2S_MODE 0x24 /* I2S Mode Control */ +#define FM801_VOLUME 0x26 /* Volume Up/Down/Mute Status */ +#define FM801_I2C_CTRL 0x29 /* I2C Control */ +#define FM801_AC97_CMD 0x2a /* AC'97 Command */ +#define FM801_AC97_DATA 0x2c /* AC'97 Data */ +#define FM801_MPU401_DATA 0x30 /* MPU401 Data */ +#define FM801_MPU401_CMD 0x31 /* MPU401 Command */ +#define FM801_GPIO_CTRL 0x52 /* General Purpose I/O Control */ +#define FM801_GEN_CTRL 0x54 /* General Control */ +#define FM801_IRQ_MASK 0x56 /* Interrupt Mask */ +#define FM801_IRQ_STATUS 0x5a /* Interrupt Status */ +#define FM801_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ +#define FM801_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ +#define FM801_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ +#define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ +#define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ + +#define FM801_AC97_ADDR_SHIFT 10 + +/* playback and record control register bits */ +#define FM801_BUF1_LAST (1<<1) +#define FM801_BUF2_LAST (1<<2) +#define FM801_START (1<<5) +#define FM801_PAUSE (1<<6) +#define FM801_IMMED_STOP (1<<7) +#define FM801_RATE_SHIFT 8 +#define FM801_RATE_MASK (15 << FM801_RATE_SHIFT) +#define FM801_CHANNELS_4 (1<<12) /* playback only */ +#define FM801_CHANNELS_6 (2<<12) /* playback only */ +#define FM801_CHANNELS_6MS (3<<12) /* playback only */ +#define FM801_CHANNELS_MASK (3<<12) +#define FM801_16BIT (1<<14) +#define FM801_STEREO (1<<15) + +/* IRQ status bits */ +#define FM801_IRQ_PLAYBACK (1<<8) +#define FM801_IRQ_CAPTURE (1<<9) +#define FM801_IRQ_VOLUME (1<<14) +#define FM801_IRQ_MPU (1<<15) + +/* + + */ + +typedef struct _snd_fm801 fm801_t; + +struct _snd_fm801 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + unsigned int multichannel: 1, /* multichannel support */ + secondary: 1; /* secondary codec */ + unsigned char secondary_addr; /* address of the secondary codec */ + + unsigned short ply_ctrl; /* playback control */ + unsigned short cap_ctrl; /* capture control */ + + unsigned long ply_buffer; + unsigned int ply_buf; + unsigned int ply_count; + unsigned int ply_size; + unsigned int ply_pos; + + unsigned long cap_buffer; + unsigned int cap_buf; + unsigned int cap_count; + unsigned int cap_size; + unsigned int cap_pos; + + ac97_t *ac97; + ac97_t *ac97_sec; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + +static struct pci_device_id snd_fm801_ids[] __devinitdata = { + { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_fm801_ids); + +/* + * common I/O routines + */ + +static int snd_fm801_update_bits(fm801_t *chip, unsigned short reg, + unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + spin_lock(&chip->reg_lock); + old = inw(chip->port + reg); + new = (old & ~mask) | value; + change = old != new; + if (change) + outw(new, chip->port + reg); + spin_unlock(&chip->reg_lock); + return change; +} + +static void snd_fm801_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + int idx; + + /* + * Wait until the codec interface is not ready.. + */ + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok1; + udelay(10); + } + snd_printk("AC'97 interface is busy (1)\n"); + return; + + ok1: + /* write data and address */ + outw(val, FM801_REG(chip, AC97_DATA)); + outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + /* + * Wait until the write command is not completed.. + */ + for (idx = 0; idx < 1000; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + return; + udelay(10); + } + snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num); +} + +static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return -ENXIO); + int idx; + + /* + * Wait until the codec interface is not ready.. + */ + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok1; + udelay(10); + } + snd_printk("AC'97 interface is busy (1)\n"); + return 0; + + ok1: + /* read command */ + outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD)); + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok2; + udelay(10); + } + snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num); + return 0; + + ok2: + for (idx = 0; idx < 1000; idx++) { + if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8)) + goto ok3; + udelay(10); + } + snd_printk("AC'97 interface #%d is not valid (2)\n", ac97->num); + return 0; + + ok3: + return inw(FM801_REG(chip, AC97_DATA)); +} + +static unsigned int rates[] = { + 5500, 8000, 9600, 11025, + 16000, 19200, 22050, 32000, + 38400, 44100, 48000 +}; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = RATES, + .list = rates, + .mask = 0, +}; + +static unsigned int channels[] = { + 2, 4, 6 +}; + +#define CHANNELS sizeof(channels) / sizeof(channels[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + .count = CHANNELS, + .list = channels, + .mask = 0, +}; + +/* + * Sample rate routines + */ + +static unsigned short snd_fm801_rate_bits(unsigned int rate) +{ + unsigned int idx; + + for (idx = 0; idx < 11; idx++) + if (rates[idx] == rate) + return idx; + snd_BUG(); + return RATES - 1; +} + +/* + * PCM part + */ + +static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->ply_ctrl &= ~(FM801_BUF1_LAST | + FM801_BUF2_LAST | + FM801_PAUSE); + chip->ply_ctrl |= FM801_START | + FM801_IMMED_STOP; + break; + case SNDRV_PCM_TRIGGER_STOP: + chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + chip->ply_ctrl |= FM801_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + chip->ply_ctrl &= ~FM801_PAUSE; + break; + default: + spin_unlock(&chip->reg_lock); + snd_BUG(); + return -EINVAL; + } + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + spin_unlock(&chip->reg_lock); + return 0; +} + +static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->cap_ctrl &= ~(FM801_BUF1_LAST | + FM801_BUF2_LAST | + FM801_PAUSE); + chip->cap_ctrl |= FM801_START | + FM801_IMMED_STOP; + break; + case SNDRV_PCM_TRIGGER_STOP: + chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + chip->cap_ctrl |= FM801_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + chip->cap_ctrl &= ~FM801_PAUSE; + break; + default: + spin_unlock(&chip->reg_lock); + snd_BUG(); + return -EINVAL; + } + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + spin_unlock(&chip->reg_lock); + return 0; +} + +static int snd_fm801_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_fm801_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_fm801_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->ply_size = snd_pcm_lib_buffer_bytes(substream); + chip->ply_count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->ply_ctrl &= ~(FM801_START | FM801_16BIT | + FM801_STEREO | FM801_RATE_MASK | + FM801_CHANNELS_MASK); + if (snd_pcm_format_width(runtime->format) == 16) + chip->ply_ctrl |= FM801_16BIT; + if (runtime->channels > 1) { + chip->ply_ctrl |= FM801_STEREO; + if (runtime->channels == 4) + chip->ply_ctrl |= FM801_CHANNELS_4; + else if (runtime->channels == 6) + chip->ply_ctrl |= FM801_CHANNELS_6; + } + chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; + chip->ply_buf = 0; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT)); + chip->ply_buffer = runtime->dma_addr; + chip->ply_pos = 0; + outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1)); + outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_fm801_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->cap_size = snd_pcm_lib_buffer_bytes(substream); + chip->cap_count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->cap_ctrl &= ~(FM801_START | FM801_16BIT | + FM801_STEREO | FM801_RATE_MASK); + if (snd_pcm_format_width(runtime->format) == 16) + chip->cap_ctrl |= FM801_16BIT; + if (runtime->channels > 1) + chip->cap_ctrl |= FM801_STEREO; + chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; + chip->cap_buf = 0; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT)); + chip->cap_buffer = runtime->dma_addr; + chip->cap_pos = 0; + outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1)); + outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + size_t ptr; + + if (!(chip->ply_ctrl & FM801_START)) + return 0; + spin_lock_irqsave(&chip->reg_lock, flags); + ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT)); + if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) { + ptr += chip->ply_count; + ptr %= chip->ply_size; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + size_t ptr; + + if (!(chip->cap_ctrl & FM801_START)) + return 0; + spin_lock_irqsave(&chip->reg_lock, flags); + ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT)); + if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) { + ptr += chip->cap_count; + ptr %= chip->cap_size; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames(substream->runtime, ptr); +} + +static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + fm801_t *chip = snd_magic_cast(fm801_t, dev_id, return); + unsigned short status; + unsigned int tmp; + + status = inw(FM801_REG(chip, IRQ_STATUS)); + status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME; + if (! status) + return; + /* ack first */ + outw(status, FM801_REG(chip, IRQ_STATUS)); + if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) { + spin_lock(&chip->reg_lock); + chip->ply_buf++; + chip->ply_pos += chip->ply_count; + chip->ply_pos %= chip->ply_size; + tmp = chip->ply_pos + chip->ply_count; + tmp %= chip->ply_size; + outl(chip->ply_buffer + tmp, + (chip->ply_buf & 1) ? + FM801_REG(chip, PLY_BUF1) : + FM801_REG(chip, PLY_BUF2)); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->playback_substream); + } + if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) { + spin_lock(&chip->reg_lock); + chip->cap_buf++; + chip->cap_pos += chip->cap_count; + chip->cap_pos %= chip->cap_size; + tmp = chip->cap_pos + chip->cap_count; + tmp %= chip->cap_size; + outl(chip->cap_buffer + tmp, + (chip->cap_buf & 1) ? + FM801_REG(chip, CAP_BUF1) : + FM801_REG(chip, CAP_BUF2)); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->capture_substream); + } + if (chip->rmidi && (status & FM801_IRQ_MPU)) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + if (status & FM801_IRQ_VOLUME) + ;/* TODO */ +} + +static snd_pcm_hardware_t snd_fm801_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5500, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_fm801_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5500, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_fm801_playback_open(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback_substream = substream; + runtime->hw = snd_fm801_playback; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (chip->multichannel) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_fm801_capture_open(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture_substream = substream; + runtime->hw = snd_fm801_capture; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_fm801_playback_close(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + return 0; +} + +static int snd_fm801_capture_close(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_fm801_playback_ops = { + .open = snd_fm801_playback_open, + .close = snd_fm801_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_fm801_hw_params, + .hw_free = snd_fm801_hw_free, + .prepare = snd_fm801_playback_prepare, + .trigger = snd_fm801_playback_trigger, + .pointer = snd_fm801_playback_pointer, +}; + +static snd_pcm_ops_t snd_fm801_capture_ops = { + .open = snd_fm801_capture_open, + .close = snd_fm801_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_fm801_hw_params, + .hw_free = snd_fm801_hw_free, + .prepare = snd_fm801_capture_prepare, + .trigger = snd_fm801_capture_trigger, + .pointer = snd_fm801_capture_pointer, +}; + +static void snd_fm801_pcm_free(snd_pcm_t *pcm) +{ + fm801_t *chip = snd_magic_cast(fm801_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_fm801_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "FM801"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer routines + */ + +#define FM801_SINGLE(xname, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \ + .get = snd_fm801_get_single, .put = snd_fm801_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + return snd_fm801_update_bits(chip, reg, mask << shift, val << shift); +} + +#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ + .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ + .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock(&chip->reg_lock); + ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask; + spin_unlock(&chip->reg_lock); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + return snd_fm801_update_bits(chip, reg, + (mask << shift_left) | (mask << shift_right), + (val1 << shift_left ) | (val2 << shift_right)); +} + +static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[5] = { + "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + if (uinfo->value.enumerated.item > 4) + uinfo->value.enumerated.item = 4; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = inw(FM801_REG(chip, REC_SRC)) & 7; + if (val > 4) + val = 4; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if ((val = ucontrol->value.enumerated.item[0]) > 4) + return -EINVAL; + return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); +} + +#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = { +FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1), +FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), +FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1), +FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), +FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1), +FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Capture Source", + .info = snd_fm801_info_mux, + .get = snd_fm801_get_mux, + .put = snd_fm801_put_mux, +} +}; + +#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = { +FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0), +FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), +FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0), +FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0), +FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0), +FM801_SINGLE("IEC958 Playback Switch", FM801_GEN_CTRL, 2, 1, 0), +}; + +static void snd_fm801_mixer_free_ac97(ac97_t *ac97) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + if (ac97->num == 0) { + chip->ac97 = NULL; + } else { + chip->ac97_sec = NULL; + } +} + +static int __devinit snd_fm801_mixer(fm801_t *chip) +{ + ac97_t ac97; + unsigned int i; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_fm801_codec_write; + ac97.read = snd_fm801_codec_read; + ac97.private_data = chip; + ac97.private_free = snd_fm801_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + if (chip->secondary) { + ac97.num = 1; + ac97.addr = chip->secondary_addr; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97_sec)) < 0) + return err; + } + for (i = 0; i < FM801_CONTROLS; i++) + snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip)); + if (chip->multichannel) { + for (i = 0; i < FM801_CONTROLS_MULTI; i++) + snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip)); + } + return 0; +} + +/* + * initialization routines + */ + +static int snd_fm801_free(fm801_t *chip) +{ + unsigned short cmdw; + + if (chip->irq < 0) + goto __end_hw; + + /* interrupt setup - mask everything */ + cmdw = inw(FM801_REG(chip, IRQ_MASK)); + cmdw |= 0x00c3; + outw(cmdw, FM801_REG(chip, IRQ_MASK)); + + __end_hw: + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_fm801_dev_free(snd_device_t *device) +{ + fm801_t *chip = snd_magic_cast(fm801_t, device->device_data, return -ENXIO); + return snd_fm801_free(chip); +} + +static int __devinit snd_fm801_create(snd_card_t * card, + struct pci_dev * pci, + fm801_t ** rchip) +{ + fm801_t *chip; + unsigned char rev, id; + unsigned short cmdw; + signed long timeout; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_fm801_dev_free, + }; + + *rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = snd_magic_kcalloc(fm801_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 0x80, "FM801")) == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->port, chip->port + 0x80 - 1); + snd_fm801_free(chip); + return -EBUSY; + } + if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) { + snd_printk("unable to grab IRQ %d\n", chip->irq); + snd_fm801_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + + pci_read_config_byte(pci, PCI_REVISION_ID, &rev); + if (rev >= 0xb1) /* FM801-AU */ + chip->multichannel = 1; + + /* codec cold reset + AC'97 warm reset */ + outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL)); + udelay(100); + outw(0, FM801_REG(chip, CODEC_CTRL)); + + timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ + + outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) + goto __ac97_secondary; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + snd_printk("Primary AC'97 codec not found\n"); + snd_fm801_free(chip); + return -EIO; + + __ac97_secondary: + if (!chip->multichannel) /* lookup is not required */ + goto __ac97_ok; + for (id = 3; id > 0; id--) { /* my card has the secondary codec */ + /* at address #3, so the loop is inverted */ + + if ((timeout - (signed long)jiffies) < HZ / 20) + timeout = jiffies + HZ / 20; + + outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) { + cmdw = inw(FM801_REG(chip, AC97_DATA)); + if (cmdw != 0xffff && cmdw != 0) { + chip->secondary = 1; + chip->secondary_addr = id; + goto __ac97_ok; + } + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + } + + /* the recovery phase, it seems that probing for non-existing codec might */ + /* cause timeout problems */ + timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ + + outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) + goto __ac97_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + snd_printk("Primary AC'97 codec not responding\n"); + snd_fm801_free(chip); + return -EIO; + + __ac97_ok: + + /* init volume */ + outw(0x0808, FM801_REG(chip, PCM_VOL)); + outw(0x9f1f, FM801_REG(chip, FM_VOL)); + outw(0x8808, FM801_REG(chip, I2S_VOL)); + + /* I2S control - I2S mode */ + outw(0x0003, FM801_REG(chip, I2S_MODE)); + + /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ + cmdw = inw(FM801_REG(chip, IRQ_MASK)); + cmdw &= ~0x0083; + outw(cmdw, FM801_REG(chip, IRQ_MASK)); + + /* interrupt clear */ + outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_fm801_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int __devinit snd_card_fm801_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + fm801_t *chip; + opl3_t *opl3; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if ((err = snd_fm801_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_fm801_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, + FM801_REG(chip, MPU401_DATA), 1, + chip->irq, 0, &chip->rmidi)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0), + FM801_REG(chip, OPL3_BANK1), + OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "FM801"); + strcpy(card->shortname, "ForteMedia FM801-"); + strcat(card->shortname, chip->multichannel ? "AU" : "AS"); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_fm801_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "FM801", + .id_table = snd_fm801_ids, + .probe = snd_card_fm801_probe, + .remove = __devexit_p(snd_card_fm801_remove), +}; + +static int __init alsa_card_fm801_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ForteMedia FM801 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_fm801_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_fm801_init) +module_exit(alsa_card_fm801_exit) + +#ifndef MODULE + +/* format is: snd-fm801=enable,index,id */ + +static int __init alsa_card_fm801_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-fm801=", alsa_card_fm801_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/Makefile linux/sound/pci/ice1712/Makefile --- linux-2.4.21-rc1.orig/sound/pci/ice1712/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/Makefile 2003-04-29 05:13:43.000000000 -0600 @@ -0,0 +1,22 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# +O_TARGET := _ice1712.o + +list-multi := snd-ice1712.o snd-ice1724.o + +snd-ice1712-objs := ice1712.o ak4524.o delta.o hoontech.o ews.o +snd-ice1724-objs := ice1724.o ak4524.o amp.o revo.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o +obj-$(CONFIG_SND_ICE1724) += snd-ice1724.o + +include $(TOPDIR)/Rules.make + +snd-ice1712.o: $(snd-ice1712-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ice1712-objs) + +snd-ice1724.o: $(snd-ice1724-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ice1724-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/ak4524.c linux/sound/pci/ice1712/ak4524.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/ak4524.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/ak4524.c 2003-02-25 08:05:02.000000000 -0700 @@ -0,0 +1,527 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "ice1712.h" + + +/* + * write AK4xxx register + */ +void snd_ice1712_akm4xxx_write(akm4xxx_t *ak, int chip, + unsigned char addr, unsigned char data) +{ + unsigned int tmp; + int idx; + unsigned int addrdata; + ice1712_t *ice = ak->chip; + + snd_assert(chip >= 0 && chip < 4, return); + + if (ak->ops.start) { + if (ak->ops.start(ak, chip) < 0) + return; + } else + snd_ice1712_save_gpio_status(ice); + + tmp = snd_ice1712_gpio_read(ice); + tmp |= ak->add_flags; + tmp &= ~ak->mask_flags; + if (ak->cs_mask == ak->cs_addr) { + if (ak->cif) { + tmp |= ak->cs_mask; /* start without chip select */ + } else { + tmp &= ~ak->cs_mask; /* chip select low */ + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + } + } else { + /* doesn't handle cf=1 yet */ + tmp &= ~ak->cs_mask; + tmp |= ak->cs_addr; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + } + + /* build I2C address + data byte */ + addrdata = (ak->caddr << 6) | 0x20 | (addr & 0x1f); + addrdata = (addrdata << 8) | data; + for (idx = 15; idx >= 0; idx--) { + /* drop clock */ + tmp &= ~ak->clk_mask; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + /* set data */ + if (addrdata & (1 << idx)) + tmp |= ak->data_mask; + else + tmp &= ~ak->data_mask; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + /* raise clock */ + tmp |= ak->clk_mask; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + } + + /* save the data */ + if (ak->type == SND_AK4524 || ak->type == SND_AK4528) { + if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0) + ak->images[chip][addr] = data; + else + ak->ipga_gain[chip][addr-4] = data; + } else { + /* AK4529, or else */ + ak->images[chip][addr] = data; + } + + if (ak->cs_mask == ak->cs_addr) { + if (ak->cif) { + /* assert a cs pulse to trigger */ + tmp &= ~ak->cs_mask; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + } + tmp |= ak->cs_mask; /* chip select high to trigger */ + } else { + tmp &= ~ak->cs_mask; + tmp |= ak->cs_none; /* deselect address */ + } + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + + if (ak->ops.stop) + ak->ops.stop(ak); + else + snd_ice1712_restore_gpio_status(ice); +} + +/* + * reset the AKM codecs + * @state: 1 = reset codec, 0 = restore the registers + * + * assert the reset operation and restores the register values to the chips. + */ +void snd_ice1712_akm4xxx_reset(akm4xxx_t *ak, int state) +{ + unsigned int chip; + unsigned char reg; + + switch (ak->type) { + case SND_AK4524: + case SND_AK4528: + for (chip = 0; chip < ak->num_dacs/2; chip++) { + snd_ice1712_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03); + if (state) + continue; + /* DAC volumes */ + for (reg = 0x04; reg < (ak->type == SND_AK4528 ? 0x06 : 0x08); reg++) + snd_ice1712_akm4xxx_write(ak, chip, reg, ak->images[chip][reg]); + if (ak->type == SND_AK4528) + continue; + /* IPGA */ + for (reg = 0x04; reg < 0x06; reg++) + snd_ice1712_akm4xxx_write(ak, chip, reg, ak->ipga_gain[chip][reg-4]); + } + break; + case SND_AK4529: + /* FIXME: needed for ak4529? */ + break; + case SND_AK4355: + snd_ice1712_akm4xxx_write(ak, 0, 0x01, state ? 0x02 : 0x01); + if (state) + return; + for (reg = 0x00; reg < 0x0a; reg++) + if (reg != 0x01) + snd_ice1712_akm4xxx_write(ak, 0, reg, ak->images[0][reg]); + break; + case SND_AK4381: + for (chip = 0; chip < ak->num_dacs/2; chip++) { + snd_ice1712_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f); + if (state) + continue; + for (reg = 0x01; reg < 0x05; reg++) + snd_ice1712_akm4xxx_write(ak, chip, reg, ak->images[chip][reg]); + } + break; + } +} + +/* + * initialize all the ak4xxx chips + */ +static void __devinit snd_ice1712_akm4xxx_init_chip(akm4xxx_t *ak) +{ + static unsigned char inits_ak4524[] = { + 0x00, 0x07, /* 0: all power up */ + 0x01, 0x00, /* 1: ADC/DAC reset */ + 0x02, 0x60, /* 2: 24bit I2S */ + 0x03, 0x19, /* 3: deemphasis off */ + 0x01, 0x03, /* 1: ADC/DAC enable */ + 0x04, 0x00, /* 4: ADC left muted */ + 0x05, 0x00, /* 5: ADC right muted */ + 0x04, 0x80, /* 4: ADC IPGA gain 0dB */ + 0x05, 0x80, /* 5: ADC IPGA gain 0dB */ + 0x06, 0x00, /* 6: DAC left muted */ + 0x07, 0x00, /* 7: DAC right muted */ + 0xff, 0xff + }; + static unsigned char inits_ak4528[] = { + 0x00, 0x07, /* 0: all power up */ + 0x01, 0x00, /* 1: ADC/DAC reset */ + 0x02, 0x60, /* 2: 24bit I2S */ + 0x03, 0x0d, /* 3: deemphasis off, turn LR highpass filters on */ + 0x01, 0x03, /* 1: ADC/DAC enable */ + 0x04, 0x00, /* 4: ADC left muted */ + 0x05, 0x00, /* 5: ADC right muted */ + 0xff, 0xff + }; + static unsigned char inits_ak4529[] = { + 0x09, 0x01, /* 9: ATS=0, RSTN=1 */ + 0x0a, 0x3f, /* A: all power up, no zero/overflow detection */ + 0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */ + 0x01, 0x00, /* 1: ACKS=0, ADC, loop off */ + 0x02, 0xff, /* 2: LOUT1 muted */ + 0x03, 0xff, /* 3: ROUT1 muted */ + 0x04, 0xff, /* 4: LOUT2 muted */ + 0x05, 0xff, /* 5: ROUT2 muted */ + 0x06, 0xff, /* 6: LOUT3 muted */ + 0x07, 0xff, /* 7: ROUT3 muted */ + 0x0b, 0xff, /* B: LOUT4 muted */ + 0x0c, 0xff, /* C: ROUT4 muted */ + 0x08, 0x55, /* 8: deemphasis all off */ + 0xff, 0xff + }; + static unsigned char inits_ak4355[] = { + 0x01, 0x02, /* 1: reset and soft-mute */ + 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */ + // 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */ + 0x02, 0x2e, + 0x03, 0x01, /* 3: de-emphasis off */ + 0x04, 0x00, /* 4: LOUT1 volume muted */ + 0x05, 0x00, /* 5: ROUT1 volume muted */ + 0x06, 0x00, /* 6: LOUT2 volume muted */ + 0x07, 0x00, /* 7: ROUT2 volume muted */ + 0x08, 0x00, /* 8: LOUT3 volume muted */ + 0x09, 0x00, /* 9: ROUT3 volume muted */ + 0x0a, 0x00, /* a: DATT speed=0, ignore DZF */ + 0x01, 0x01, /* 1: un-reset, unmute */ + 0xff, 0xff + }; + static unsigned char inits_ak4381[] = { + 0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */ + // 0x01, 0x02, /* 1: de-emphasis off, normal speed, sharp roll-off, DZF off */ + 0x01, 0x12, + 0x02, 0x00, /* 2: DZF disabled */ + 0x03, 0x00, /* 3: LATT 0 */ + 0x04, 0x00, /* 4: RATT 0 */ + 0x00, 0x0f, /* 0: power-up, un-reset */ + 0xff, 0xff + }; + + int chip, num_chips; + unsigned char *ptr, reg, data, *inits; + + switch (ak->type) { + case SND_AK4524: + inits = inits_ak4524; + num_chips = ak->num_dacs / 2; + break; + case SND_AK4528: + inits = inits_ak4528; + num_chips = ak->num_dacs / 2; + break; + case SND_AK4529: + inits = inits_ak4529; + num_chips = 1; + break; + case SND_AK4355: + inits = inits_ak4355; + num_chips = 1; + break; + case SND_AK4381: + inits = inits_ak4381; + num_chips = ak->num_dacs / 2; + break; + default: + snd_BUG(); + return; + } + + for (chip = 0; chip < num_chips; chip++) { + ptr = inits; + while (*ptr != 0xff) { + reg = *ptr++; + data = *ptr++; + snd_ice1712_akm4xxx_write(ak, chip, reg, data); + } + } +} + + +/* + * initialize the akm4xxx_t record with the template + */ +void snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *temp, ice1712_t *ice) +{ + *ak = *temp; + ak->chip = ice; + snd_ice1712_akm4xxx_init_chip(ak); +} + + +#define AK_GET_CHIP(val) (((val) >> 8) & 0xff) +#define AK_GET_ADDR(val) ((val) & 0xff) +#define AK_GET_SHIFT(val) (((val) >> 16) & 0x7f) +#define AK_GET_INVERT(val) (((val) >> 23) & 1) +#define AK_GET_MASK(val) (((val) >> 24) & 0xff) +#define AK_COMPOSE(chip,addr,shift,mask) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24)) +#define AK_INVERT (1<<23) + +static int snd_ice1712_akm4xxx_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int mask = AK_GET_MASK(kcontrol->private_value); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ice1712_akm4xxx_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int invert = AK_GET_INVERT(kcontrol->private_value); + unsigned int mask = AK_GET_MASK(kcontrol->private_value); + unsigned char val = ak->images[chip][addr]; + + ucontrol->value.integer.value[0] = invert ? mask - val : val; + return 0; +} + +static int snd_ice1712_akm4xxx_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int invert = AK_GET_INVERT(kcontrol->private_value); + unsigned int mask = AK_GET_MASK(kcontrol->private_value); + unsigned char nval = ucontrol->value.integer.value[0] % (mask+1); + int change; + + if (invert) + nval = mask - nval; + change = ak->images[chip][addr] != nval; + if (change) + snd_ice1712_akm4xxx_write(ak, chip, addr, nval); + return change; +} + +static int snd_ice1712_akm4xxx_ipga_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 36; + return 0; +} + +static int snd_ice1712_akm4xxx_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + ucontrol->value.integer.value[0] = ak->ipga_gain[chip][addr-4] & 0x7f; + return 0; +} + +static int snd_ice1712_akm4xxx_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80; + int change = ak->ipga_gain[chip][addr] != nval; + if (change) + snd_ice1712_akm4xxx_write(ak, chip, addr, nval); + return change; +} + +static int snd_ice1712_akm4xxx_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[4] = { + "44.1kHz", "Off", "48kHz", "32kHz", + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_akm4xxx_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int shift = AK_GET_SHIFT(kcontrol->private_value); + ucontrol->value.enumerated.item[0] = (ak->images[chip][addr] >> shift) & 3; + return 0; +} + +static int snd_ice1712_akm4xxx_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int shift = AK_GET_SHIFT(kcontrol->private_value); + unsigned char nval = ucontrol->value.enumerated.item[0] & 3; + int change; + + nval = (nval << shift) | (ak->images[chip][addr] & ~(3 << shift)); + change = ak->images[chip][addr] != nval; + if (change) + snd_ice1712_akm4xxx_write(ak, chip, addr, nval); + return change; +} + +/* + * build AK4524 controls + */ + +int __devinit snd_ice1712_akm4xxx_build_controls(ice1712_t *ice) +{ + unsigned int idx; + int err; + unsigned int akidx; + + for (akidx = 0; akidx < ice->akm_codecs; akidx++) { + akm4xxx_t *ak = &ice->akm[akidx]; + for (idx = 0; idx < ak->num_dacs; ++idx) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "DAC Volume"); + ctl.id.index = idx + ak->idx_offset * 2; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_akm4xxx_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_akm4xxx_volume_get; + ctl.put = snd_ice1712_akm4xxx_volume_put; + switch (ak->type) { + case SND_AK4524: + ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); /* register 6 & 7 */ + break; + case SND_AK4528: + ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */ + break; + case SND_AK4529: { + int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; /* registers 2-7 and b,c */ + ctl.private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT; + break; + } + case SND_AK4355: + ctl.private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */ + break; + case SND_AK4381: + ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); /* register 3 & 4 */ + break; + default: + return -EINVAL; + } + ctl.private_data = ak; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "ADC Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_akm4xxx_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_akm4xxx_volume_get; + ctl.put = snd_ice1712_akm4xxx_volume_put; + ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */ + ctl.private_data = ak; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "IPGA Analog Capture Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_akm4xxx_ipga_gain_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_akm4xxx_ipga_gain_get; + ctl.put = snd_ice1712_akm4xxx_ipga_gain_put; + ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); /* register 4 & 5 */ + ctl.private_data = ak; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + for (idx = 0; idx < ak->num_dacs/2; idx++) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Deemphasis"); + ctl.id.index = idx + ak->idx_offset; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_akm4xxx_deemphasis_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_akm4xxx_deemphasis_get; + ctl.put = snd_ice1712_akm4xxx_deemphasis_put; + switch (ak->type) { + case SND_AK4524: + case SND_AK4528: + ctl.private_value = AK_COMPOSE(idx, 3, 0, 0); /* register 3 */ + break; + case SND_AK4529: { + int shift = idx == 3 ? 6 : (2 - idx) * 2; + ctl.private_value = AK_COMPOSE(0, 8, shift, 0); /* register 8 with shift */ + break; + } + case SND_AK4355: + ctl.private_value = AK_COMPOSE(idx, 3, 0, 0); + break; + case SND_AK4381: + ctl.private_value = AK_COMPOSE(idx, 1, 1, 0); + break; + } + ctl.private_data = ak; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + } + return 0; +} + + diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/amp.c linux/sound/pci/ice1712/amp.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/amp.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/amp.c 2003-01-28 09:22:25.000000000 -0700 @@ -0,0 +1,63 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000 + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "amp.h" + + +static int __devinit snd_vt1724_amp_init(ice1712_t *ice) +{ + /* only use basic functionality for now */ + + ice->num_total_dacs = 2; /* only PSDOUT0 is connected */ + + return 0; +} + +static int __devinit snd_vt1724_amp_add_controls(ice1712_t *ice) +{ + /* we use pins 39 and 41 of the VT1616 for left and right read outputs */ + snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000); + return 0; +} + + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = { + { + VT1724_SUBDEVICE_AUDIO2000, + "AMP Ltd AUDIO2000", + snd_vt1724_amp_init, + snd_vt1724_amp_add_controls, + }, + { } /* terminator */ +}; + diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/amp.h linux/sound/pci/ice1712/amp.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/amp.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/amp.h 2003-02-28 07:25:08.000000000 -0700 @@ -0,0 +1,34 @@ +#ifndef __SOUND_AMP_H +#define __SOUND_AMP_H + +/* + * ALSA driver for VIA VT1724 (Envy24HT) + * + * Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000 + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000}," + +#define VT1724_SUBDEVICE_AUDIO2000 0x12142417 /* Advanced Micro Peripherals Ltd AUDIO2000 */ + +extern struct snd_ice1712_card_info snd_vt1724_amp_cards[]; + + +#endif /* __SOUND_AMP_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/delta.c linux/sound/pci/ice1712/delta.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/delta.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/delta.c 2003-02-25 08:05:03.000000000 -0700 @@ -0,0 +1,621 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "delta.h" + +#define SND_CS8403 +#include + + +/* + * CS8427 via SPI mode (for Audiophile), emulated I2C + */ + +/* send 8 bits */ +static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp) +{ + int idx; + + for (idx = 7; idx >= 0; idx--) { + tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK); + if (data & (1 << idx)) + tmp |= ICE1712_DELTA_AP_DOUT; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + } +} + +/* read 8 bits */ +static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp) +{ + unsigned char data = 0; + int idx; + + for (idx = 7; idx >= 0; idx--) { + tmp &= ~ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN) + data |= 1 << idx; + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + } + return data; +} + +/* assert chip select */ +static unsigned char ap_cs8427_codec_select(ice1712_t *ice) +{ + unsigned char tmp; + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010LT) { + tmp &= ~ICE1712_DELTA_1010LT_CS; + tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427; + } else { /* Audiophile */ + tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC; + tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL; + } + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + return tmp; +} + +/* deassert chip select */ +static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp) +{ + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010LT) { + tmp &= ~ICE1712_DELTA_1010LT_CS; + tmp |= ICE1712_DELTA_1010LT_CS_NONE; + } else { /* Audiophile */ + tmp |= ICE1712_DELTA_AP_CS_DIGITAL; + } + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); +} + +/* sequential write */ +static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + int res = count; + unsigned char tmp; + + down(&ice->gpio_mutex); + tmp = ap_cs8427_codec_select(ice); + ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */ + while (count-- > 0) + ap_cs8427_write_byte(ice, *bytes++, tmp); + ap_cs8427_codec_deassert(ice, tmp); + up(&ice->gpio_mutex); + return res; +} + +/* sequential read */ +static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + int res = count; + unsigned char tmp; + + down(&ice->gpio_mutex); + tmp = ap_cs8427_codec_select(ice); + ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */ + while (count-- > 0) + *bytes++ = ap_cs8427_read_byte(ice, tmp); + ap_cs8427_codec_deassert(ice, tmp); + up(&ice->gpio_mutex); + return res; +} + +static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + if (addr == 0x10) + return 1; + return -ENOENT; +} + +static snd_i2c_ops_t ap_cs8427_i2c_ops = { + .sendbytes = ap_cs8427_sendbytes, + .readbytes = ap_cs8427_readbytes, + .probeaddr = ap_cs8427_probeaddr, +}; + +/* + */ + +static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits) +{ + unsigned char tmp, mask1, mask2; + int idx; + /* send byte to transmitter */ + mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK; + mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA; + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + for (idx = 7; idx >= 0; idx--) { + tmp &= ~(mask1 | mask2); + if (bits & (1 << idx)) + tmp |= mask2; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(100); + tmp |= mask1; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(100); + } + tmp &= ~mask1; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + up(&ice->gpio_mutex); +} + + +static void delta_spdif_default_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits); +} + +static int delta_spdif_default_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + unsigned int val; + int change; + + val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irq(&ice->reg_lock); + change = ice->spdif.cs8403_bits != val; + ice->spdif.cs8403_bits = val; + if (change && ice->playback_pro_substream == NULL) { + spin_unlock_irq(&ice->reg_lock); + snd_ice1712_delta_cs8403_spdif_write(ice, val); + } else { + spin_unlock_irq(&ice->reg_lock); + } + return change; +} + +static void delta_spdif_stream_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits); +} + +static int delta_spdif_stream_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + unsigned int val; + int change; + + val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irq(&ice->reg_lock); + change = ice->spdif.cs8403_stream_bits != val; + ice->spdif.cs8403_stream_bits = val; + if (change && ice->playback_pro_substream != NULL) { + spin_unlock_irq(&ice->reg_lock); + snd_ice1712_delta_cs8403_spdif_write(ice, val); + } else { + spin_unlock_irq(&ice->reg_lock); + } + return change; +} + + +/* + * AK4524 on Delta 44 and 66 to choose the chip mask + */ +static int delta_ak4524_start(akm4xxx_t *ak, int chip) +{ + snd_ice1712_save_gpio_status(ak->chip); + ak->cs_mask = + ak->cs_addr = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : + ICE1712_DELTA_CODEC_CHIP_B; + return 0; +} + +/* + * AK4524 on Delta1010LT to choose the chip address + */ +static int delta1010lt_ak4524_start(akm4xxx_t *ak, int chip) +{ + snd_ice1712_save_gpio_status(ak->chip); + ak->cs_mask = ICE1712_DELTA_1010LT_CS; + ak->cs_addr = chip << 4; + return 0; +} + +/* + * change the rate of AK4524 on Delta 44/66, AP, 1010LT + */ +static void delta_ak4524_set_rate_val(akm4xxx_t *ak, unsigned int rate) +{ + unsigned char tmp, tmp2; + ice1712_t *ice = ak->chip; + + if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + return; + + /* check before reset ak4524 to avoid unnecessary clicks */ + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + up(&ice->gpio_mutex); + tmp2 = tmp; + tmp2 &= ~ICE1712_DELTA_DFS; + if (rate > 48000) + tmp2 |= ICE1712_DELTA_DFS; + if (tmp == tmp2) + return; + + /* do it again */ + snd_ice1712_akm4xxx_reset(ak, 1); + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS; + if (rate > 48000) + tmp |= ICE1712_DELTA_DFS; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + up(&ice->gpio_mutex); + snd_ice1712_akm4xxx_reset(ak, 0); +} + + +/* + * SPDIF ops for Delta 1010, Dio, 66 + */ + +/* open callback */ +static void delta_open_spdif(ice1712_t *ice, snd_pcm_substream_t * substream) +{ + ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits; +} + +/* set up */ +static void delta_setup_spdif(ice1712_t *ice, int rate) +{ + unsigned long flags; + unsigned int tmp; + int change; + + spin_lock_irqsave(&ice->reg_lock, flags); + tmp = ice->spdif.cs8403_stream_bits; + if (tmp & 0x01) /* consumer */ + tmp &= (tmp & 0x01) ? ~0x06 : ~0x18; + switch (rate) { + case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break; + case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break; + case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break; + default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break; + } + change = ice->spdif.cs8403_stream_bits != tmp; + ice->spdif.cs8403_stream_bits = tmp; + spin_unlock_irqrestore(&ice->reg_lock, flags); + if (change) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id); + snd_ice1712_delta_cs8403_spdif_write(ice, tmp); +} + + +/* + * initialize the chips on M-Audio cards + */ + +static akm4xxx_t akm_audiophile __devinitdata = { + .type = SND_AK4528, + .num_adcs = 2, + .num_dacs = 2, + .caddr = 2, + .cif = 0, + .data_mask = ICE1712_DELTA_AP_DOUT, + .clk_mask = ICE1712_DELTA_AP_CCLK, + .cs_mask = ICE1712_DELTA_AP_CS_CODEC, + .cs_addr = ICE1712_DELTA_AP_CS_CODEC, + .cs_none = 0, + .add_flags = ICE1712_DELTA_AP_CS_DIGITAL, + .mask_flags = 0, + .ops = { + .set_rate_val = delta_ak4524_set_rate_val + } +}; + +static akm4xxx_t akm_delta410 __devinitdata = { + .type = SND_AK4529, + .num_adcs = 2, + .num_dacs = 8, + .caddr = 0, + .cif = 0, + .data_mask = ICE1712_DELTA_AP_DOUT, + .clk_mask = ICE1712_DELTA_AP_CCLK, + .cs_mask = ICE1712_DELTA_AP_CS_CODEC, + .cs_addr = ICE1712_DELTA_AP_CS_CODEC, + .cs_none = 0, + .add_flags = ICE1712_DELTA_AP_CS_DIGITAL, + .mask_flags = 0, + .ops = { + .set_rate_val = delta_ak4524_set_rate_val + } +}; + +static akm4xxx_t akm_delta1010lt __devinitdata = { + .type = SND_AK4524, + .num_adcs = 8, + .num_dacs = 8, + .caddr = 2, + .cif = 0, /* the default level of the CIF pin from AK4524 */ + .data_mask = ICE1712_DELTA_1010LT_DOUT, + .clk_mask = ICE1712_DELTA_1010LT_CCLK, + .cs_mask = 0, + .cs_addr = 0, /* set later */ + .cs_none = ICE1712_DELTA_1010LT_CS_NONE, + .add_flags = 0, + .mask_flags = 0, + .ops = { + .start = delta1010lt_ak4524_start, + .set_rate_val = delta_ak4524_set_rate_val + } +}; + +static akm4xxx_t akm_delta44 __devinitdata = { + .type = SND_AK4524, + .num_adcs = 4, + .num_dacs = 4, + .caddr = 2, + .cif = 0, /* the default level of the CIF pin from AK4524 */ + .data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA, + .clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK, + .cs_mask = 0, + .cs_addr = 0, /* set later */ + .cs_none = 0, + .add_flags = 0, + .mask_flags = 0, + .ops = { + .start = delta_ak4524_start, + .set_rate_val = delta_ak4524_set_rate_val + } +}; + +static int __devinit snd_ice1712_delta_init(ice1712_t *ice) +{ + int err; + akm4xxx_t *ak; + + /* determine I2C, DACs and ADCs */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + ice->num_total_dacs = 2; + break; + case ICE1712_SUBDEVICE_DELTA410: + ice->num_total_dacs = 8; + break; + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_DELTA66: + ice->num_total_dacs = ice->omni ? 8 : 4; + break; + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTA1010LT: + ice->num_total_dacs = 8; + break; + } + + /* initialize spdif */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_DELTA410: + case ICE1712_SUBDEVICE_DELTA1010LT: + if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { + snd_printk("unable to create I2C bus\n"); + return err; + } + ice->i2c->private_data = ice; + ice->i2c->ops = &ap_cs8427_i2c_ops; + if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0) + return err; + break; + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + ice->spdif.ops.open = delta_open_spdif; + ice->spdif.ops.setup_rate = delta_setup_spdif; + ice->spdif.ops.default_get = delta_spdif_default_get; + ice->spdif.ops.default_put = delta_spdif_default_put; + ice->spdif.ops.stream_get = delta_spdif_stream_get; + ice->spdif.ops.stream_put = delta_spdif_stream_put; + /* Set spdif defaults */ + snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits); + break; + } + + /* no analog? */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + return 0; + } + + /* second stage of initialization, analog parts and others */ + ak = ice->akm = kmalloc(sizeof(akm4xxx_t), GFP_KERNEL); + if (! ak) + return -ENOMEM; + ice->akm_codecs = 1; + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + snd_ice1712_akm4xxx_init(ak, &akm_audiophile, ice); + break; + case ICE1712_SUBDEVICE_DELTA410: + snd_ice1712_akm4xxx_init(ak, &akm_delta410, ice); + break; + case ICE1712_SUBDEVICE_DELTA1010LT: + snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, ice); + break; + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_DELTA44: + snd_ice1712_akm4xxx_init(ak, &akm_delta44, ice); + break; + default: + snd_BUG(); + return -EINVAL; + } + + return 0; +} + + +/* + * additional controls for M-Audio cards + */ + +static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0); +static snd_kcontrol_new_t snd_ice1712_delta1010lt_wordclock_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 1, 0); +static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); +static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0); +static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); + + +static int __devinit snd_ice1712_delta_add_controls(ice1712_t *ice) +{ + int err; + + /* 1010 and dio specific controls */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_DELTADIO2496: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_DELTA1010LT: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice)); + if (err < 0) + return err; + break; + } + + /* normal spdif controls */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_DELTA410: + case ICE1712_SUBDEVICE_DELTA1010LT: + err = snd_ice1712_spdif_build_controls(ice); + if (err < 0) + return err; + break; + } + + /* spdif status in */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice)); + if (err < 0) + return err; + break; + } + + /* ak4524 controls */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010LT: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_DELTA410: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_DELTA66: + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; + break; + } + return 0; +} + + +/* entry point */ +struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = { + { + ICE1712_SUBDEVICE_DELTA1010, + "M Audio Delta 1010", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + }, + { + ICE1712_SUBDEVICE_DELTADIO2496, + "M Audio Delta DiO 2496", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + 1, /* NO MPU */ + }, + { + ICE1712_SUBDEVICE_DELTA66, + "M Audio Delta 66", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + 1, /* NO MPU */ + }, + { + ICE1712_SUBDEVICE_DELTA44, + "M Audio Delta 44", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + 1, /* NO MPU */ + }, + { + ICE1712_SUBDEVICE_AUDIOPHILE, + "M Audio Audiophile 24/96", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + }, + { + ICE1712_SUBDEVICE_DELTA410, + "M Audio Delta 410", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + }, + { + ICE1712_SUBDEVICE_DELTA1010LT, + "M Audio Delta 1010LT", + snd_ice1712_delta_init, + snd_ice1712_delta_add_controls, + }, + { } /* terminator */ +}; diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/delta.h linux/sound/pci/ice1712/delta.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/delta.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/delta.h 2002-11-10 06:55:23.000000000 -0700 @@ -0,0 +1,137 @@ +#ifndef __SOUND_DELTA_H +#define __SOUND_DELTA_H + +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define DELTA_DEVICE_DESC \ + "{MidiMan M Audio,Delta 1010},"\ + "{MidiMan M Audio,Delta 1010LT},"\ + "{MidiMan M Audio,Delta DiO 2496},"\ + "{MidiMan M Audio,Delta 66},"\ + "{MidiMan M Audio,Delta 44},"\ + "{MidiMan M Audio,Audiophile 24/96}," + +#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 +#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6 +#define ICE1712_SUBDEVICE_DELTA66 0x121432d6 +#define ICE1712_SUBDEVICE_DELTA44 0x121433d6 +#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6 +#define ICE1712_SUBDEVICE_DELTA410 0x121438d6 +#define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6 + +/* entry point */ +extern struct snd_ice1712_card_info snd_ice1712_delta_cards[]; + + +/* + * MidiMan M-Audio Delta GPIO definitions + */ + +/* MidiMan M-Audio Delta shared pins */ +#define ICE1712_DELTA_DFS 0x01 /* fast/slow sample rate mode */ + /* (>48kHz must be 1) */ +#define ICE1712_DELTA_SPDIF_IN_STAT 0x02 + /* S/PDIF input status */ + /* 0 = valid signal is present */ + /* all except Delta44 */ + /* look to CS8414 datasheet */ +#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04 + /* S/PDIF output status clock */ + /* (writting on rising edge - 0->1) */ + /* all except Delta44 */ + /* look to CS8404A datasheet */ +#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08 + /* S/PDIF output status data */ + /* all except Delta44 */ + /* look to CS8404A datasheet */ +/* MidiMan M-Audio DeltaDiO */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10 + /* coaxial (0), optical (1) */ + /* S/PDIF input select*/ + +/* MidiMan M-Audio Delta1010 */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10 + /* 1 - clock are taken from S/PDIF input */ + /* 0 - clock are taken from Word Clock input */ + /* affected SPMCLKIN pin of Envy24 */ +#define ICE1712_DELTA_WORD_CLOCK_STATUS 0x20 + /* 0 = valid word clock signal is present */ + +/* MidiMan M-Audio Delta66 */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10 + /* AKM4524 serial data */ +#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20 + /* AKM4524 serial clock */ + /* (writting on rising edge - 0->1 */ +#define ICE1712_DELTA_CODEC_CHIP_A 0x40 +#define ICE1712_DELTA_CODEC_CHIP_B 0x80 + /* 1 - select chip A or B */ + +/* MidiMan M-Audio Delta44 */ +/* 0x01 = DFS */ +/* 0x10 = CODEC_SERIAL_DATA */ +/* 0x20 = CODEC_SERIAL_CLOCK */ +/* 0x40 = CODEC_CHIP_A */ +/* 0x80 = CODEC_CHIP_B */ + +/* MidiMan M-Audio Audiophile/Delta410 definitions */ +/* thanks to Kristof Pelckmans for Delta410 info */ +/* 0x01 = DFS */ +#define ICE1712_DELTA_AP_CCLK 0x02 /* SPI clock */ + /* (clocking on rising edge - 0->1) */ +#define ICE1712_DELTA_AP_DIN 0x04 /* data input */ +#define ICE1712_DELTA_AP_DOUT 0x08 /* data output */ +#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */ + /* low signal = select */ +#define ICE1712_DELTA_AP_CS_CODEC 0x20 /* AK4528 (audiophile), AK4529 (Delta410) chip select */ + /* low signal = select */ + +/* MidiMan M-Audio Delta1010LT definitions */ +/* thanks to Anders Johansson */ +/* 0x01 = DFS */ +#define ICE1712_DELTA_1010LT_CCLK 0x02 /* SPI clock (AK4524 + CS8427) */ +#define ICE1712_DELTA_1010LT_DIN 0x04 /* data input (CS8427) */ +#define ICE1712_DELTA_1010LT_DOUT 0x08 /* data output (AK4524 + CS8427) */ +#define ICE1712_DELTA_1010LT_CS 0x70 /* mask for CS address */ +#define ICE1712_DELTA_1010LT_CS_CHIP_A 0x00 /* AK4524 #0 */ +#define ICE1712_DELTA_1010LT_CS_CHIP_B 0x10 /* AK4524 #1 */ +#define ICE1712_DELTA_1010LT_CS_CHIP_C 0x20 /* AK4524 #2 */ +#define ICE1712_DELTA_1010LT_CS_CHIP_D 0x30 /* AK4524 #3 */ +#define ICE1712_DELTA_1010LT_CS_CS8427 0x40 /* CS8427 */ +#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */ +#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */ + +#endif /* __SOUND_DELTA_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/envy24ht.h linux/sound/pci/ice1712/envy24ht.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/envy24ht.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/envy24ht.h 2003-02-25 08:05:03.000000000 -0700 @@ -0,0 +1,213 @@ +#ifndef __SOUND_VT1724_H +#define __SOUND_VT1724_H + +/* + * ALSA driver for ICEnsemble VT1724 (Envy24) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#include "ice1712.h" + +enum { + ICE_EEP2_SYSCONF = 0, /* 06 */ + ICE_EEP2_ACLINK, /* 07 */ + ICE_EEP2_I2S, /* 08 */ + ICE_EEP2_SPDIF, /* 09 */ + ICE_EEP2_GPIO_DIR, /* 0a */ + ICE_EEP2_GPIO_DIR1, /* 0b */ + ICE_EEP2_GPIO_DIR2, /* 0c */ + ICE_EEP2_GPIO_MASK, /* 0d */ + ICE_EEP2_GPIO_MASK1, /* 0e */ + ICE_EEP2_GPIO_MASK2, /* 0f */ + ICE_EEP2_GPIO_STATE, /* 10 */ + ICE_EEP2_GPIO_STATE1, /* 11 */ + ICE_EEP2_GPIO_STATE2 /* 12 */ +}; + +/* + * Direct registers + */ + +#define ICEREG1724(ice, x) ((ice)->port + VT1724_REG_##x) + +#define VT1724_REG_CONTROL 0x00 /* byte */ +#define VT1724_RESET 0x80 /* reset whole chip */ +#define VT1724_REG_IRQMASK 0x01 /* byte */ +#define VT1724_IRQ_MPU_RX 0x80 +#define VT1724_IRQ_MPU_TX 0x20 +#define VT1724_IRQ_MTPCM 0x10 +#define VT1724_REG_IRQSTAT 0x02 /* byte */ +/* look to VT1724_IRQ_* */ +#define VT1724_REG_SYS_CFG 0x04 /* byte - system configuration PCI60 on Envy24*/ +#define VT1724_CFG_CLOCK 0xc0 +#define VT1724_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */ +#define VT1724_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */ +#define VT1724_CFG_MPU401 0x20 /* MPU401 UARTs */ +#define VT1724_CFG_ADC_MASK 0x0c /* one, two or one and S/PDIF, stereo ADCs */ +#define VT1724_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */ + +#define VT1724_REG_AC97_CFG 0x05 /* byte */ +#define VT1724_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */ +#define VT1724_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */ + +#define VT1724_REG_I2S_FEATURES 0x06 /* byte */ +#define VT1724_CFG_I2S_VOLUME 0x80 /* volume/mute capability */ +#define VT1724_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */ +#define VT1724_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */ +#define VT1724_CFG_I2S_192KHZ 0x08 /* supports 192kHz sampling */ +#define VT1724_CFG_I2S_OTHER 0x07 /* other I2S IDs */ + +#define VT1724_REG_SPDIF_CFG 0x07 /* byte */ +#define VT1724_CFG_SPDIF_OUT_EN 0x80 /*Internal S/PDIF output is enabled*/ +#define VT1724_CFG_SPDIF_OUT_INT 0x40 /*Internal S/PDIF output is implemented*/ +#define VT1724_CFG_I2S_CHIPID 0x3c /* I2S chip ID */ +#define VT1724_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */ +#define VT1724_CFG_SPDIF_OUT 0x01 /* External S/PDIF output is present */ + +/*there is no consumer AC97 codec with the VT1724*/ +//#define VT1724_REG_AC97_INDEX 0x08 /* byte */ +//#define VT1724_REG_AC97_CMD 0x09 /* byte */ + +#define VT1724_REG_MPU_TXFIFO 0x0a /*byte ro. number of bytes in TX fifo*/ +#define VT1724_REG_MPU_RXFIFO 0x0b /*byte ro. number of bytes in RX fifo*/ + +//are these 2 the wrong way around? they don't seem to be used yet anyway +#define VT1724_REG_MPU_CTRL 0x0c /* byte */ +#define VT1724_REG_MPU_DATA 0x0d /* byte */ + +#define VT1724_REG_MPU_FIFO_WM 0x0e /*byte set the high/low watermarks for RX/TX fifos*/ +#define VT1724_MPU_RX_FIFO 0x20 //1=rx fifo watermark 0=tx fifo watermark +#define VT1724_MPU_FIFO_MASK 0x1f + +#define VT1724_REG_I2C_DEV_ADDR 0x10 /* byte */ +#define VT1724_I2C_WRITE 0x01 /* write direction */ +#define VT1724_REG_I2C_BYTE_ADDR 0x11 /* byte */ +#define VT1724_REG_I2C_DATA 0x12 /* byte */ +#define VT1724_REG_I2C_CTRL 0x13 /* byte */ +#define VT1724_I2C_EEPROM 0x80 /* EEPROM exists */ +#define VT1724_I2C_BUSY 0x01 /* busy bit */ + +#define VT1724_REG_GPIO_DATA 0x14 /* word */ +#define VT1724_REG_GPIO_WRITE_MASK 0x16 /* word */ +#define VT1724_REG_GPIO_DIRECTION 0x18 /* dword? (3 bytes) 0=input 1=output. + bit3 - during reset used for Eeprom power-on strapping + if TESTEN# pin active, bit 2 always input*/ +#define VT1724_REG_POWERDOWN 0x1c +#define VT1724_REG_GPIO_DATA_22 0x1e /* byte direction for GPIO 16:22 */ +#define VT1724_REG_GPIO_WRITE_MASK_22 0x1f /* byte write mask for GPIO 16:22 */ + + +/* + * Professional multi-track direct control registers + */ + +#define ICEMT1724(ice, x) ((ice)->profi_port + VT1724_MT_##x) + +#define VT1724_MT_IRQ 0x00 /* byte - interrupt mask */ +#define VT1724_MULTI_PDMA4 0x80 /* SPDIF Out / PDMA4 */ +#define VT1724_MULTI_PDMA3 0x40 /* PDMA3 */ +#define VT1724_MULTI_PDMA2 0x20 /* PDMA2 */ +#define VT1724_MULTI_PDMA1 0x10 /* PDMA1 */ +#define VT1724_MULTI_FIFO_ERR 0x08 /* DMA FIFO underrun/overrun. */ +#define VT1724_MULTI_RDMA1 0x04 /* RDMA1 (S/PDIF input) */ +#define VT1724_MULTI_RDMA0 0x02 /* RMDA0 */ +#define VT1724_MULTI_PDMA0 0x01 /* MC Interleave/PDMA0 */ + +#define VT1724_MT_RATE 0x01 /* byte - sampling rate select */ +#define VT1724_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */ +#define VT1724_MT_I2S_FORMAT 0x02 /* byte - I2S data format */ +#define VT1724_MT_I2S_MCLK_128X 0x08 +#define VT1724_MT_I2S_FORMAT_MASK 0x03 +#define VT1724_MT_I2S_FORMAT_I2S 0x00 +#define VT1724_MT_DMA_INT_MASK 0x03 /* byte -DMA Interrupt Mask */ +/* lool to VT1724_MULTI_* */ +#define VT1724_MT_AC97_INDEX 0x04 /* byte - AC'97 index */ +#define VT1724_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */ +#define VT1724_AC97_COLD 0x80 /* cold reset */ +#define VT1724_AC97_WARM 0x40 /* warm reset */ +#define VT1724_AC97_WRITE 0x20 /* W: write, R: write in progress */ +#define VT1724_AC97_READ 0x10 /* W: read, R: read in progress */ +#define VT1724_AC97_READY 0x08 /* codec ready status bit */ +#define VT1724_AC97_PBK_VSR 0x02 /* playback VSR */ +#define VT1724_AC97_CAP_VSR 0x01 /* capture VSR */ +#define VT1724_MT_AC97_DATA 0x06 /* word - AC'97 data */ +#define VT1724_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */ +#define VT1724_MT_PLAYBACK_SIZE 0x14 /* dword - playback size */ +#define VT1724_MT_DMA_CONTROL 0x18 /* byte - control */ +#define VT1724_PDMA4_START 0x80 /* SPDIF out / PDMA4 start */ +#define VT1724_PDMA3_START 0x40 /* PDMA3 start */ +#define VT1724_PDMA2_START 0x20 /* PDMA2 start */ +#define VT1724_PDMA1_START 0x10 /* PDMA1 start */ +#define VT1724_RDMA1_START 0x04 /* RDMA1 start */ +#define VT1724_RDMA0_START 0x02 /* RMDA0 start */ +#define VT1724_PDMA0_START 0x01 /* MC Interleave / PDMA0 start */ +#define VT1724_MT_BURST 0x19 /* Interleaved playback DMA Active streams / PCI burst size */ +#define VT1724_MT_DMA_FIFO_ERR 0x1a /*Global playback and record DMA FIFO Underrun/Overrun */ +#define VT1724_PDMA4_UNDERRUN 0x80 +#define VT1724_PDMA2_UNDERRUN 0x40 +#define VT1724_PDMA3_UNDERRUN 0x20 +#define VT1724_PDMA1_UNDERRUN 0x10 +#define VT1724_RDMA1_UNDERRUN 0x04 +#define VT1724_RDMA0_UNDERRUN 0x02 +#define VT1724_PDMA0_UNDERRUN 0x01 +#define VT1724_MT_DMA_PAUSE 0x1b /*Global playback and record DMA FIFO pause/resume */ +#define VT1724_PDMA4_PAUSE 0x80 +#define VT1724_PDMA3_PAUSE 0x40 +#define VT1724_PDMA2_PAUSE 0x20 +#define VT1724_PDMA1_PAUSE 0x10 +#define VT1724_RDMA1_PAUSE 0x04 +#define VT1724_RDMA0_PAUSE 0x02 +#define VT1724_PDMA0_PAUSE 0x01 +#define VT1724_MT_PLAYBACK_COUNT 0x1c /* word - playback count */ +#define VT1724_MT_CAPTURE_ADDR 0x20 /* dword - capture address */ +#define VT1724_MT_CAPTURE_SIZE 0x24 /* word - capture size */ +#define VT1724_MT_CAPTURE_COUNT 0x26 /* word - capture count */ + +#define VT1724_MT_ROUTE_PLAYBACK 0x2c /* word */ + +#define VT1724_MT_RDMA1_ADDR 0x30 /* dword - RDMA1 capture address */ +#define VT1724_MT_RDMA1_SIZE 0x34 /* word - RDMA1 capture size */ +#define VT1724_MT_RDMA1_COUNT 0x36 /* word - RDMA1 capture count */ + +#define VT1724_MT_SPDIF_CTRL 0x3c /* word */ +#define VT1724_MT_MONITOR_PEAKINDEX 0x3e /* byte */ +#define VT1724_MT_MONITOR_PEAKDATA 0x3f /* byte */ + +/* concurrent stereo channels */ +#define VT1724_MT_PDMA4_ADDR 0x40 /* dword */ +#define VT1724_MT_PDMA4_SIZE 0x44 /* word */ +#define VT1724_MT_PDMA4_COUNT 0x46 /* word */ +#define VT1724_MT_PDMA3_ADDR 0x50 /* dword */ +#define VT1724_MT_PDMA3_SIZE 0x54 /* word */ +#define VT1724_MT_PDMA3_COUNT 0x56 /* word */ +#define VT1724_MT_PDMA2_ADDR 0x60 /* dword */ +#define VT1724_MT_PDMA2_SIZE 0x64 /* word */ +#define VT1724_MT_PDMA2_COUNT 0x66 /* word */ +#define VT1724_MT_PDMA1_ADDR 0x70 /* dword */ +#define VT1724_MT_PDMA1_SIZE 0x74 /* word */ +#define VT1724_MT_PDMA1_COUNT 0x76 /* word */ + + +#endif /* __SOUND_VT1724_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/ews.c linux/sound/pci/ice1712/ews.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/ews.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/ews.c 2003-03-05 05:07:27.000000000 -0700 @@ -0,0 +1,990 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire + * + * Copyright (c) 2000 Jaroslav Kysela + * 2002 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "ews.h" + +#define SND_CS8404 +#include + +/* + * access via i2c mode (for EWX 24/96, EWS 88MT&D) + */ + +/* send SDA and SCL */ +static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char tmp = 0; + if (clk) + tmp |= ICE1712_EWX2496_SERIAL_CLOCK; + if (data) + tmp |= ICE1712_EWX2496_SERIAL_DATA; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); +} + +static int ewx_i2c_getclock(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0; +} + +static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + int bit; + /* set RW pin to low */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0); + if (ack) + udelay(5); + bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0; + /* set RW pin to high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW); + /* reset write mask */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK); + return bit; +} + +static void ewx_i2c_start(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char mask; + + snd_ice1712_save_gpio_status(ice); + /* set RW high */ + mask = ICE1712_EWX2496_RW; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */ + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */ + break; + } + snd_ice1712_gpio_write_bits(ice, mask, mask); +} + +static void ewx_i2c_stop(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + snd_ice1712_restore_gpio_status(ice); +} + +static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char mask = 0; + + if (clock) + mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */ + if (data) + mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */ + ice->gpio.direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA); + ice->gpio.direction |= mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio.direction); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask); +} + +static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = { + .start = ewx_i2c_start, + .stop = ewx_i2c_stop, + .direction = ewx_i2c_direction, + .setlines = ewx_i2c_setlines, + .getclock = ewx_i2c_getclock, + .getdata = ewx_i2c_getdata, +}; + + +/* + * AK4524 access + */ + +/* AK4524 chip select; address 0x48 bit 0-3 */ +static int snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask) +{ + unsigned char data, ndata; + + snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[1], &data, 1) != 1) + goto __error; + ndata = (data & 0xf0) | chip_mask; + if (ndata != data) + if (snd_i2c_sendbytes(ice->i2cdevs[1], &ndata, 1) != 1) + goto __error; + snd_i2c_unlock(ice->i2c); + return 0; + + __error: + snd_i2c_unlock(ice->i2c); + snd_printk(KERN_ERR "AK4524 chip select failed, check cable to the front module\n"); + return -EIO; +} + +/* start callback for EWS88MT, needs to select a certain chip mask */ +static int ews88mt_ak4524_start(akm4xxx_t *ak, int chip) +{ + ice1712_t *ice = ak->chip; + unsigned char tmp; + /* assert AK4524 CS */ + if (snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f) < 0) + return -EINVAL; + snd_ice1712_save_gpio_status(ice); + tmp = ICE1712_EWS88_SERIAL_DATA | + ICE1712_EWS88_SERIAL_CLOCK | + ICE1712_EWS88_RW; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio.direction | tmp); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp); + return 0; +} + +/* stop callback for EWS88MT, needs to deselect chip mask */ +static void ews88mt_ak4524_stop(akm4xxx_t *ak) +{ + ice1712_t *ice = ak->chip; + snd_ice1712_restore_gpio_status(ice); + udelay(1); + snd_ice1712_ews88mt_chip_select(ice, 0x0f); +} + +/* start callback for EWX24/96 */ +static int ewx2496_ak4524_start(akm4xxx_t *ak, int chip) +{ + ice1712_t *ice = ak->chip; + unsigned char tmp; + snd_ice1712_save_gpio_status(ice); + tmp = ICE1712_EWX2496_SERIAL_DATA | + ICE1712_EWX2496_SERIAL_CLOCK | + ICE1712_EWX2496_AK4524_CS | + ICE1712_EWX2496_RW; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio.direction | tmp); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp); + return 0; +} + +/* start callback for DMX 6fire */ +static int dmx6fire_ak4524_start(akm4xxx_t *ak, int chip) +{ + ice1712_t *ice = ak->chip; + unsigned char tmp; + snd_ice1712_save_gpio_status(ice); + tmp = ak->cs_mask = ak->cs_addr = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK; + tmp |= ICE1712_6FIRE_SERIAL_DATA | + ICE1712_6FIRE_SERIAL_CLOCK | + ICE1712_6FIRE_RW; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio.direction | tmp); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp); + return 0; +} + + +/* + * CS8404 interface on EWS88MT/D + */ + +static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits) +{ + unsigned char bytes[2]; + + snd_i2c_lock(ice->i2c); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, goto _error); + break; + case ICE1712_SUBDEVICE_EWS88D: + snd_runtime_check(snd_i2c_readbytes(ice->i2cdevs[0], bytes, 2) == 2, goto _error); + if (bits != bytes[1]) { + bytes[1] = bits; + snd_runtime_check(snd_i2c_readbytes(ice->i2cdevs[0], bytes, 2) == 2, goto _error); + } + break; + } + _error: + snd_i2c_unlock(ice->i2c); +} + +/* + */ + +static void ews88_spdif_default_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits); +} + +static int ews88_spdif_default_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + unsigned int val; + int change; + + val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irq(&ice->reg_lock); + change = ice->spdif.cs8403_bits != val; + ice->spdif.cs8403_bits = val; + if (change && ice->playback_pro_substream == NULL) { + spin_unlock_irq(&ice->reg_lock); + snd_ice1712_ews_cs8404_spdif_write(ice, val); + } else { + spin_unlock_irq(&ice->reg_lock); + } + return change; +} + +static void ews88_spdif_stream_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits); +} + +static int ews88_spdif_stream_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol) +{ + unsigned int val; + int change; + + val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irq(&ice->reg_lock); + change = ice->spdif.cs8403_stream_bits != val; + ice->spdif.cs8403_stream_bits = val; + if (change && ice->playback_pro_substream != NULL) { + spin_unlock_irq(&ice->reg_lock); + snd_ice1712_ews_cs8404_spdif_write(ice, val); + } else { + spin_unlock_irq(&ice->reg_lock); + } + return change; +} + + +/* open callback */ +static void ews88_open_spdif(ice1712_t *ice, snd_pcm_substream_t * substream) +{ + ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits; +} + +/* set up SPDIF for EWS88MT / EWS88D */ +static void ews88_setup_spdif(ice1712_t *ice, int rate) +{ + unsigned long flags; + unsigned char tmp; + int change; + + spin_lock_irqsave(&ice->reg_lock, flags); + tmp = ice->spdif.cs8403_stream_bits; + if (tmp & 0x10) /* consumer */ + tmp &= (tmp & 0x01) ? ~0x06 : ~0x60; + switch (rate) { + case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break; + case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break; + case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break; + default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break; + } + change = ice->spdif.cs8403_stream_bits != tmp; + ice->spdif.cs8403_stream_bits = tmp; + spin_unlock_irqrestore(&ice->reg_lock, flags); + if (change) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id); + snd_ice1712_ews_cs8404_spdif_write(ice, tmp); +} + + +/* + */ +static akm4xxx_t akm_ews88mt __devinitdata = { + .num_adcs = 8, + .num_dacs = 8, + .type = SND_AK4524, + .caddr = 2, + .cif = 1, /* CIF high */ + .data_mask = ICE1712_EWS88_SERIAL_DATA, + .clk_mask = ICE1712_EWS88_SERIAL_CLOCK, + .cs_mask = 0, + .cs_addr = 0, + .cs_none = 0, /* no chip select on gpio */ + .add_flags = ICE1712_EWS88_RW, /* set rw bit high */ + .mask_flags = 0, + .ops = { + .start = ews88mt_ak4524_start, + .stop = ews88mt_ak4524_stop + } +}; + +static akm4xxx_t akm_ewx2496 __devinitdata = { + .num_adcs = 2, + .num_dacs = 2, + .type = SND_AK4524, + .caddr = 2, + .cif = 1, /* CIF high */ + .data_mask = ICE1712_EWS88_SERIAL_DATA, + .clk_mask = ICE1712_EWS88_SERIAL_CLOCK, + .cs_mask = ICE1712_EWX2496_AK4524_CS, + .cs_addr = ICE1712_EWX2496_AK4524_CS, + .cs_none = 0, + .add_flags = ICE1712_EWS88_RW, /* set rw bit high */ + .mask_flags = 0, + .ops = { + .start = ewx2496_ak4524_start + } +}; + +static akm4xxx_t akm_6fire __devinitdata = { + .num_adcs = 6, + .num_dacs = 6, + .type = SND_AK4524, + .caddr = 2, + .cif = 1, /* CIF high */ + .data_mask = ICE1712_6FIRE_SERIAL_DATA, + .clk_mask = ICE1712_6FIRE_SERIAL_CLOCK, + .cs_mask = 0, + .cs_addr = 0, /* set later */ + .cs_none = 0, + .add_flags = ICE1712_6FIRE_RW, /* set rw bit high */ + .mask_flags = 0, + .ops = { + .start = dmx6fire_ak4524_start + } +}; + + +/* + * initialize the chip + */ + +static int __devinit snd_ice1712_ews_init(ice1712_t *ice) +{ + int err; + akm4xxx_t *ak; + + /* set the analog DACs */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + ice->num_total_dacs = 2; + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + ice->num_total_dacs = 8; + break; + case ICE1712_SUBDEVICE_EWS88D: + break; /* no analog */ + case ICE1712_SUBDEVICE_DMX6FIRE: + ice->num_total_dacs = 6; + break; + } + + /* create i2c */ + if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { + snd_printk("unable to create I2C bus\n"); + return err; + } + ice->i2c->private_data = ice; + ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops; + + /* create i2c devices */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DMX6FIRE: + if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->i2cdevs[0])) < 0) { + snd_printk("PCF9554 initialization failed\n"); + return err; + } + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0) + return err; + if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->i2cdevs[0])) < 0) + return err; + if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->i2cdevs[1])) < 0) + return err; + /* Check if the front module is connected */ + if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88D: + if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->i2cdevs[0])) < 0) + return err; + break; + } + + /* set up SPDIF interface */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0) + return err; + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + if ((err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR)) < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_EWS88D: + /* set up CS8404 */ + ice->spdif.ops.open = ews88_open_spdif; + ice->spdif.ops.setup_rate = ews88_setup_spdif; + ice->spdif.ops.default_get = ews88_spdif_default_get; + ice->spdif.ops.default_put = ews88_spdif_default_put; + ice->spdif.ops.stream_get = ews88_spdif_stream_get; + ice->spdif.ops.stream_put = ews88_spdif_stream_put; + /* Set spdif defaults */ + snd_ice1712_ews_cs8404_spdif_write(ice, ice->spdif.cs8403_bits); + break; + } + + /* no analog? */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88D: + return 0; + } + + /* analog section */ + ak = ice->akm = kmalloc(sizeof(akm4xxx_t), GFP_KERNEL); + if (! ak) + return -ENOMEM; + ice->akm_codecs = 1; + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, ice); + break; + case ICE1712_SUBDEVICE_EWX2496: + snd_ice1712_akm4xxx_init(ak, &akm_ewx2496, ice); + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + snd_ice1712_akm4xxx_init(ak, &akm_6fire, ice); + break; + } + + return 0; +} + +/* + * EWX 24/96 specific controls + */ + +/* i/o sensitivity - this callback is shared among other devices, too */ +static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ + + static char *texts[2] = { + "+4dBu", "-10dBV", + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + + snd_ice1712_save_gpio_status(ice); + ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0; + snd_ice1712_restore_gpio_status(ice); + return 0; +} + +static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = ucontrol->value.enumerated.item[0] ? mask : 0; + snd_ice1712_save_gpio_status(ice); + val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + nval |= val & ~mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval); + snd_ice1712_restore_gpio_status(ice); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_ewx2496_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Sensitivity Switch", + .info = snd_ice1712_ewx_io_sense_info, + .get = snd_ice1712_ewx_io_sense_get, + .put = snd_ice1712_ewx_io_sense_put, + .private_value = ICE1712_EWX2496_AIN_SEL, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Output Sensitivity Switch", + .info = snd_ice1712_ewx_io_sense_info, + .get = snd_ice1712_ewx_io_sense_get, + .put = snd_ice1712_ewx_io_sense_put, + .private_value = ICE1712_EWX2496_AOUT_SEL, + }, +}; + + +/* + * EWS88MT specific controls + */ +/* analog output sensitivity;; address 0x48 bit 6 */ +static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char data; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[1], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */ + return 0; +} + +/* analog output sensitivity;; address 0x48 bit 6 */ +static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char data, ndata; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[1], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0); + if (ndata != data && snd_i2c_sendbytes(ice->i2cdevs[1], &ndata, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + return ndata != data; +} + +/* analog input sensitivity; address 0x46 */ +static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int channel = kcontrol->id.index; + unsigned char data; + + snd_assert(channel >= 0 && channel <= 7, return 0); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[0], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + /* reversed; high = +4dBu, low = -10dBV */ + ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1; + snd_i2c_unlock(ice->i2c); + return 0; +} + +/* analog output sensitivity; address 0x46 */ +static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int channel = kcontrol->id.index; + unsigned char data, ndata; + + snd_assert(channel >= 0 && channel <= 7, return 0); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[0], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel)); + if (ndata != data && snd_i2c_sendbytes(ice->i2cdevs[0], &ndata, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + return ndata != data; +} + +static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Sensitivity Switch", + .info = snd_ice1712_ewx_io_sense_info, + .get = snd_ice1712_ews88mt_input_sense_get, + .put = snd_ice1712_ews88mt_input_sense_put, +}; + +static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Output Sensitivity Switch", + .info = snd_ice1712_ewx_io_sense_info, + .get = snd_ice1712_ews88mt_output_sense_get, + .put = snd_ice1712_ews88mt_output_sense_put, +}; + + +/* + * EWS88D specific controls + */ + +static int snd_ice1712_ews88d_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + unsigned char data[2]; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[0], data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01; + if (invert) + data[0] ^= 0x01; + ucontrol->value.integer.value[0] = data[0]; + return 0; +} + +static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + unsigned char data[2], ndata[2]; + int change; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->i2cdevs[0], data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7)); + if (invert) { + if (! ucontrol->value.integer.value[0]) + ndata[shift >> 3] |= (1 << (shift & 7)); + } else { + if (ucontrol->value.integer.value[0]) + ndata[shift >> 3] |= (1 << (shift & 7)); + } + change = (data[shift >> 3] != ndata[shift >> 3]); + if (change && snd_i2c_sendbytes(ice->i2cdevs[0], data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + return change; +} + +#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \ +{ .iface = xiface,\ + .name = xname,\ + .access = xaccess,\ + .info = snd_ice1712_ews88d_control_info,\ + .get = snd_ice1712_ews88d_control_get,\ + .put = snd_ice1712_ews88d_control_put,\ + .private_value = xshift | (xinvert << 8),\ +} + +static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = { + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */ + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0), +}; + + +/* + * DMX 6Fire specific controls + */ + +#define PCF9554_REG_INPUT 0 +#define PCF9554_REG_OUTPUT 1 +#define PCF9554_REG_POLARITY 2 +#define PCF9554_REG_CONFIG 3 + +static int snd_ice1712_6fire_read_pca(ice1712_t *ice, unsigned char reg) +{ + unsigned char byte; + snd_i2c_lock(ice->i2c); + byte = reg; + snd_i2c_sendbytes(ice->i2cdevs[0], &byte, 1); + byte = 0; + if (snd_i2c_readbytes(ice->i2cdevs[0], &byte, 1) != 1) { + snd_i2c_unlock(ice->i2c); + printk("cannot read pca\n"); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + return byte; +} + +static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char reg, unsigned char data) +{ + unsigned char bytes[2]; + snd_i2c_lock(ice->i2c); + bytes[0] = reg; + bytes[1] = data; + if (snd_i2c_sendbytes(ice->i2cdevs[0], bytes, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + snd_i2c_unlock(ice->i2c); + return 0; +} + +static int snd_ice1712_6fire_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int data; + + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) + return data; + data = (data >> shift) & 1; + if (invert) + data ^= 1; + ucontrol->value.integer.value[0] = data; + return 0; +} + +static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int data, ndata; + + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) + return data; + ndata = data & ~(1 << shift); + if (ucontrol->value.integer.value[0]) + ndata |= (1 << shift); + if (invert) + ndata ^= (1 << shift); + if (data != ndata) { + snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata); + return 1; + } + return 0; +} + +static int snd_ice1712_6fire_select_input_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[4] = { + "Internal", "Front Input", "Rear Input", "Wave Table" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_6fire_select_input_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int data; + + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) + return data; + ucontrol->value.integer.value[0] = data & 3; + return 0; +} + +static int snd_ice1712_6fire_select_input_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int data, ndata; + + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) + return data; + ndata = data & ~3; + ndata |= (ucontrol->value.integer.value[0] & 3); + if (data != ndata) { + snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata); + return 1; + } + return 0; +} + + +#define DMX6FIRE_CONTROL(xname, xshift, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname,\ + .info = snd_ice1712_6fire_control_info,\ + .get = snd_ice1712_6fire_control_get,\ + .put = snd_ice1712_6fire_control_put,\ + .private_value = xshift | (xinvert << 8),\ +} + +static snd_kcontrol_new_t snd_ice1712_6fire_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Select", + .info = snd_ice1712_6fire_select_input_info, + .get = snd_ice1712_6fire_select_input_get, + .put = snd_ice1712_6fire_select_input_put, + }, + DMX6FIRE_CONTROL("Front Digital Input Switch", 2, 0), + // DMX6FIRE_CONTROL("Master Clock Select", 3, 0), + DMX6FIRE_CONTROL("Optical Digital Input Switch", 4, 0), + DMX6FIRE_CONTROL("Phono Analog Input Switch", 5, 0), + DMX6FIRE_CONTROL("Breakbox LED", 6, 0), +}; + + +static int __devinit snd_ice1712_ews_add_controls(ice1712_t *ice) +{ + unsigned int idx; + int err; + snd_kcontrol_t *kctl; + + /* all terratec cards have spdif */ + err = snd_ice1712_spdif_build_controls(ice); + if (err < 0) + return err; + + /* ak4524 controls */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_DMX6FIRE: + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; + break; + } + + /* card specific controls */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_ewx2496_controls); idx++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx2496_controls[idx], ice)); + if (err < 0) + return err; + } + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88MT_NEW: + for (idx = 0; idx < 8; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice); + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88D: + for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_ews88d_controls); idx++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice)); + if (err < 0) + return err; + } + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_6fire_controls); idx++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_controls[idx], ice)); + if (err < 0) + return err; + } + break; + } + return 0; +} + + +/* entry point */ +struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { + { + ICE1712_SUBDEVICE_EWX2496, + "TerraTec EWX24/96", + snd_ice1712_ews_init, + snd_ice1712_ews_add_controls, + }, + { + ICE1712_SUBDEVICE_EWS88MT, + "TerraTec EWS88MT", + snd_ice1712_ews_init, + snd_ice1712_ews_add_controls, + }, + { + ICE1712_SUBDEVICE_EWS88MT_NEW, + "TerraTec EWS88MT", + snd_ice1712_ews_init, + snd_ice1712_ews_add_controls, + }, + { + ICE1712_SUBDEVICE_EWS88D, + "TerraTec EWS88D", + snd_ice1712_ews_init, + snd_ice1712_ews_add_controls, + }, + { + ICE1712_SUBDEVICE_DMX6FIRE, + "TerraTec DMX6Fire", + snd_ice1712_ews_init, + snd_ice1712_ews_add_controls, + }, + { } /* terminator */ +}; diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/ews.h linux/sound/pci/ice1712/ews.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/ews.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/ews.h 2003-03-05 05:07:28.000000000 -0700 @@ -0,0 +1,82 @@ +#ifndef __SOUND_EWS_H +#define __SOUND_EWS_H + +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire + * + * Copyright (c) 2000 Jaroslav Kysela + * 2002 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define EWS_DEVICE_DESC \ + "{TerraTec,EWX 24/96},"\ + "{TerraTec,EWS 88MT},"\ + "{TerraTec,EWS 88D},"\ + "{TerraTec,DMX 6Fire}," + +#define ICE1712_SUBDEVICE_EWX2496 0x3b153011 +#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511 +#define ICE1712_SUBDEVICE_EWS88MT_NEW 0x3b152511 +#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11 +#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811 + +/* entry point */ +extern struct snd_ice1712_card_info snd_ice1712_ews_cards[]; + + +/* TerraTec EWX 24/96 configuration definitions */ + +#define ICE1712_EWX2496_AK4524_CS 0x01 /* AK4524 chip select; low = active */ +#define ICE1712_EWX2496_AIN_SEL 0x02 /* input sensitivity switch; high = louder */ +#define ICE1712_EWX2496_AOUT_SEL 0x04 /* output sensitivity switch; high = louder */ +#define ICE1712_EWX2496_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_EWX2496_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_EWX2496_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_EWX2496_TX2 0x40 /* MIDI2 (not used) */ +#define ICE1712_EWX2496_RX2 0x80 /* MIDI2 (not used) */ + +/* TerraTec EWS 88MT/D configuration definitions */ +/* RW, SDA snd SCLK are identical with EWX24/96 */ +#define ICE1712_EWS88_CS8414_RATE 0x07 /* CS8414 sample rate: gpio 0-2 */ +#define ICE1712_EWS88_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_EWS88_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_EWS88_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_EWS88_TX2 0x40 /* MIDI2 (only on 88D) */ +#define ICE1712_EWS88_RX2 0x80 /* MIDI2 (only on 88D) */ + +/* i2c address */ +#define ICE1712_EWS88MT_CS8404_ADDR (0x40>>1) +#define ICE1712_EWS88MT_INPUT_ADDR (0x46>>1) +#define ICE1712_EWS88MT_OUTPUT_ADDR (0x48>>1) +#define ICE1712_EWS88MT_OUTPUT_SENSE 0x40 /* mask */ +#define ICE1712_EWS88D_PCF_ADDR (0x40>>1) + +/* TerraTec DMX 6Fire configuration definitions */ +#define ICE1712_6FIRE_AK4524_CS_MASK 0x07 /* AK4524 chip select #1-#3 */ +#define ICE1712_6FIRE_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_6FIRE_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_6FIRE_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_6FIRE_TX2 0x40 /* MIDI2 */ +#define ICE1712_6FIRE_RX2 0x80 /* MIDI2 */ + +#define ICE1712_6FIRE_PCF9554_ADDR (0x40>>1) +#define ICE1712_6FIRE_CS8427_ADDR (0x22) + +#endif /* __SOUND_EWS_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/hoontech.c linux/sound/pci/ice1712/hoontech.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/hoontech.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/hoontech.c 2002-12-09 04:35:23.000000000 -0700 @@ -0,0 +1,228 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for Hoontech STDSP24 + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "hoontech.h" + + +static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte) +{ + byte |= ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); + byte &= ~ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); + byte |= ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); +} + +static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate) +{ + down(&ice->gpio_mutex); + + /* select box */ + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + + /* prepare for write */ + if (chn == 3) + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + udelay(100); + if (chn == 3) { + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + } else { + switch (chn) { + case 0: ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break; + case 1: ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break; + case 2: ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break; + } + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + } + udelay(100); + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + udelay(100); + + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave) +{ + down(&ice->gpio_mutex); + + /* select box */ + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + /* MIDI2 is direct */ + ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + + up(&ice->gpio_mutex); +} + +static int __devinit snd_ice1712_hoontech_init(ice1712_t *ice) +{ + int box, chn; + + ice->num_total_dacs = 8; + + ice->hoontech_boxbits[0] = + ice->hoontech_boxbits[1] = + ice->hoontech_boxbits[2] = + ice->hoontech_boxbits[3] = 0; /* should be already */ + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1); + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1); + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1); + ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0); + + /* let's go - activate only functions in first box */ + ice->hoontech_config = 0; + /* ICE1712_STDSP24_MUTE | + ICE1712_STDSP24_INSEL | + ICE1712_STDSP24_DAREAR; */ + ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | + ICE1712_STDSP24_BOX_CHN2 | + ICE1712_STDSP24_BOX_CHN3 | + ICE1712_STDSP24_BOX_CHN4 | + ICE1712_STDSP24_BOX_MIDI1 | + ICE1712_STDSP24_BOX_MIDI2; + ice->hoontech_boxconfig[1] = + ice->hoontech_boxconfig[2] = + ice->hoontech_boxconfig[3] = 0; + snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0); + snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0); + snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0); + for (box = 0; box < 4; box++) { + for (chn = 0; chn < 4; chn++) + snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0); + snd_ice1712_stdsp24_box_midi(ice, box, + (ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0, + (ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0); + } + + return 0; +} + + +/* entry point */ +struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { + { + ICE1712_SUBDEVICE_STDSP24, + "Hoontech SoundTrack Audio DSP24", + snd_ice1712_hoontech_init, + }, + { + ICE1712_SUBDEVICE_STDSP24_MEDIA7_1, + "Hoontech STA DSP24 Media 7.1", + snd_ice1712_hoontech_init, + }, + { } /* terminator */ +}; + diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/hoontech.h linux/sound/pci/ice1712/hoontech.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/hoontech.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/hoontech.h 2002-12-16 07:23:48.000000000 -0700 @@ -0,0 +1,68 @@ +#ifndef __SOUND_HOONTECH_H +#define __SOUND_HOONTECH_H + +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for Hoontech STDSP24 + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define HOONTECH_DEVICE_DESC \ + "{Hoontech SoundTrack DSP 24}," \ + "{Hoontech SoundTrack DSP 24 Value}," \ + "{Hoontech SoundTrack DSP 24 Media 7.1}," \ + +#define ICE1712_SUBDEVICE_STDSP24 0x12141217 /* Hoontech SoundTrack Audio DSP 24 */ +#define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */ + +extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[]; + + +/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */ + +#define ICE1712_STDSP24_0_BOX(r, x) r[0] = ((r[0] & ~3) | ((x)&3)) +#define ICE1712_STDSP24_0_DAREAR(r, x) r[0] = ((r[0] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_1_CHN1(r, x) r[1] = ((r[1] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_1_CHN2(r, x) r[1] = ((r[1] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_1_CHN3(r, x) r[1] = ((r[1] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_2_CHN4(r, x) r[2] = ((r[2] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_2_MIDIIN(r, x) r[2] = ((r[2] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_2_MIDI1(r, x) r[2] = ((r[2] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_3_MIDI2(r, x) r[3] = ((r[3] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_3_MUTE(r, x) r[3] = ((r[3] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_3_INSEL(r, x) r[3] = ((r[3] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_SET_ADDR(r, a) r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3)) +#define ICE1712_STDSP24_CLOCK(r, a, c) r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5)) +#define ICE1712_STDSP24_CLOCK_BIT (1<<5) + +/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */ + +#define ICE1712_STDSP24_DAREAR (1<<0) +#define ICE1712_STDSP24_MUTE (1<<1) +#define ICE1712_STDSP24_INSEL (1<<2) + +#define ICE1712_STDSP24_BOX_CHN1 (1<<0) /* input channel 1 */ +#define ICE1712_STDSP24_BOX_CHN2 (1<<1) /* input channel 2 */ +#define ICE1712_STDSP24_BOX_CHN3 (1<<2) /* input channel 3 */ +#define ICE1712_STDSP24_BOX_CHN4 (1<<3) /* input channel 4 */ +#define ICE1712_STDSP24_BOX_MIDI1 (1<<8) +#define ICE1712_STDSP24_BOX_MIDI2 (1<<9) + +#endif /* __SOUND_HOONTECH_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/ice1712.c linux/sound/pci/ice1712/ice1712.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/ice1712.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/ice1712.c 2003-03-16 03:53:14.000000000 -0700 @@ -0,0 +1,2621 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - spdif nonaudio consumer mode does not work (at least with my + Sony STR-DB830) +*/ + +/* + * Changes: + * + * 2002.09.09 Takashi Iwai + * split the code to several files. each low-level routine + * is stored in the local file and called from registration + * function from card_info struct. + * + * 2002.11.26 James Stafford + * Added support for VT1724 (Envy24HT) + * I have left out support for 176.4 and 192 KHz for the moment. + * I also haven't done anything with the internal S/PDIF transmitter or the MPU-401 + * + * 2003.02.20 Taksahi Iwai + * Split vt1724 part to an independent driver. + * The GPIO is accessed through the callback functions now. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#include "ice1712.h" + +/* lowlevel routines */ +#include "delta.h" +#include "ews.h" +#include "hoontech.h" + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{" + HOONTECH_DEVICE_DESC + DELTA_DEVICE_DESC + EWS_DEVICE_DESC + "{ICEnsemble,Generic ICE1712}," + "{ICEnsemble,Generic Envy24}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int omni[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; /* Delta44 & 66 Omni I/O support */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ICE1712 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ICE1712 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ICE1712 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(omni, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support."); +MODULE_PARM_SYNTAX(omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); + +#ifndef PCI_VENDOR_ID_ICE +#define PCI_VENDOR_ID_ICE 0x1412 +#endif +#ifndef PCI_DEVICE_ID_ICE_1712 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#endif + +static struct pci_device_id snd_ice1712_ids[] __devinitdata = { + { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_ice1712_ids); + +static int snd_ice1712_build_pro_mixer(ice1712_t *ice); +static int snd_ice1712_build_controls(ice1712_t *ice); + +static int PRO_RATE_LOCKED = 0; +static int PRO_RATE_RESET = 1; +static unsigned int PRO_RATE_DEFAULT = 44100; + +/* + * Basic I/O + */ + +/* check whether the clock mode is spdif-in */ +static inline int is_spdif_master(ice1712_t *ice) +{ + return (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) ? 1 : 0; +} + +static inline int is_pro_rate_locked(ice1712_t *ice) +{ + return is_spdif_master(ice) || PRO_RATE_LOCKED; +} + +static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data) +{ + outb((channel << 4) | addr, ICEDS(ice, INDEX)); + outl(data, ICEDS(ice, DATA)); +} + +static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr) +{ + outb((channel << 4) | addr, ICEDS(ice, INDEX)); + return inl(ICEDS(ice, DATA)); +} + +static void snd_ice1712_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEREG(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEREG(ice, AC97_INDEX)); + outw(val, ICEREG(ice, AC97_DATA)); + old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); + outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) + break; +} + +static unsigned short snd_ice1712_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEREG(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEREG(ice, AC97_INDEX)); + outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) + break; + if (tm >= 0x10000) /* timeout */ + return ~0; + return inw(ICEREG(ice, AC97_DATA)); +} + +/* + * pro ac97 section + */ + +static void snd_ice1712_pro_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEMT(ice, AC97_INDEX)); + outw(val, ICEMT(ice, AC97_DATA)); + old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); + outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) + break; +} + + +static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEMT(ice, AC97_INDEX)); + outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) + break; + if (tm >= 0x10000) /* timeout */ + return ~0; + return inw(ICEMT(ice, AC97_DATA)); +} + +/* + * consumer ac97 digital mix + */ +static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0; + return 0; +} + +static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char val, nval; + + spin_lock_irq(&ice->reg_lock); + val = inb(ICEMT(ice, MONITOR_ROUTECTRL)); + nval = val & ~ICE1712_ROUTE_AC97; + if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97; + outb(nval, ICEMT(ice, MONITOR_ROUTECTRL)); + spin_unlock_irq(&ice->reg_lock); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Mixer To AC97", + .info = snd_ice1712_digmix_route_ac97_info, + .get = snd_ice1712_digmix_route_ac97_get, + .put = snd_ice1712_digmix_route_ac97_put, +}; + + +/* + * gpio operations + */ +static void snd_ice1712_set_gpio_dir(ice1712_t *ice, unsigned int data) +{ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, data); +} + +static void snd_ice1712_set_gpio_mask(ice1712_t *ice, unsigned int data) +{ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data); +} + +static unsigned int snd_ice1712_get_gpio_data(ice1712_t *ice) +{ + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); +} + +static void snd_ice1712_set_gpio_data(ice1712_t *ice, unsigned int val) +{ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, val); +} + + +/* + * + * CS8427 interface + * + */ + +/* + * change the input clock selection + * spdif_clock = 1 - IEC958 input, 0 - Envy24 + */ +static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock) +{ + unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */ + unsigned char val, nval; + int res = 0; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EIO; + } + nval = val & 0xf0; + if (spdif_clock) + nval |= 0x01; + else + nval |= 0x04; + if (val != nval) { + reg[1] = nval; + if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) { + res = -EIO; + } else { + res++; + } + } + snd_i2c_unlock(ice->i2c); + return res; +} + +/* + * spdif callbacks + */ +static void open_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream) +{ + snd_cs8427_iec958_active(ice->cs8427, 1); +} + +static void close_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream) +{ + snd_cs8427_iec958_active(ice->cs8427, 0); +} + +static void setup_cs8427(ice1712_t *ice, int rate) +{ + snd_cs8427_iec958_pcm(ice->cs8427, rate); +} + +/* + * create and initialize callbacks for cs8427 interface + */ +int __devinit snd_ice1712_init_cs8427(ice1712_t *ice, int addr) +{ + int err; + + if ((err = snd_cs8427_create(ice->i2c, addr, &ice->cs8427)) < 0) { + snd_printk("CS8427 initialization failed\n"); + return err; + } + ice->spdif.ops.open = open_cs8427; + ice->spdif.ops.close = close_cs8427; + ice->spdif.ops.setup_rate = setup_cs8427; + return 0; +} + + +/* + * Interrupt handler + */ + +static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return); + unsigned char status; + + while (1) { + status = inb(ICEREG(ice, IRQSTAT)); + if (status == 0) + break; + if (status & ICE1712_IRQ_MPU1) { + if (ice->rmidi[0]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); + outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT)); + status &= ~ICE1712_IRQ_MPU1; + } + if (status & ICE1712_IRQ_TIMER) + outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT)); + if (status & ICE1712_IRQ_MPU2) { + if (ice->rmidi[1]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs); + outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT)); + status &= ~ICE1712_IRQ_MPU2; + } + if (status & ICE1712_IRQ_PROPCM) { + unsigned char mtstat = inb(ICEMT(ice, IRQ)); + if (mtstat & ICE1712_MULTI_PBKSTATUS) { + if (ice->playback_pro_substream) + snd_pcm_period_elapsed(ice->playback_pro_substream); + outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ)); + } + if (mtstat & ICE1712_MULTI_CAPSTATUS) { + if (ice->capture_pro_substream) + snd_pcm_period_elapsed(ice->capture_pro_substream); + outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ)); + } + } + if (status & ICE1712_IRQ_FM) + outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT)); + if (status & ICE1712_IRQ_PBKDS) { + u32 idx; + u16 pbkstatus; + snd_pcm_substream_t *substream; + pbkstatus = inw(ICEDS(ice, INTSTAT)); + //printk("pbkstatus = 0x%x\n", pbkstatus); + for (idx = 0; idx < 6; idx++) { + if ((pbkstatus & (3 << (idx * 2))) == 0) + continue; + if ((substream = ice->playback_con_substream_ds[idx]) != NULL) + snd_pcm_period_elapsed(substream); + outw(3 << (idx * 2), ICEDS(ice, INTSTAT)); + } + outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT)); + } + if (status & ICE1712_IRQ_CONCAP) { + if (ice->capture_con_substream) + snd_pcm_period_elapsed(ice->capture_con_substream); + outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT)); + } + if (status & ICE1712_IRQ_CONPBK) { + if (ice->playback_con_substream) + snd_pcm_period_elapsed(ice->playback_con_substream); + outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT)); + } + } +} + + +/* + * PCM part - misc + */ + +static int snd_ice1712_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ice1712_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* + * PCM part - consumer I/O + */ + +static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u32 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) { + tmp |= 2; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) { + tmp &= ~2; + } else { + result = -EINVAL; + } + snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u32 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) { + tmp |= 2; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) { + tmp &= ~2; + } else { + result = -EINVAL; + } + snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u8 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else { + result = -EINVAL; + } + snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size, rate, tmp; + + period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x0000; + if (snd_pcm_format_width(runtime->format) == 16) + tmp |= 0x10; + if (runtime->channels == 2) + tmp |= 0x08; + rate = (runtime->rate * 8192) / 375; + if (rate > 0x000fffff) + rate = 0x000fffff; + spin_lock(&ice->reg_lock); + outb(0, ice->ddma_port + 15); + outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b); + outl(runtime->dma_addr, ice->ddma_port + 0); + outw(buf_size, ice->ddma_port + 4); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp); + snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8); + snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0); + spin_unlock(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size, rate, tmp, chn; + + period_size = snd_pcm_lib_period_bytes(substream) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x0064; + if (snd_pcm_format_width(runtime->format) == 16) + tmp &= ~0x04; + if (runtime->channels == 2) + tmp |= 0x08; + rate = (runtime->rate * 8192) / 375; + if (rate > 0x000fffff) + rate = 0x000fffff; + ice->playback_con_active_buf[substream->number] = 0; + ice->playback_con_virt_addr[substream->number] = runtime->dma_addr; + chn = substream->number * 2; + spin_lock(&ice->reg_lock); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0)); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp); + if (runtime->channels == 2) { + snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate); + snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0); + } + spin_unlock(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size; + u8 tmp; + + period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x06; + if (snd_pcm_format_width(runtime->format) == 16) + tmp &= ~0x04; + if (runtime->channels == 2) + tmp &= ~0x02; + spin_lock(&ice->reg_lock); + outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR)); + outw(buf_size, ICEREG(ice, CONCAP_COUNT)); + snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8); + snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); + spin_unlock(&ice->reg_lock); + snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + return 0; +} + +static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + size_t ptr; + + if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1)) + return 0; + ptr = runtime->buffer_size - inw(ice->ddma_port + 4); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + u8 addr; + size_t ptr; + + if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1)) + return 0; + if (ice->playback_con_active_buf[substream->number]) + addr = ICE1712_DSC_ADDR1; + else + addr = ICE1712_DSC_ADDR0; + ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) - + ice->playback_con_virt_addr[substream->number]; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1)) + return 0; + ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_ice1712_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (64*1024), + .period_bytes_min = 64, + .period_bytes_max = (64*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ice1712_playback_ds = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ice1712_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (64*1024), + .period_bytes_min = 64, + .period_bytes_max = (64*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ice1712_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_con_substream = substream; + runtime->hw = snd_ice1712_playback; + return 0; +} + +static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + u32 tmp; + + ice->playback_con_substream_ds[substream->number] = substream; + runtime->hw = snd_ice1712_playback_ds; + spin_lock_irq(&ice->reg_lock); + tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2)); + outw(tmp, ICEDS(ice, INTMASK)); + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_con_substream = substream; + runtime->hw = snd_ice1712_capture; + runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC]; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + return 0; +} + +static int snd_ice1712_playback_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_con_substream = NULL; + return 0; +} + +static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + u32 tmp; + + spin_lock_irq(&ice->reg_lock); + tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2)); + outw(tmp, ICEDS(ice, INTMASK)); + spin_unlock_irq(&ice->reg_lock); + ice->playback_con_substream_ds[substream->number] = NULL; + return 0; +} + +static int snd_ice1712_capture_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_con_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_ice1712_playback_ops = { + .open = snd_ice1712_playback_open, + .close = snd_ice1712_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ice1712_hw_params, + .hw_free = snd_ice1712_hw_free, + .prepare = snd_ice1712_playback_prepare, + .trigger = snd_ice1712_playback_trigger, + .pointer = snd_ice1712_playback_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_playback_ds_ops = { + .open = snd_ice1712_playback_ds_open, + .close = snd_ice1712_playback_ds_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ice1712_hw_params, + .hw_free = snd_ice1712_hw_free, + .prepare = snd_ice1712_playback_ds_prepare, + .trigger = snd_ice1712_playback_ds_trigger, + .pointer = snd_ice1712_playback_ds_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_capture_ops = { + .open = snd_ice1712_capture_open, + .close = snd_ice1712_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ice1712_hw_params, + .hw_free = snd_ice1712_hw_free, + .prepare = snd_ice1712_capture_prepare, + .trigger = snd_ice1712_capture_trigger, + .pointer = snd_ice1712_capture_pointer, +}; + +static void snd_ice1712_pcm_free(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 consumer"); + ice->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + + printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n"); + + return 0; +} + +static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm_ds = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_free_ds; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 consumer (DS)"); + ice->pcm_ds = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +/* + * PCM code - professional part (multitrack) + */ + +static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000 }; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = RATES, + .list = rates, + .mask = 0, +}; + +static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what; + unsigned int old; + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + what = ICE1712_PLAYBACK_PAUSE; + snd_pcm_trigger_done(substream, substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT(ice, PLAYBACK_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + old |= what; + else + old &= ~what; + outl(old, ICEMT(ice, PLAYBACK_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + unsigned int old; + snd_pcm_substream_t *s = substream; + + do { + if (s == ice->playback_pro_substream) { + what |= ICE1712_PLAYBACK_START; + snd_pcm_trigger_done(s, substream); + } else if (s == ice->capture_pro_substream) { + what |= ICE1712_CAPTURE_START_SHADOW; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT(ice, PLAYBACK_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_START) + old |= what; + else + old &= ~what; + outl(old, ICEMT(ice, PLAYBACK_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +/* + */ +static void snd_ice1712_set_pro_rate(ice1712_t *ice, unsigned int rate, int force) +{ + unsigned long flags; + unsigned char val; + unsigned int i; + + spin_lock_irqsave(&ice->reg_lock, flags); + if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW| + ICE1712_PLAYBACK_PAUSE| + ICE1712_PLAYBACK_START)) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + return; + } + if (!force && is_pro_rate_locked(ice)) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + return; + } + + switch (rate) { + case 8000: val = 6; break; + case 9600: val = 3; break; + case 11025: val = 10; break; + case 12000: val = 2; break; + case 16000: val = 5; break; + case 22050: val = 9; break; + case 24000: val = 1; break; + case 32000: val = 4; break; + case 44100: val = 8; break; + case 48000: val = 0; break; + case 64000: val = 15; break; + case 88200: val = 11; break; + case 96000: val = 7; break; + default: + snd_BUG(); + val = 0; + break; + } + outb(val, ICEMT(ice, RATE)); + + spin_unlock_irqrestore(&ice->reg_lock, flags); + + for (i = 0; i < ice->akm_codecs; i++) { + if (ice->akm[i].ops.set_rate_val) + ice->akm[i].ops.set_rate_val(&ice->akm[i], rate); + } +} + +static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream); + spin_lock(&ice->reg_lock); + outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR)); + outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE)); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT)); + spin_unlock(&ice->reg_lock); + + return 0; +} + +static int snd_ice1712_playback_pro_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0); + if (ice->spdif.ops.setup_rate) + ice->spdif.ops.setup_rate(ice, params_rate(hw_params)); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream); + spin_lock(&ice->reg_lock); + outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR)); + outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE)); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT)); + spin_unlock(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_capture_pro_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START)) + return 0; + ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW)) + return 0; + ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_ice1712_playback_pro = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + .rate_min = 4000, + .rate_max = 96000, + .channels_min = 10, + .channels_max = 10, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 10 * 4 * 2, + .period_bytes_max = 131040, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ice1712_capture_pro = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + .rate_min = 4000, + .rate_max = 96000, + .channels_min = 12, + .channels_max = 12, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 12 * 4 * 2, + .period_bytes_max = 131040, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_substream = substream; + runtime->hw = snd_ice1712_playback_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + + if (ice->spdif.ops.open) + ice->spdif.ops.open(ice, substream); + + return 0; +} + +static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ice->capture_pro_substream = substream; + runtime->hw = snd_ice1712_capture_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->playback_pro_substream = NULL; + if (ice->spdif.ops.close) + ice->spdif.ops.close(ice, substream); + + return 0; +} + +static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->capture_pro_substream = NULL; + return 0; +} + +static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm_pro = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static snd_pcm_ops_t snd_ice1712_playback_pro_ops = { + .open = snd_ice1712_playback_pro_open, + .close = snd_ice1712_playback_pro_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ice1712_playback_pro_hw_params, + .hw_free = snd_ice1712_hw_free, + .prepare = snd_ice1712_playback_pro_prepare, + .trigger = snd_ice1712_pro_trigger, + .pointer = snd_ice1712_playback_pro_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_capture_pro_ops = { + .open = snd_ice1712_capture_pro_open, + .close = snd_ice1712_capture_pro_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ice1712_capture_pro_hw_params, + .hw_free = snd_ice1712_hw_free, + .prepare = snd_ice1712_capture_pro_prepare, + .trigger = snd_ice1712_pro_trigger, + .pointer = snd_ice1712_capture_pro_pointer, +}; + +static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_profi_free; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 multi"); + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + + ice->pcm_pro = pcm; + if (rpcm) + *rpcm = pcm; + + if (ice->cs8427) { + /* assign channels to iec958 */ + err = snd_cs8427_iec958_build(ice->cs8427, + pcm->streams[0].substream, + pcm->streams[1].substream); + if (err < 0) + return err; + } + + if ((err = snd_ice1712_build_pro_mixer(ice)) < 0) + return err; + return 0; +} + +/* + * Mixer section + */ + +static void snd_ice1712_update_volume(ice1712_t *ice, int index) +{ + unsigned int vol = ice->pro_volumes[index]; + unsigned short val = 0; + + val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f; + val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8; + outb(index, ICEMT(ice, MONITOR_INDEX)); + outw(val, ICEMT(ice, MONITOR_VOLUME)); +} + +static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + + spin_lock_irq(&ice->reg_lock); + ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1); + ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1); + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + unsigned int nval, change; + + nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) | + (ucontrol->value.integer.value[1] ? 0 : 0x80000000); + spin_lock_irq(&ice->reg_lock); + nval |= ice->pro_volumes[index] & ~0x80008000; + change = nval != ice->pro_volumes[index]; + ice->pro_volumes[index] = nval; + snd_ice1712_update_volume(ice, index); + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 96; + return 0; +} + +static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + + spin_lock_irq(&ice->reg_lock); + ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127; + ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127; + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + unsigned int nval, change; + + nval = (ucontrol->value.integer.value[0] & 127) | + ((ucontrol->value.integer.value[1] & 127) << 16); + spin_lock_irq(&ice->reg_lock); + nval |= ice->pro_volumes[index] & ~0x007f007f; + change = nval != ice->pro_volumes[index]; + ice->pro_volumes[index] = nval; + snd_ice1712_update_volume(ice, index); + spin_unlock_irq(&ice->reg_lock); + return change; +} + + +static int __devinit snd_ice1712_build_pro_mixer(ice1712_t *ice) +{ + snd_card_t * card = ice->card; + snd_kcontrol_t ctl; + int idx, err; + + /* PCM playback */ + for (idx = 0; idx < 10; idx++) { + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Playback Switch"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_switch_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_switch_get; + ctl.put = snd_ice1712_pro_mixer_switch_put; + ctl.private_value = idx; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Playback Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_volume_get; + ctl.put = snd_ice1712_pro_mixer_volume_put; + ctl.private_value = idx; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + } + + /* PCM capture */ + for (idx = 0; idx < 10; idx++) { + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Capture Switch"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_switch_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_switch_get; + ctl.put = snd_ice1712_pro_mixer_switch_put; + ctl.private_value = idx + 10; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Capture Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_volume_get; + ctl.put = snd_ice1712_pro_mixer_volume_put; + ctl.private_value = idx + 10; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + } + + /* initialize volumes */ + for (idx = 0; idx < 20; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + return 0; +} + +static void snd_ice1712_mixer_free_ac97(ac97_t *ac97) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); + ice->ac97 = NULL; +} + +static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) +{ + int err; + + if (ice_has_con_ac97(ice)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ice1712_ac97_write; + ac97.read = snd_ice1712_ac97_read; + ac97.private_data = ice; + ac97.private_free = snd_ice1712_mixer_free_ac97; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) + printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n"); + else { + if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0) + return err; + return 0; + } + } + + if (! (ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ice1712_pro_ac97_write; + ac97.read = snd_ice1712_pro_ac97_read; + ac97.private_data = ice; + ac97.private_free = snd_ice1712_mixer_free_ac97; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) + printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n"); + else + return 0; + } + /* I2S mixer only */ + strcat(ice->card->mixername, "ICE1712 - multitrack"); + return 0; +} + +/* + * + */ + +static inline unsigned int eeprom_double(ice1712_t *ice, int idx) +{ + return (unsigned int)ice->eeprom.data[idx] | ((unsigned int)ice->eeprom.data[idx + 1] << 8); +} + +static void snd_ice1712_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return); + unsigned int idx; + + snd_iprintf(buffer, "%s\n\n", ice->card->longname); + snd_iprintf(buffer, "EEPROM:\n"); + + snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor); + snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size); + snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version); + snd_iprintf(buffer, " Codec : 0x%x\n", ice->eeprom.data[ICE_EEP1_CODEC]); + snd_iprintf(buffer, " ACLink : 0x%x\n", ice->eeprom.data[ICE_EEP1_ACLINK]); + snd_iprintf(buffer, " I2S ID : 0x%x\n", ice->eeprom.data[ICE_EEP1_I2SID]); + snd_iprintf(buffer, " S/PDIF : 0x%x\n", ice->eeprom.data[ICE_EEP1_SPDIF]); + snd_iprintf(buffer, " GPIO mask : 0x%x\n", ice->eeprom.gpiomask); + snd_iprintf(buffer, " GPIO state : 0x%x\n", ice->eeprom.gpiostate); + snd_iprintf(buffer, " GPIO direction : 0x%x\n", ice->eeprom.gpiodir); + snd_iprintf(buffer, " AC'97 main : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_MAIN_LO)); + snd_iprintf(buffer, " AC'97 pcm : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_PCM_LO)); + snd_iprintf(buffer, " AC'97 record : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_REC_LO)); + snd_iprintf(buffer, " AC'97 record src : 0x%x\n", ice->eeprom.data[ICE_EEP1_AC97_RECSRC]); + for (idx = 0; idx < 4; idx++) + snd_iprintf(buffer, " DAC ID #%i : 0x%x\n", idx, ice->eeprom.data[ICE_EEP1_DAC_ID + idx]); + for (idx = 0; idx < 4; idx++) + snd_iprintf(buffer, " ADC ID #%i : 0x%x\n", idx, ice->eeprom.data[ICE_EEP1_ADC_ID + idx]); + for (idx = 0x1c; idx < ice->eeprom.size; idx++) + snd_iprintf(buffer, " Extra #%02i : 0x%x\n", idx, ice->eeprom.data[idx]); + + snd_iprintf(buffer, "\nRegisters:\n"); + snd_iprintf(buffer, " PSDOUT03 : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_PSDOUT03))); + snd_iprintf(buffer, " CAPTURE : 0x%08x\n", inl(ICEMT(ice, ROUTE_CAPTURE))); + snd_iprintf(buffer, " SPDOUT : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT))); + snd_iprintf(buffer, " RATE : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE))); +} + +static void __devinit snd_ice1712_proc_init(ice1712_t * ice) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(ice->card, "ice1712", &entry)) + snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read); +} + +/* + * + */ + +static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(ice1712_eeprom_t); + return 0; +} + +static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom)); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "ICE1712 EEPROM", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ice1712_eeprom_info, + .get = snd_ice1712_eeprom_get +}; + +/* + */ +static int snd_ice1712_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + if (ice->spdif.ops.default_get) + ice->spdif.ops.default_get(ice, ucontrol); + return 0; +} + +static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + if (ice->spdif.ops.default_put) + return ice->spdif.ops.default_put(ice, ucontrol); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_ice1712_spdif_info, + .get = snd_ice1712_spdif_default_get, + .put = snd_ice1712_spdif_default_put +}; + +static int snd_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + if (ice->spdif.ops.default_get) { + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_NOT_COPYRIGHT | + IEC958_AES0_CON_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL | + IEC958_AES1_CON_CATEGORY; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + } else { + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + } + return 0; +} + +static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + if (ice->spdif.ops.default_get) { + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE; + } else { + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + } + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_ice1712_spdif_info, + .get = snd_ice1712_spdif_maskc_get, +}; + +static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_ice1712_spdif_info, + .get = snd_ice1712_spdif_maskp_get, +}; + +static int snd_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + if (ice->spdif.ops.stream_get) + ice->spdif.ops.stream_get(ice, ucontrol); + return 0; +} + +static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + if (ice->spdif.ops.stream_put) + return ice->spdif.ops.stream_put(ice, ucontrol); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_ice1712_spdif_info, + .get = snd_ice1712_spdif_stream_get, + .put = snd_ice1712_spdif_stream_put +}; + +int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0; + + snd_ice1712_save_gpio_status(ice); + ucontrol->value.integer.value[0] = (snd_ice1712_gpio_read(ice) & mask ? 1 : 0) ^ invert; + snd_ice1712_restore_gpio_status(ice); + return 0; +} + +int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? mask : 0; + unsigned int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert; + snd_ice1712_save_gpio_status(ice); + val = snd_ice1712_gpio_read(ice); + nval |= val & ~mask; + if (val != nval) + snd_ice1712_gpio_write(ice, nval); + snd_ice1712_restore_gpio_status(ice); + return val != nval; +} + +/* + * rate + */ +static int snd_ice1712_pro_internal_clock_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { + "8000", /* 0: 6 */ + "9600", /* 1: 3 */ + "11025", /* 2: 10 */ + "12000", /* 3: 2 */ + "16000", /* 4: 5 */ + "22050", /* 5: 9 */ + "24000", /* 6: 1 */ + "32000", /* 7: 4 */ + "44100", /* 8: 8 */ + "48000", /* 9: 0 */ + "64000", /* 10: 15 */ + "88200", /* 11: 11 */ + "96000", /* 12: 7 */ + "IEC958 Input", /* 13: -- */ + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 14; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_pro_internal_clock_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + static unsigned char xlate[16] = { + 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10 + }; + unsigned char val; + + spin_lock_irq(&ice->reg_lock); + if (is_spdif_master(ice)) { + ucontrol->value.enumerated.item[0] = 13; + } else { + val = xlate[inb(ICEMT(ice, RATE)) & 15]; + if (val == 255) { + snd_BUG(); + val = 0; + } + ucontrol->value.enumerated.item[0] = val; + } + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static int snd_ice1712_pro_internal_clock_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + static unsigned int xrate[13] = { + 8000, 9600, 11025, 12000, 1600, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000 + }; + unsigned char oval; + int change = 0; + + spin_lock_irq(&ice->reg_lock); + oval = inb(ICEMT(ice, RATE)); + if (ucontrol->value.enumerated.item[0] == 13) { + outb(oval | ICE1712_SPDIF_MASTER, ICEMT(ice, RATE)); + } else { + PRO_RATE_DEFAULT = xrate[ucontrol->value.integer.value[0] % 13]; + spin_unlock_irq(&ice->reg_lock); + snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 1); + spin_lock_irq(&ice->reg_lock); + } + change = inb(ICEMT(ice, RATE)) != oval; + spin_unlock_irq(&ice->reg_lock); + + if ((oval & ICE1712_SPDIF_MASTER) != (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)) { + /* change CS8427 clock source too */ + if (ice->cs8427) { + snd_ice1712_cs8427_set_input_clock(ice, is_spdif_master(ice)); + } + /* notify ak4524 chip as well */ + if (is_spdif_master(ice)) { + unsigned int i; + for (i = 0; i < ice->akm_codecs; i++) { + if (ice->akm[i].ops.set_rate_val) + ice->akm[i].ops.set_rate_val(&ice->akm[i], 0); + } + } + } + + return change; +} + +static snd_kcontrol_new_t snd_ice1712_pro_internal_clock __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Internal Clock", + .info = snd_ice1712_pro_internal_clock_info, + .get = snd_ice1712_pro_internal_clock_get, + .put = snd_ice1712_pro_internal_clock_put +}; + +static int snd_ice1712_pro_rate_locking_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_rate_locking_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.integer.value[0] = PRO_RATE_LOCKED; + return 0; +} + +static int snd_ice1712_pro_rate_locking_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change = 0, nval; + + nval = ucontrol->value.integer.value[0] ? 1 : 0; + spin_lock_irq(&ice->reg_lock); + change = PRO_RATE_LOCKED != nval; + PRO_RATE_LOCKED = nval; + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_pro_rate_locking __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Rate Locking", + .info = snd_ice1712_pro_rate_locking_info, + .get = snd_ice1712_pro_rate_locking_get, + .put = snd_ice1712_pro_rate_locking_put +}; + +static int snd_ice1712_pro_rate_reset_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_rate_reset_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.integer.value[0] = PRO_RATE_RESET; + return 0; +} + +static int snd_ice1712_pro_rate_reset_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change = 0, nval; + + nval = ucontrol->value.integer.value[0] ? 1 : 0; + spin_lock_irq(&ice->reg_lock); + change = PRO_RATE_RESET != nval; + PRO_RATE_RESET = nval; + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_pro_rate_reset __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Rate Reset", + .info = snd_ice1712_pro_rate_reset_info, + .get = snd_ice1712_pro_rate_reset_get, + .put = snd_ice1712_pro_rate_reset_put +}; + +/* + * routing + */ +static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { + "PCM Out", /* 0 */ + "H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */ + "H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */ + "IEC958 In L", "IEC958 In R", /* 9-10 */ + "Digital Mixer", /* 11 - optional */ + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + unsigned int val, cval; + + spin_lock_irq(&ice->reg_lock); + val = inw(ICEMT(ice, ROUTE_PSDOUT03)); + cval = inl(ICEMT(ice, ROUTE_CAPTURE)); + spin_unlock_irq(&ice->reg_lock); + + val >>= ((idx % 2) * 8) + ((idx / 2) * 2); + val &= 3; + cval >>= ((idx / 2) * 8) + ((idx % 2) * 4); + if (val == 1 && idx < 2) + ucontrol->value.enumerated.item[0] = 11; + else if (val == 2) + ucontrol->value.enumerated.item[0] = (cval & 7) + 1; + else if (val == 3) + ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change, shift; + int idx = kcontrol->id.index; + unsigned int val, old_val, nval; + + /* update PSDOUT */ + if (ucontrol->value.enumerated.item[0] >= 11) + nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */ + else if (ucontrol->value.enumerated.item[0] >= 9) + nval = 3; /* spdif in */ + else if (ucontrol->value.enumerated.item[0] >= 1) + nval = 2; /* analog in */ + else + nval = 0; /* pcm */ + shift = ((idx % 2) * 8) + ((idx / 2) * 2); + spin_lock_irq(&ice->reg_lock); + val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03)); + val &= ~(0x03 << shift); + val |= nval << shift; + change = val != old_val; + if (change) + outw(val, ICEMT(ice, ROUTE_PSDOUT03)); + spin_unlock_irq(&ice->reg_lock); + if (nval < 2) /* dig mixer of pcm */ + return change; + + /* update CAPTURE */ + spin_lock_irq(&ice->reg_lock); + val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE)); + shift = ((idx / 2) * 8) + ((idx % 2) * 4); + if (nval == 2) { /* analog in */ + nval = ucontrol->value.enumerated.item[0] - 1; + val &= ~(0x07 << shift); + val |= nval << shift; + } else { /* spdif in */ + nval = (ucontrol->value.enumerated.item[0] - 9) << 3; + val &= ~(0x08 << shift); + val |= nval << shift; + } + if (val != old_val) { + change = 1; + outl(val, ICEMT(ice, ROUTE_CAPTURE)); + } + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + unsigned int val, cval; + val = inw(ICEMT(ice, ROUTE_SPDOUT)); + cval = (val >> (idx * 4 + 8)) & 0x0f; + val = (val >> (idx * 2)) & 0x03; + if (val == 1) + ucontrol->value.enumerated.item[0] = 11; + else if (val == 2) + ucontrol->value.enumerated.item[0] = (cval & 7) + 1; + else if (val == 3) + ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change, shift; + int idx = kcontrol->id.index; + unsigned int val, old_val, nval; + + /* update SPDOUT */ + spin_lock_irq(&ice->reg_lock); + val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT)); + if (ucontrol->value.enumerated.item[0] >= 11) + nval = 1; + else if (ucontrol->value.enumerated.item[0] >= 9) + nval = 3; + else if (ucontrol->value.enumerated.item[0] >= 1) + nval = 2; + else + nval = 0; + shift = idx * 2; + val &= ~(0x03 << shift); + val |= nval << shift; + shift = idx * 4 + 8; + if (nval == 2) { + nval = ucontrol->value.enumerated.item[0] - 1; + val &= ~(0x07 << shift); + val |= nval << shift; + } else if (nval == 3) { + nval = (ucontrol->value.enumerated.item[0] - 9) << 3; + val &= ~(0x08 << shift); + val |= nval << shift; + } + change = val != old_val; + if (change) + outw(val, ICEMT(ice, ROUTE_SPDOUT)); + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Playback Route", + .info = snd_ice1712_pro_route_info, + .get = snd_ice1712_pro_route_analog_get, + .put = snd_ice1712_pro_route_analog_put, +}; + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Route", + .info = snd_ice1712_pro_route_info, + .get = snd_ice1712_pro_route_spdif_get, + .put = snd_ice1712_pro_route_spdif_put, +}; + + +static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE)); + return 0; +} + +static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change; + + spin_lock_irq(&ice->reg_lock); + change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0]; + outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE)); + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Volume Rate", + .info = snd_ice1712_pro_volume_rate_info, + .get = snd_ice1712_pro_volume_rate_get, + .put = snd_ice1712_pro_volume_rate_put +}; + +static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 22; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx; + + spin_lock_irq(&ice->reg_lock); + for (idx = 0; idx < 22; idx++) { + outb(idx, ICEMT(ice, MONITOR_PEAKINDEX)); + ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA)); + } + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Peak", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ice1712_pro_peak_info, + .get = snd_ice1712_pro_peak_get +}; + +/* + * + */ + +static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice, + unsigned char dev, + unsigned char addr) +{ + long t = 0x10000; + + outb(addr, ICEREG(ice, I2C_BYTE_ADDR)); + outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR)); + while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ; + return inb(ICEREG(ice, I2C_DATA)); +} + +static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice) +{ + int dev = 0xa0; /* EEPROM device address */ + unsigned int i; + + if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) { + snd_printk("ICE1712 has not detected EEPROM\n"); + return -EIO; + } + ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | + (snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | + (snd_ice1712_read_i2c(ice, dev, 0x03) << 24); + ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04); + if (ice->eeprom.size > 32) { + snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size); + return -EIO; + } + ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05); + if (ice->eeprom.version != 1) { + snd_printk("invalid EEPROM version %i\n", ice->eeprom.version); + /* return -EIO; */ + } + for (i = 0; i < ice->eeprom.size; i++) + ice->eeprom.data[i] = snd_ice1712_read_i2c(ice, dev, i + 6); + + ice->eeprom.gpiomask = ice->eeprom.data[ICE_EEP1_GPIO_MASK]; + ice->eeprom.gpiostate = ice->eeprom.data[ICE_EEP1_GPIO_STATE]; + ice->eeprom.gpiodir = ice->eeprom.data[ICE_EEP1_GPIO_DIR]; + + return 0; +} + + + +static int __devinit snd_ice1712_chip_init(ice1712_t *ice) +{ + outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL)); + udelay(200); + outb(ICE1712_NATIVE, ICEREG(ice, CONTROL)); + udelay(200); + pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]); + pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]); + pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]); + pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]); + if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) { + ice->gpio.write_mask = ice->eeprom.gpiomask; + ice->gpio.direction = ice->eeprom.gpiodir; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate); + } else { + ice->gpio.write_mask = 0xc0; + ice->gpio.direction = 0xff; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT); + } + snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0); + if (!(ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97)) { + outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD)); + udelay(100); + outb(0, ICEREG(ice, AC97_CMD)); + udelay(200); + snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0); + } + + return 0; +} + +int __devinit snd_ice1712_spdif_build_controls(ice1712_t *ice) +{ + int err; + snd_kcontrol_t *kctl; + + snd_assert(ice->pcm_pro != NULL, return -EIO); + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + ice->spdif.stream_ctl = kctl; + return 0; +} + + +static int __devinit snd_ice1712_build_controls(ice1712_t *ice) +{ + unsigned int idx; + snd_kcontrol_t *kctl; + int err; + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_internal_clock, ice)); + if (err < 0) + return err; + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_rate_locking, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_rate_reset, ice)); + if (err < 0) + return err; + + for (idx = 0; idx < ice->num_total_dacs; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + + for (idx = 0; idx < 2; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); + if (err < 0) + return err; + + return 0; +} + +static int snd_ice1712_free(ice1712_t *ice) +{ + if (ice->res_port == NULL) + goto __hw_end; + /* mask all interrupts */ + outb(0xc0, ICEMT(ice, IRQ)); + outb(0xff, ICEREG(ice, IRQMASK)); + /* --- */ + __hw_end: + if (ice->irq >= 0) { + synchronize_irq(ice->irq); + free_irq(ice->irq, (void *) ice); + } + if (ice->res_port) { + release_resource(ice->res_port); + kfree_nocheck(ice->res_port); + } + if (ice->res_ddma_port) { + release_resource(ice->res_ddma_port); + kfree_nocheck(ice->res_ddma_port); + } + if (ice->res_dmapath_port) { + release_resource(ice->res_dmapath_port); + kfree_nocheck(ice->res_dmapath_port); + } + if (ice->res_profi_port) { + release_resource(ice->res_profi_port); + kfree_nocheck(ice->res_profi_port); + } + if (ice->akm) + kfree(ice->akm); + snd_magic_kfree(ice); + return 0; +} + +static int snd_ice1712_dev_free(snd_device_t *device) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO); + return snd_ice1712_free(ice); +} + +static int __devinit snd_ice1712_create(snd_card_t * card, + struct pci_dev *pci, + int omni, + ice1712_t ** r_ice1712) +{ + ice1712_t *ice; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_ice1712_dev_free, + }; + + *r_ice1712 = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL); + if (ice == NULL) + return -ENOMEM; + ice->omni = omni ? 1 : 0; + spin_lock_init(&ice->reg_lock); + init_MUTEX(&ice->gpio_mutex); + ice->gpio.set_mask = snd_ice1712_set_gpio_mask; + ice->gpio.set_dir = snd_ice1712_set_gpio_dir; + ice->gpio.set_data = snd_ice1712_set_gpio_data; + ice->gpio.get_data = snd_ice1712_get_gpio_data; + + ice->spdif.cs8403_bits = + ice->spdif.cs8403_stream_bits = (0x01 | /* consumer format */ + 0x10 | /* no emphasis */ + 0x20); /* PCM encoder/decoder */ + ice->card = card; + ice->pci = pci; + ice->irq = -1; + ice->port = pci_resource_start(pci, 0); + ice->ddma_port = pci_resource_start(pci, 1); + ice->dmapath_port = pci_resource_start(pci, 2); + ice->profi_port = pci_resource_start(pci, 3); + pci_set_master(pci); + pci_write_config_word(ice->pci, 0x40, 0x807f); + pci_write_config_word(ice->pci, 0x42, 0x0006); + snd_ice1712_proc_init(ice); + synchronize_irq(pci->irq); + + if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1); + return -EIO; + } + if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1); + return -EIO; + } + if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1); + return -EIO; + } + if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1); + return -EIO; + } + if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) { + snd_ice1712_free(ice); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EIO; + } + + ice->irq = pci->irq; + + if (snd_ice1712_read_eeprom(ice) < 0) { + snd_ice1712_free(ice); + return -EIO; + } + if (snd_ice1712_chip_init(ice) < 0) { + snd_ice1712_free(ice); + return -EIO; + } + + /* unmask used interrupts */ + outb((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 | + (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0, + ICEREG(ice, IRQMASK)); + outb(0x00, ICEMT(ice, IRQ)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) { + snd_ice1712_free(ice); + return err; + } + + *r_ice1712 = ice; + return 0; +} + + +/* + * + * Registration + * + */ + +static struct snd_ice1712_card_info no_matched __devinitdata; + +static struct snd_ice1712_card_info *card_tables[] __devinitdata = { + snd_ice1712_hoontech_cards, + snd_ice1712_delta_cards, + snd_ice1712_ews_cards, + 0, +}; + + +static int __devinit snd_ice1712_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + ice1712_t *ice; + int pcm_dev = 0, err; + struct snd_ice1712_card_info **tbl, *c; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "ICE1712"); + strcpy(card->shortname, "ICEnsemble ICE1712"); + + if ((err = snd_ice1712_create(card, pci, omni[dev], &ice)) < 0) { + snd_card_free(card); + return err; + } + + for (tbl = card_tables; *tbl; tbl++) { + for (c = *tbl; c->subvendor; c++) { + if (c->subvendor == ice->eeprom.subvendor) { + strcpy(card->shortname, c->name); + if (c->chip_init) { + if ((err = c->chip_init(ice)) < 0) { + snd_card_free(card); + return err; + } + } + goto __found; + } + } + } + c = &no_matched; + __found: + + if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (ice_has_con_ac97(ice)) + if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_ac97_mixer(ice)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + + if (c->build_controls) { + if ((err = c->build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + } + + if (ice_has_con_ac97(ice)) + if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (! c->no_mpu401) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, + ICEREG(ice, MPU1_CTRL), 1, + ice->irq, 0, + &ice->rmidi[0])) < 0) { + snd_card_free(card); + return err; + } + + if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) + if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, + ICEREG(ice, MPU2_CTRL), 1, + ice->irq, 0, + &ice->rmidi[1])) < 0) { + snd_card_free(card); + return err; + } + } + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, ice->port, ice->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_ice1712_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ICE1712", + .id_table = snd_ice1712_ids, + .probe = snd_ice1712_probe, + .remove = __devexit_p(snd_ice1712_remove), +}; + +static int __init alsa_card_ice1712_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ICE1712 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ice1712_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ice1712_init) +module_exit(alsa_card_ice1712_exit) + +#ifndef MODULE + +/* format is: snd-ice1712=enable,index,id */ + +static int __init alsa_card_ice1712_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ice1712=", alsa_card_ice1712_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/ice1712.h linux/sound/pci/ice1712/ice1712.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/ice1712.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/ice1712.h 2003-02-28 07:25:39.000000000 -0700 @@ -0,0 +1,475 @@ +#ifndef __SOUND_ICE1712_H +#define __SOUND_ICE1712_H + +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + + +/* + * Direct registers + */ + +#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x) + +#define ICE1712_REG_CONTROL 0x00 /* byte */ +#define ICE1712_RESET 0x80 /* reset whole chip */ +#define ICE1712_SERR_LEVEL 0x04 /* SERR# level otherwise edge */ +#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */ +#define ICE1712_REG_IRQMASK 0x01 /* byte */ +#define ICE1712_IRQ_MPU1 0x80 +#define ICE1712_IRQ_TIMER 0x40 +#define ICE1712_IRQ_MPU2 0x20 +#define ICE1712_IRQ_PROPCM 0x10 +#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */ +#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */ +#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */ +#define ICE1712_IRQ_CONPBK 0x01 /* consumer playback */ +#define ICE1712_REG_IRQSTAT 0x02 /* byte */ +/* look to ICE1712_IRQ_* */ +#define ICE1712_REG_INDEX 0x03 /* byte - indirect CCIxx regs */ +#define ICE1712_REG_DATA 0x04 /* byte - indirect CCIxx regs */ +#define ICE1712_REG_NMI_STAT1 0x05 /* byte */ +#define ICE1712_REG_NMI_DATA 0x06 /* byte */ +#define ICE1712_REG_NMI_INDEX 0x07 /* byte */ +#define ICE1712_REG_AC97_INDEX 0x08 /* byte */ +#define ICE1712_REG_AC97_CMD 0x09 /* byte */ +#define ICE1712_AC97_COLD 0x80 /* cold reset */ +#define ICE1712_AC97_WARM 0x40 /* warm reset */ +#define ICE1712_AC97_WRITE 0x20 /* W: write, R: write in progress */ +#define ICE1712_AC97_READ 0x10 /* W: read, R: read in progress */ +#define ICE1712_AC97_READY 0x08 /* codec ready status bit */ +#define ICE1712_AC97_PBK_VSR 0x02 /* playback VSR */ +#define ICE1712_AC97_CAP_VSR 0x01 /* capture VSR */ +#define ICE1712_REG_AC97_DATA 0x0a /* word (little endian) */ +#define ICE1712_REG_MPU1_CTRL 0x0c /* byte */ +#define ICE1712_REG_MPU1_DATA 0x0d /* byte */ +#define ICE1712_REG_I2C_DEV_ADDR 0x10 /* byte */ +#define ICE1712_I2C_WRITE 0x01 /* write direction */ +#define ICE1712_REG_I2C_BYTE_ADDR 0x11 /* byte */ +#define ICE1712_REG_I2C_DATA 0x12 /* byte */ +#define ICE1712_REG_I2C_CTRL 0x13 /* byte */ +#define ICE1712_I2C_EEPROM 0x80 /* EEPROM exists */ +#define ICE1712_I2C_BUSY 0x01 /* busy bit */ +#define ICE1712_REG_CONCAP_ADDR 0x14 /* dword - consumer capture */ +#define ICE1712_REG_CONCAP_COUNT 0x18 /* word - current/base count */ +#define ICE1712_REG_SERR_SHADOW 0x1b /* byte */ +#define ICE1712_REG_MPU2_CTRL 0x1c /* byte */ +#define ICE1712_REG_MPU2_DATA 0x1d /* byte */ +#define ICE1712_REG_TIMER 0x1e /* word */ + +/* + * Indirect registers + */ + +#define ICE1712_IREG_PBK_COUNT_HI 0x00 +#define ICE1712_IREG_PBK_COUNT_LO 0x01 +#define ICE1712_IREG_PBK_CTRL 0x02 +#define ICE1712_IREG_PBK_LEFT 0x03 /* left volume */ +#define ICE1712_IREG_PBK_RIGHT 0x04 /* right volume */ +#define ICE1712_IREG_PBK_SOFT 0x05 /* soft volume */ +#define ICE1712_IREG_PBK_RATE_LO 0x06 +#define ICE1712_IREG_PBK_RATE_MID 0x07 +#define ICE1712_IREG_PBK_RATE_HI 0x08 +#define ICE1712_IREG_CAP_COUNT_HI 0x10 +#define ICE1712_IREG_CAP_COUNT_LO 0x11 +#define ICE1712_IREG_CAP_CTRL 0x12 +#define ICE1712_IREG_GPIO_DATA 0x20 +#define ICE1712_IREG_GPIO_WRITE_MASK 0x21 +#define ICE1712_IREG_GPIO_DIRECTION 0x22 +#define ICE1712_IREG_CONSUMER_POWERDOWN 0x30 +#define ICE1712_IREG_PRO_POWERDOWN 0x31 + +/* + * Consumer section direct DMA registers + */ + +#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x) + +#define ICE1712_DS_INTMASK 0x00 /* word - interrupt mask */ +#define ICE1712_DS_INTSTAT 0x02 /* word - interrupt status */ +#define ICE1712_DS_DATA 0x04 /* dword - channel data */ +#define ICE1712_DS_INDEX 0x08 /* dword - channel index */ + +/* + * Consumer section channel registers + */ + +#define ICE1712_DSC_ADDR0 0x00 /* dword - base address 0 */ +#define ICE1712_DSC_COUNT0 0x01 /* word - count 0 */ +#define ICE1712_DSC_ADDR1 0x02 /* dword - base address 1 */ +#define ICE1712_DSC_COUNT1 0x03 /* word - count 1 */ +#define ICE1712_DSC_CONTROL 0x04 /* byte - control & status */ +#define ICE1712_BUFFER1 0x80 /* buffer1 is active */ +#define ICE1712_BUFFER1_AUTO 0x40 /* buffer1 auto init */ +#define ICE1712_BUFFER0_AUTO 0x20 /* buffer0 auto init */ +#define ICE1712_FLUSH 0x10 /* flush FIFO */ +#define ICE1712_STEREO 0x08 /* stereo */ +#define ICE1712_16BIT 0x04 /* 16-bit data */ +#define ICE1712_PAUSE 0x02 /* pause */ +#define ICE1712_START 0x01 /* start */ +#define ICE1712_DSC_RATE 0x05 /* dword - rate */ +#define ICE1712_DSC_VOLUME 0x06 /* word - volume control */ + +/* + * Professional multi-track direct control registers + */ + +#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x) + +#define ICE1712_MT_IRQ 0x00 /* byte - interrupt mask */ +#define ICE1712_MULTI_CAPTURE 0x80 /* capture IRQ */ +#define ICE1712_MULTI_PLAYBACK 0x40 /* playback IRQ */ +#define ICE1712_MULTI_CAPSTATUS 0x02 /* capture IRQ status */ +#define ICE1712_MULTI_PBKSTATUS 0x01 /* playback IRQ status */ +#define ICE1712_MT_RATE 0x01 /* byte - sampling rate select */ +#define ICE1712_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */ +#define ICE1712_MT_I2S_FORMAT 0x02 /* byte - I2S data format */ +#define ICE1712_MT_AC97_INDEX 0x04 /* byte - AC'97 index */ +#define ICE1712_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */ +/* look to ICE1712_AC97_* */ +#define ICE1712_MT_AC97_DATA 0x06 /* word - AC'97 data */ +#define ICE1712_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */ +#define ICE1712_MT_PLAYBACK_SIZE 0x14 /* word - playback size */ +#define ICE1712_MT_PLAYBACK_COUNT 0x16 /* word - playback count */ +#define ICE1712_MT_PLAYBACK_CONTROL 0x18 /* byte - control */ +#define ICE1712_CAPTURE_START_SHADOW 0x04 /* capture start */ +#define ICE1712_PLAYBACK_PAUSE 0x02 /* playback pause */ +#define ICE1712_PLAYBACK_START 0x01 /* playback start */ +#define ICE1712_MT_CAPTURE_ADDR 0x20 /* dword - capture address */ +#define ICE1712_MT_CAPTURE_SIZE 0x24 /* word - capture size */ +#define ICE1712_MT_CAPTURE_COUNT 0x26 /* word - capture count */ +#define ICE1712_MT_CAPTURE_CONTROL 0x28 /* byte - control */ +#define ICE1712_CAPTURE_START 0x01 /* capture start */ +#define ICE1712_MT_ROUTE_PSDOUT03 0x30 /* word */ +#define ICE1712_MT_ROUTE_SPDOUT 0x32 /* word */ +#define ICE1712_MT_ROUTE_CAPTURE 0x34 /* dword */ +#define ICE1712_MT_MONITOR_VOLUME 0x38 /* word */ +#define ICE1712_MT_MONITOR_INDEX 0x3a /* byte */ +#define ICE1712_MT_MONITOR_RATE 0x3b /* byte */ +#define ICE1712_MT_MONITOR_ROUTECTRL 0x3c /* byte */ +#define ICE1712_ROUTE_AC97 0x01 /* route digital mixer output to AC'97 */ +#define ICE1712_MT_MONITOR_PEAKINDEX 0x3e /* byte */ +#define ICE1712_MT_MONITOR_PEAKDATA 0x3f /* byte */ + +/* + * Codec configuration bits + */ + +/* PCI[60] System Configuration */ +#define ICE1712_CFG_CLOCK 0xc0 +#define ICE1712_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */ +#define ICE1712_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */ +#define ICE1712_CFG_EXT 0x80 /* external clock */ +#define ICE1712_CFG_2xMPU401 0x20 /* two MPU401 UARTs */ +#define ICE1712_CFG_NO_CON_AC97 0x10 /* consumer AC'97 codec is not present */ +#define ICE1712_CFG_ADC_MASK 0x0c /* one, two, three, four stereo ADCs */ +#define ICE1712_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */ +/* PCI[61] AC-Link Configuration */ +#define ICE1712_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */ +#define ICE1712_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */ +/* PCI[62] I2S Features */ +#define ICE1712_CFG_I2S_VOLUME 0x80 /* volume/mute capability */ +#define ICE1712_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */ +#define ICE1712_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */ +#define ICE1712_CFG_I2S_OTHER 0x0f /* other I2S IDs */ +/* PCI[63] S/PDIF Configuration */ +#define ICE1712_CFG_I2S_CHIPID 0xfc /* I2S chip ID */ +#define ICE1712_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */ +#define ICE1712_CFG_SPDIF_OUT 0x01 /* S/PDIF output is present */ + +/* + * DMA mode values + * identical with DMA_XXX on i386 architecture. + */ +#define ICE1712_DMA_MODE_WRITE 0x48 +#define ICE1712_DMA_AUTOINIT 0x10 + + +/* + * + */ + +typedef struct _snd_ice1712 ice1712_t; +typedef struct snd_ak4xxx akm4xxx_t; + +typedef struct { + unsigned int subvendor; /* PCI[2c-2f] */ + unsigned char size; /* size of EEPROM image in bytes */ + unsigned char version; /* must be 1 (or 2 for vt1724) */ + unsigned char data[32]; + unsigned int gpiomask; + unsigned int gpiostate; + unsigned int gpiodir; +} ice1712_eeprom_t; + +enum { + ICE_EEP1_CODEC = 0, /* 06 */ + ICE_EEP1_ACLINK, /* 07 */ + ICE_EEP1_I2SID, /* 08 */ + ICE_EEP1_SPDIF, /* 09 */ + ICE_EEP1_GPIO_MASK, /* 0a */ + ICE_EEP1_GPIO_STATE, /* 0b */ + ICE_EEP1_GPIO_DIR, /* 0c */ + ICE_EEP1_AC97_MAIN_LO, /* 0d */ + ICE_EEP1_AC97_MAIN_HI, /* 0e */ + ICE_EEP1_AC97_PCM_LO, /* 0f */ + ICE_EEP1_AC97_PCM_HI, /* 10 */ + ICE_EEP1_AC97_REC_LO, /* 11 */ + ICE_EEP1_AC97_REC_HI, /* 12 */ + ICE_EEP1_AC97_RECSRC, /* 13 */ + ICE_EEP1_DAC_ID, /* 14 */ + ICE_EEP1_DAC_ID1, + ICE_EEP1_DAC_ID2, + ICE_EEP1_DAC_ID3, + ICE_EEP1_ADC_ID, /* 18 */ + ICE_EEP1_ADC_ID1, + ICE_EEP1_ADC_ID2, + ICE_EEP1_ADC_ID3 +}; + +#define ice_has_con_ac97(ice) (!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97)) + + +struct snd_ak4xxx { + unsigned int num_adcs; /* AK4524 or AK4528 ADCs */ + unsigned int num_dacs; /* AK4524 or AK4528 DACs */ + unsigned char images[4][16]; /* saved register image */ + unsigned char ipga_gain[4][2]; /* saved register image for IPGA (AK4528) */ + ice1712_t *chip; + /* template should fill the following fields */ + unsigned int idx_offset; /* control index offset */ + enum { + SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4355, SND_AK4381 + } type; + unsigned int cif: 1; /* CIF mode */ + unsigned char caddr; /* C0 and C1 bits */ + unsigned int data_mask; /* DATA gpio bit */ + unsigned int clk_mask; /* CLK gpio bit */ + unsigned int cs_mask; /* bit mask for select/deselect address */ + unsigned int cs_addr; /* bits to select address */ + unsigned int cs_none; /* bits to deselect address */ + unsigned int add_flags; /* additional bits at init */ + unsigned int mask_flags; /* total mask bits */ + struct snd_akm4xxx_ops { + int (*start)(akm4xxx_t *ak, int chip); + void (*stop)(akm4xxx_t *ak); + void (*set_rate_val)(akm4xxx_t *ak, unsigned int rate); + } ops; +}; + +struct snd_ice1712_spdif { + unsigned char cs8403_bits; + unsigned char cs8403_stream_bits; + snd_kcontrol_t *stream_ctl; + + struct snd_ice1712_spdif_ops { + void (*open)(ice1712_t *, snd_pcm_substream_t *); + void (*setup_rate)(ice1712_t *, int rate); + void (*close)(ice1712_t *, snd_pcm_substream_t *); + void (*default_get)(ice1712_t *, snd_ctl_elem_value_t * ucontrol); + int (*default_put)(ice1712_t *, snd_ctl_elem_value_t * ucontrol); + void (*stream_get)(ice1712_t *, snd_ctl_elem_value_t * ucontrol); + int (*stream_put)(ice1712_t *, snd_ctl_elem_value_t * ucontrol); + } ops; +}; + + +struct _snd_ice1712 { + unsigned long conp_dma_size; + unsigned long conc_dma_size; + unsigned long prop_dma_size; + unsigned long proc_dma_size; + int irq; + + unsigned long port; + struct resource *res_port; + unsigned long ddma_port; + struct resource *res_ddma_port; + unsigned long dmapath_port; + struct resource *res_dmapath_port; + unsigned long profi_port; + struct resource *res_profi_port; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_ds; + snd_pcm_t *pcm_pro; + snd_pcm_substream_t *playback_con_substream; + snd_pcm_substream_t *playback_con_substream_ds[6]; + snd_pcm_substream_t *capture_con_substream; + snd_pcm_substream_t *playback_pro_substream; + snd_pcm_substream_t *capture_pro_substream; + unsigned int playback_pro_size; + unsigned int capture_pro_size; + unsigned int playback_con_virt_addr[6]; + unsigned int playback_con_active_buf[6]; + unsigned int capture_con_virt_addr; + unsigned int ac97_ext_id; + ac97_t *ac97; + snd_rawmidi_t *rmidi[2]; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; + + ice1712_eeprom_t eeprom; + + unsigned int pro_volumes[20]; + unsigned int omni: 1; /* Delta Omni I/O */ + unsigned int vt1724: 1; + unsigned int num_total_dacs; /* total DACs */ + unsigned char hoontech_boxbits[4]; + unsigned int hoontech_config; + unsigned short hoontech_boxconfig[4]; + unsigned int cur_rate; /* current rate */ + + unsigned int akm_codecs; + akm4xxx_t *akm; + struct snd_ice1712_spdif spdif; + + snd_i2c_bus_t *i2c; /* I2C bus */ + snd_i2c_device_t *cs8404; /* CS8404A I2C device */ + snd_i2c_device_t *cs8427; /* CS8427 I2C device */ + snd_i2c_device_t *i2cdevs[2]; /* additional i2c devices */ + + struct ice1712_gpio { + unsigned int direction; /* current direction bits */ + unsigned int write_mask; /* current mask bits */ + unsigned int saved[2]; /* for ewx_i2c */ + /* operators */ + void (*set_mask)(ice1712_t *ice, unsigned int data); + void (*set_dir)(ice1712_t *ice, unsigned int data); + void (*set_data)(ice1712_t *ice, unsigned int data); + unsigned int (*get_data)(ice1712_t *ice); + } gpio; + struct semaphore gpio_mutex; +}; + +#define chip_t ice1712_t + + +/* + * gpio access functions + */ +static inline void snd_ice1712_gpio_set_dir(ice1712_t *ice, unsigned int bits) +{ + ice->gpio.set_dir(ice, bits); +} + +static inline void snd_ice1712_gpio_set_mask(ice1712_t *ice, unsigned int bits) +{ + ice->gpio.set_mask(ice, bits); +} + +static inline void snd_ice1712_gpio_write(ice1712_t *ice, unsigned int val) +{ + ice->gpio.set_data(ice, val); +} + +static inline unsigned int snd_ice1712_gpio_read(ice1712_t *ice) +{ + return ice->gpio.get_data(ice); +} + +/* + * save and restore gpio status + * The access to gpio will be protected by mutex, so don't forget to + * restore! + */ +static inline void snd_ice1712_save_gpio_status(ice1712_t *ice) +{ + down(&ice->gpio_mutex); + ice->gpio.saved[0] = ice->gpio.direction; + ice->gpio.saved[1] = ice->gpio.write_mask; +} + +static inline void snd_ice1712_restore_gpio_status(ice1712_t *ice) +{ + ice->gpio.set_dir(ice, ice->gpio.saved[0]); + ice->gpio.set_mask(ice, ice->gpio.saved[1]); + ice->gpio.direction = ice->gpio.saved[0]; + ice->gpio.write_mask = ice->gpio.saved[1]; + up(&ice->gpio_mutex); +} + +/* for bit controls */ +#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \ +{ .iface = xiface, .name = xname, .access = xaccess, .info = snd_ice1712_gpio_info, \ + .get = snd_ice1712_gpio_get, .put = snd_ice1712_gpio_put, \ + .private_value = mask | (invert << 24) } + +int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +/* + * set gpio direction, write mask and data + */ +static inline void snd_ice1712_gpio_write_bits(ice1712_t *ice, unsigned int mask, unsigned int bits) +{ + ice->gpio.direction |= mask; + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); + snd_ice1712_gpio_set_mask(ice, ~mask); + snd_ice1712_gpio_write(ice, mask & bits); +} + +int snd_ice1712_spdif_build_controls(ice1712_t *ice); + +void snd_ice1712_akm4xxx_write(akm4xxx_t *ice, int chip, unsigned char addr, unsigned char data); +void snd_ice1712_akm4xxx_reset(akm4xxx_t *ice, int state); +void snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *template, ice1712_t *ice); +int snd_ice1712_akm4xxx_build_controls(ice1712_t *ice); + +int snd_ice1712_init_cs8427(ice1712_t *ice, int addr); + +static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data) +{ + outb(addr, ICEREG(ice, INDEX)); + outb(data, ICEREG(ice, DATA)); +} + +static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr) +{ + outb(addr, ICEREG(ice, INDEX)); + return inb(ICEREG(ice, DATA)); +} + + +/* + * entry pointer + */ + +struct snd_ice1712_card_info { + unsigned int subvendor; + char *name; + int (*chip_init)(ice1712_t *); + int (*build_controls)(ice1712_t *); + int no_mpu401: 1; +}; + + +#endif /* __SOUND_ICE1712_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/ice1724.c linux/sound/pci/ice1712/ice1724.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/ice1724.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/ice1724.c 2003-03-15 08:38:29.000000000 -0700 @@ -0,0 +1,1973 @@ +/* + * ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT) + * + * Copyright (c) 2000 Jaroslav Kysela + * 2002 James Stafford + * 2003 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#include "ice1712.h" +#include "envy24ht.h" + +/* lowlevel routines */ +#include "amp.h" +#include "revo.h" + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ICEnsemble ICE1724 (Envy24HT)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{" + REVO_DEVICE_DESC + AMP_AUDIO2000_DEVICE_DESC + "{VIA,VT1724}," + "{ICEnsemble,Generic ICE1724}," + "{ICEnsemble,Generic Envy24HT}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for ICE1724 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for ICE1724 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable ICE1724 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +#ifndef PCI_VENDOR_ID_ICE +#define PCI_VENDOR_ID_ICE 0x1412 +#endif +#ifndef PCI_DEVICE_ID_VT1724 +#define PCI_DEVICE_ID_VT1724 0x1724 +#endif + +static struct pci_device_id snd_vt1724_ids[] __devinitdata = { + { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_vt1724_ids); + + +static int PRO_RATE_LOCKED = 0; +static int PRO_RATE_RESET = 1; +static unsigned int PRO_RATE_DEFAULT = 44100; + +/* + * Basic I/O + */ + +/* check whether the clock mode is spdif-in */ +static inline int is_spdif_master(ice1712_t *ice) +{ + return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0; +} + +static inline int is_pro_rate_locked(ice1712_t *ice) +{ + return is_spdif_master(ice) || PRO_RATE_LOCKED; +} + +/* + * ac97 section + */ + +static unsigned char snd_vt1724_ac97_ready(ice1712_t *ice) +{ + unsigned char old_cmd; + int tm; + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT1724(ice, AC97_CMD)); + if (old_cmd & (VT1724_AC97_WRITE | VT1724_AC97_READ)) + continue; + if (!(old_cmd & VT1724_AC97_READY)) + continue; + return 0; + } + return old_cmd; +} + +static int snd_vt1724_ac97_wait_bit(ice1712_t *ice, unsigned char bit) +{ + int tm; + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0) + return 0; + return -EIO; +} + +static void snd_vt1724_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + unsigned char old_cmd; + + old_cmd = snd_vt1724_ac97_ready(ice); + outb(reg, ICEMT1724(ice, AC97_INDEX)); + outw(val, ICEMT1724(ice, AC97_DATA)); + old_cmd &= ~(VT1724_AC97_PBK_VSR | VT1724_AC97_CAP_VSR); + outb(old_cmd | VT1724_AC97_WRITE, ICEMT1724(ice, AC97_CMD)); + snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_WRITE); +} + +static unsigned short snd_vt1724_ac97_read(ac97_t *ac97, unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + unsigned char old_cmd; + + old_cmd = snd_vt1724_ac97_ready(ice); + outb(reg, ICEMT1724(ice, AC97_INDEX)); + outb(old_cmd | VT1724_AC97_READ, ICEMT1724(ice, AC97_CMD)); + if (snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_READ) < 0) + return ~0; + return inw(ICEMT1724(ice, AC97_DATA)); +} + + +/* + * GPIO operations + */ + +/* set gpio direction 0 = read, 1 = write */ +static void snd_vt1724_set_gpio_dir(ice1712_t *ice, unsigned int data) +{ + outl(data, ICEREG1724(ice, GPIO_DIRECTION)); +} + +/* set the gpio mask (0 = writable) */ +static void snd_vt1724_set_gpio_mask(ice1712_t *ice, unsigned int data) +{ + outw(data, ICEREG1724(ice, GPIO_WRITE_MASK)); + outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22)); +} + +static void snd_vt1724_set_gpio_data(ice1712_t *ice, unsigned int data) +{ + outw(data, ICEREG1724(ice, GPIO_DATA)); + outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22)); +} + +static unsigned int snd_vt1724_get_gpio_data(ice1712_t *ice) +{ + unsigned int data; + data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22)); + data = (data << 16) | inw(ICEREG1724(ice, GPIO_DATA)); + return data; +} + +/* + * Interrupt handler + */ + +static void snd_vt1724_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return); + unsigned char status; + + while (1) { + status = inb(ICEREG1724(ice, IRQSTAT)); + if (status == 0) + break; + + /* these should probably be separated at some point, + but as we don't currently have MPU support on the board I will leave it */ + if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) { + if (ice->rmidi[0]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); + outb(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX, ICEREG1724(ice, IRQSTAT)); + status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX); + } + if (status & VT1724_IRQ_MTPCM) { + unsigned char mtstat = inb(ICEMT1724(ice, IRQ)); + if (mtstat & VT1724_MULTI_PDMA0) { + if (ice->playback_pro_substream) + snd_pcm_period_elapsed(ice->playback_pro_substream); + } + if (mtstat & VT1724_MULTI_RDMA0) { + if (ice->capture_pro_substream) + snd_pcm_period_elapsed(ice->capture_pro_substream); + } + if (mtstat & VT1724_MULTI_PDMA4) { + if (ice->playback_con_substream) + snd_pcm_period_elapsed(ice->playback_con_substream); + } + if (mtstat & VT1724_MULTI_RDMA1) { + if (ice->capture_con_substream) + snd_pcm_period_elapsed(ice->capture_con_substream); + } + /* ack anyway to avoid freeze */ + outb(mtstat, ICEMT1724(ice, IRQ)); + /* ought to really handle this properly */ + if (mtstat & VT1724_MULTI_FIFO_ERR) { + unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR)); + outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR)); + outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK)); + /* If I don't do this, I get machine lockup due to continual interrupts */ + } + + } + } +} + +/* + * PCM code - professional part (multitrack) + */ + +static unsigned int rates[] = { + 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000, + 176400, 192000, +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates_96 = { + .count = ARRAY_SIZE(rates) - 2, /* up to 96000 */ + .list = rates, + .mask = 0, +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates_192 = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static unsigned int hw_channels[] = { + 2, 4, 6, 8 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + .count = ARRAY_SIZE(hw_channels), + .list = hw_channels, + .mask = 0, +}; + +static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned int what; + unsigned int old; + snd_pcm_substream_t *s; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + what = 0; + s = substream; + do { + if (s == ice->playback_pro_substream) + what |= VT1724_PDMA0_PAUSE; + else if (s == ice->capture_pro_substream) + what |= VT1724_RDMA0_PAUSE; + else if (s == ice->playback_con_substream) + what |= VT1724_PDMA4_PAUSE; + else if (s == ice->capture_con_substream) + what |= VT1724_RDMA1_PAUSE; + s = s->link_next; + } while (s != substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT1724(ice, DMA_PAUSE)); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + old |= what; + else + old &= ~what; + outl(old, ICEMT1724(ice, DMA_PAUSE)); + spin_unlock(&ice->reg_lock); + break; + + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + what = 0; + s = substream; + do { + if (s == ice->playback_pro_substream) { + what |= VT1724_PDMA0_START; + snd_pcm_trigger_done(s, substream); + } else if (s == ice->capture_pro_substream) { + what |= VT1724_RDMA0_START; + snd_pcm_trigger_done(s, substream); + } else if (s == ice->playback_con_substream) { + what |= VT1724_PDMA4_START; + snd_pcm_trigger_done(s, substream); + } else if (s == ice->capture_con_substream) { + what |= VT1724_RDMA1_START; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT1724(ice, DMA_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_START) + old |= what; + else + old &= ~what; + outl(old, ICEMT1724(ice, DMA_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + + default: + return -EINVAL; + } + return 0; +} + +/* + */ + +#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|VT1724_PDMA4_START) +#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|VT1724_PDMA4_PAUSE) + +static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force) +{ + unsigned long flags; + unsigned char val, old; + unsigned int i; + + spin_lock_irqsave(&ice->reg_lock, flags); + if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || + (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { + /* running? we cannot change the rate now... */ + spin_unlock_irqrestore(&ice->reg_lock, flags); + return; + } + if (!force && is_pro_rate_locked(ice)) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + return; + } + + if (rate == ice->cur_rate) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + return; + } + + switch (rate) { + case 8000: val = 6; break; + case 9600: val = 3; break; + case 11025: val = 10; break; + case 12000: val = 2; break; + case 16000: val = 5; break; + case 22050: val = 9; break; + case 24000: val = 1; break; + case 32000: val = 4; break; + case 44100: val = 8; break; + case 48000: val = 0; break; + case 64000: val = 15; break; + case 88200: val = 11; break; + case 96000: val = 7; break; + case 176400: val = 12; break; + case 192000: val = 14; break; + default: + snd_BUG(); + val = 0; + break; + } + outb(val, ICEMT1724(ice, RATE)); + ice->cur_rate = rate; + + /* check MT02 */ + if (ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) { + val = old = inb(ICEMT1724(ice, I2S_FORMAT)); + if (rate > 96000) + val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ + else + val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */ + if (val != old) { + outb(val, ICEMT1724(ice, I2S_FORMAT)); + /* FIXME: is this revo only? */ + /* assert PRST# to converters; MT05 bit 7 */ + outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + mdelay(5); + spin_lock_irqsave(&ice->reg_lock, flags); + /* deassert PRST# */ + outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD)); + } + } + spin_unlock_irqrestore(&ice->reg_lock, flags); + + /* set up codecs */ + for (i = 0; i < ice->akm_codecs; i++) { + if (ice->akm[i].ops.set_rate_val) + ice->akm[i].ops.set_rate_val(&ice->akm[i], rate); + } +} + +static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_vt1724_pcm_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_vt1724_playback_pro_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned char val; + unsigned int size; + + spin_lock(&ice->reg_lock); + val = (8 - substream->runtime->channels) >> 1; + outb(val, ICEMT1724(ice, BURST)); + + outl(substream->runtime->dma_addr, ICEMT1724(ice, PLAYBACK_ADDR)); + + size = (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1; + // outl(size, ICEMT1724(ice, PLAYBACK_SIZE)); + outw(size, ICEMT1724(ice, PLAYBACK_SIZE)); + outb(size >> 16, ICEMT1724(ice, PLAYBACK_SIZE) + 2); + size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + // outl(size, ICEMT1724(ice, PLAYBACK_COUNT)); + outw(size, ICEMT1724(ice, PLAYBACK_COUNT)); + outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2); + + spin_unlock(&ice->reg_lock); + + // printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); + return 0; +} + +#define CHECK_INVALID_PTR + +static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & VT1724_PDMA0_START)) + return 0; + ptr = inl(ICEMT1724(ice, PLAYBACK_ADDR)); +#ifdef CHECK_INVALID_PTR + if (ptr < substream->runtime->dma_addr) { + snd_printd("ice1724: invalid negative ptr\n"); + return 0; + } +#endif + ptr -= substream->runtime->dma_addr; + ptr = bytes_to_frames(substream->runtime, ptr); +#ifdef CHECK_INVALID_PTR + if (ptr >= substream->runtime->buffer_size) { + snd_printd("ice1724: invalid ptr %d (size=%d)\n", (int)ptr, (int)substream->runtime->period_size); + return 0; + } +#endif + return ptr; +} + +struct vt1724_pcm_reg { + unsigned int addr; /* ADDR register offset */ + unsigned int size; /* SIZE register offset */ + unsigned int count; /* COUNT register offset */ + unsigned int start; /* start bit */ + unsigned int pause; /* pause bit */ +}; + +static int snd_vt1724_pcm_prepare(snd_pcm_substream_t *substream, const struct vt1724_pcm_reg *reg) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + spin_lock(&ice->reg_lock); + outl(substream->runtime->dma_addr, ice->profi_port + reg->addr); + outw((snd_pcm_lib_buffer_bytes(substream) >> 2) - 1, ice->profi_port + reg->size); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ice->profi_port + reg->count); + spin_unlock(&ice->reg_lock); + return 0; +} + +static snd_pcm_uframes_t snd_vt1724_pcm_pointer(snd_pcm_substream_t *substream, const struct vt1724_pcm_reg *reg) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & reg->start)) + return 0; + ptr = inl(ice->profi_port + reg->addr); + ptr -= substream->runtime->dma_addr; + return bytes_to_frames(substream->runtime, ptr); +} + +const static struct vt1724_pcm_reg vt1724_capture_pro_reg = { + .addr = VT1724_MT_CAPTURE_ADDR, + .size = VT1724_MT_CAPTURE_SIZE, + .count = VT1724_MT_CAPTURE_COUNT, + .start = VT1724_RDMA0_START, + .pause = VT1724_RDMA0_PAUSE, +}; + +static int snd_vt1724_capture_pro_prepare(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_prepare(substream, &vt1724_capture_pro_reg); +} + +static snd_pcm_uframes_t snd_vt1724_capture_pro_pointer(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_pointer(substream, &vt1724_capture_pro_reg); +} + +static snd_pcm_hardware_t snd_vt1724_playback_pro = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + .rate_min = 4000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 8, + .buffer_bytes_max = (1UL << 21), /* 18bits dword */ + .period_bytes_min = 8 * 4 * 2, /* FIXME: constraints needed */ + .period_bytes_max = (1UL << 21), + .periods_min = 1, + .periods_max = 1024, +}; + +static snd_pcm_hardware_t snd_vt1724_capture_pro = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + .rate_min = 4000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 2 * 4 * 2, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, +}; + +/* multi-channel playback needs alignment 8x32bit regarless of the channels + * actually used + */ +#define VT1724_BUFFER_ALIGN 0x20 + +static int snd_vt1724_playback_pro_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_substream = substream; + runtime->hw = snd_vt1724_playback_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) && + (ice->eeprom.data[ICE_EEP2_I2S] & 0x08)) + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192); + else + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + VT1724_BUFFER_ALIGN); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + VT1724_BUFFER_ALIGN); + return 0; +} + +static int snd_vt1724_capture_pro_open(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ice->capture_pro_substream = substream; + runtime->hw = snd_vt1724_capture_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) && + (ice->eeprom.data[ICE_EEP2_I2S] & 0x08)) + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192); + else + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); + return 0; +} + +static int snd_vt1724_playback_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->playback_pro_substream = NULL; + + return 0; +} + +static int snd_vt1724_capture_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->capture_pro_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_vt1724_playback_pro_ops = { + .open = snd_vt1724_playback_pro_open, + .close = snd_vt1724_playback_pro_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_vt1724_pcm_hw_params, + .hw_free = snd_vt1724_pcm_hw_free, + .prepare = snd_vt1724_playback_pro_prepare, + .trigger = snd_vt1724_pcm_trigger, + .pointer = snd_vt1724_playback_pro_pointer, +}; + +static snd_pcm_ops_t snd_vt1724_capture_pro_ops = { + .open = snd_vt1724_capture_pro_open, + .close = snd_vt1724_capture_pro_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_vt1724_pcm_hw_params, + .hw_free = snd_vt1724_pcm_hw_free, + .prepare = snd_vt1724_capture_pro_prepare, + .trigger = snd_vt1724_pcm_trigger, + .pointer = snd_vt1724_capture_pro_pointer, +}; + +static int __devinit snd_vt1724_pcm_profi(ice1712_t * ice, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(ice->card, "ICE1724", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vt1724_playback_pro_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vt1724_capture_pro_ops); + + pcm->private_data = ice; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1724"); + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + + ice->pcm_pro = pcm; + + return 0; +} + + +/* + * SPDIF PCM + */ + +static snd_pcm_hardware_t snd_vt1724_playback_spdif = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + .rate_min = 4000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 2 * 4 * 2, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, +}; + +const static struct vt1724_pcm_reg vt1724_playback_spdif_reg = { + .addr = VT1724_MT_PDMA4_ADDR, + .size = VT1724_MT_PDMA4_SIZE, + .count = VT1724_MT_PDMA4_COUNT, + .start = VT1724_PDMA4_START, + .pause = VT1724_PDMA4_PAUSE, +}; + +const static struct vt1724_pcm_reg vt1724_capture_spdif_reg = { + .addr = VT1724_MT_RDMA1_ADDR, + .size = VT1724_MT_RDMA1_SIZE, + .count = VT1724_MT_RDMA1_COUNT, + .start = VT1724_RDMA1_START, + .pause = VT1724_RDMA1_PAUSE, +}; + +static int snd_vt1724_playback_spdif_prepare(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_prepare(substream, &vt1724_playback_spdif_reg); +} + +static snd_pcm_uframes_t snd_vt1724_playback_spdif_pointer(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_pointer(substream, &vt1724_playback_spdif_reg); +} + +static int snd_vt1724_capture_spdif_prepare(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_prepare(substream, &vt1724_capture_spdif_reg); +} + +static snd_pcm_uframes_t snd_vt1724_capture_spdif_pointer(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_pointer(substream, &vt1724_capture_spdif_reg); +} + +static int snd_vt1724_playback_spdif_open(snd_pcm_substream_t *substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ice->playback_con_substream = substream; + runtime->hw = snd_vt1724_playback_spdif; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); + return 0; +} + +static int snd_vt1724_playback_spdif_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->playback_con_substream = NULL; + + return 0; +} + +static int snd_vt1724_capture_spdif_open(snd_pcm_substream_t *substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ice->capture_con_substream = substream; + runtime->hw = snd_vt1724_playback_spdif; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); + return 0; +} + +static int snd_vt1724_capture_spdif_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->capture_con_substream = NULL; + + return 0; +} + +static snd_pcm_ops_t snd_vt1724_playback_spdif_ops = { + .open = snd_vt1724_playback_spdif_open, + .close = snd_vt1724_playback_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_vt1724_pcm_hw_params, + .hw_free = snd_vt1724_pcm_hw_free, + .prepare = snd_vt1724_playback_spdif_prepare, + .trigger = snd_vt1724_pcm_trigger, + .pointer = snd_vt1724_playback_spdif_pointer, +}; + +static snd_pcm_ops_t snd_vt1724_capture_spdif_ops = { + .open = snd_vt1724_capture_spdif_open, + .close = snd_vt1724_capture_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_vt1724_pcm_hw_params, + .hw_free = snd_vt1724_pcm_hw_free, + .prepare = snd_vt1724_capture_spdif_prepare, + .trigger = snd_vt1724_pcm_trigger, + .pointer = snd_vt1724_capture_spdif_pointer, +}; + + +static int __devinit snd_vt1724_pcm_spdif(ice1712_t * ice, int device) +{ + snd_pcm_t *pcm; + int play, capt; + int err; + + if (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_OUT_INT) + play = 1; + else + play = 0; + if (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN) + capt = 1; + else + capt = 0; + if (! play && ! capt) + return 0; /* no spdif device */ + + err = snd_pcm_new(ice->card, "ICE1724 IEC958", device, play, capt, &pcm); + if (err < 0) + return err; + + if (play) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_vt1724_playback_spdif_ops); + if (capt) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_vt1724_capture_spdif_ops); + + pcm->private_data = ice; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1724 IEC958"); + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + + ice->pcm = pcm; + + return 0; +} + + +/* + * Mixer section + */ + +static int __devinit snd_vt1724_ac97_mixer(ice1712_t * ice) +{ + int err; + + if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_vt1724_ac97_write; + ac97.read = snd_vt1724_ac97_read; + ac97.private_data = ice; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) + printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n"); + else + return 0; + } + /* I2S mixer only */ + strcat(ice->card->mixername, "ICE1724 - multitrack"); + return 0; +} + +/* + * + */ + +static inline unsigned int eeprom_triple(ice1712_t *ice, int idx) +{ + return (unsigned int)ice->eeprom.data[idx] | \ + ((unsigned int)ice->eeprom.data[idx + 1] << 8) | \ + ((unsigned int)ice->eeprom.data[idx + 2] << 16); +} + +static void snd_vt1724_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return); + unsigned int idx; + + snd_iprintf(buffer, "%s\n\n", ice->card->longname); + snd_iprintf(buffer, "EEPROM:\n"); + + snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor); + snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size); + snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version); + snd_iprintf(buffer, " System Config : 0x%x\n", ice->eeprom.data[ICE_EEP2_SYSCONF]); + snd_iprintf(buffer, " ACLink : 0x%x\n", ice->eeprom.data[ICE_EEP2_ACLINK]); + snd_iprintf(buffer, " I2S : 0x%x\n", ice->eeprom.data[ICE_EEP2_I2S]); + snd_iprintf(buffer, " S/PDIF : 0x%x\n", ice->eeprom.data[ICE_EEP2_SPDIF]); + snd_iprintf(buffer, " GPIO direction : 0x%x\n", ice->eeprom.gpiodir); + snd_iprintf(buffer, " GPIO mask : 0x%x\n", ice->eeprom.gpiomask); + snd_iprintf(buffer, " GPIO state : 0x%x\n", ice->eeprom.gpiostate); + for (idx = 0x12; idx < ice->eeprom.size; idx++) + snd_iprintf(buffer, " Extra #%02i : 0x%x\n", idx, ice->eeprom.data[idx]); + + snd_iprintf(buffer, "\nRegisters:\n"); + + snd_iprintf(buffer, " PSDOUT03 : 0x%08x\n", (unsigned)inl(ICEMT1724(ice, ROUTE_PLAYBACK))); + for (idx = 0x0; idx < 0x20 ; idx++) + snd_iprintf(buffer, " CCS%02x : 0x%02x\n", idx, inb(ice->port+idx)); + for (idx = 0x0; idx < 0x30 ; idx++) + snd_iprintf(buffer, " MT%02x : 0x%02x\n", idx, inb(ice->profi_port+idx)); +} + +static void __devinit snd_vt1724_proc_init(ice1712_t * ice) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(ice->card, "ice1724", &entry)) + snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read); +} + +/* + * + */ + +static int snd_vt1724_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(ice1712_eeprom_t); + return 0; +} + +static int snd_vt1724_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom)); + return 0; +} + +static snd_kcontrol_new_t snd_vt1724_eeprom __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "ICE1724 EEPROM", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_vt1724_eeprom_info, + .get = snd_vt1724_eeprom_get +}; + +/* + */ +static int snd_vt1724_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static unsigned int encode_spdif_bits(snd_aes_iec958_t *diga) +{ + unsigned int val; + + val = diga->status[0] & 0x03; /* professional, non-audio */ + if (val & 0x01) { + /* professional */ + if ((diga->status[0] & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) + val |= 1U << 3; + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_44100: + break; + case IEC958_AES0_PRO_FS_32000: + val |= 3U << 12; + break; + default: + val |= 2U << 12; + break; + } + } else { + /* consumer */ + val |= diga->status[0] & 0x04; /* copyright */ + if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS)== IEC958_AES0_CON_EMPHASIS_5015) + val |= 1U << 3; + val |= (unsigned int)(diga->status[1] & 0x3f) << 4; /* category */ + val |= (unsigned int)(diga->status[3] & IEC958_AES3_CON_FS) << 12; /* fs */ + } + return val; +} + +static void decode_spdif_bits(snd_aes_iec958_t *diga, unsigned int val) +{ + memset(diga->status, 0, sizeof(diga->status)); + diga->status[0] = val & 0x03; /* professional, non-audio */ + if (val & 0x01) { + /* professional */ + if (val & (1U << 3)) + diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; + switch ((val >> 12) & 0x7) { + case 0: + break; + case 2: + diga->status[0] |= IEC958_AES0_PRO_FS_32000; + break; + default: + diga->status[0] |= IEC958_AES0_PRO_FS_48000; + break; + } + } else { + /* consumer */ + diga->status[0] |= val & (1U << 2); /* copyright */ + if (val & (1U << 3)) + diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; + diga->status[1] |= (val >> 4) & 0x3f; /* category */ + diga->status[3] |= (val >> 12) & 0x07; /* fs */ + } +} + +static int snd_vt1724_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + val = inw(ICEMT1724(ice, SPDIF_CTRL)); + decode_spdif_bits(&ucontrol->value.iec958, val); + return 0; +} + +static int snd_vt1724_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned int val, old; + unsigned long flags; + + val = encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + old = inw(ICEMT1724(ice, SPDIF_CTRL)); + if (val != old) { + unsigned char cbit, disabled; + cbit = inb(ICEREG1724(ice, SPDIF_CFG)); + disabled = cbit & ~VT1724_CFG_SPDIF_OUT_EN; + if (cbit != disabled) + outb(disabled, ICEREG1724(ice, SPDIF_CFG)); + outw(val, ICEMT1724(ice, SPDIF_CTRL)); + if (cbit != disabled) + outb(cbit, ICEREG1724(ice, SPDIF_CFG)); + } + spin_unlock_irqrestore(&ice->reg_lock, flags); + return (val != old); +} + +static snd_kcontrol_new_t snd_vt1724_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_vt1724_spdif_info, + .get = snd_vt1724_spdif_default_get, + .put = snd_vt1724_spdif_default_put +}; + +static int snd_vt1724_spdif_maskc_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_NOT_COPYRIGHT | + IEC958_AES0_CON_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL | + IEC958_AES1_CON_CATEGORY; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + return 0; +} + +static int snd_vt1724_spdif_maskp_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS; + return 0; +} + +static snd_kcontrol_new_t snd_vt1724_spdif_maskc __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_vt1724_spdif_info, + .get = snd_vt1724_spdif_maskc_get, +}; + +static snd_kcontrol_new_t snd_vt1724_spdif_maskp __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_vt1724_spdif_info, + .get = snd_vt1724_spdif_maskp_get, +}; + +static int snd_vt1724_spdif_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_vt1724_spdif_sw_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = inb(ICEREG1724(ice, SPDIF_CFG)) & VT1724_CFG_SPDIF_OUT_EN ? 1 : 0; + return 0; +} + +static int snd_vt1724_spdif_sw_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char old, val; + unsigned long flags; + spin_lock_irqsave(&ice->reg_lock, flags); + old = val = inb(ICEREG1724(ice, SPDIF_CFG)); + val &= ~VT1724_CFG_SPDIF_OUT_EN; + if (ucontrol->value.integer.value[0]) + val |= VT1724_CFG_SPDIF_OUT_EN; + if (old != val) + outb(val, ICEREG1724(ice, SPDIF_CFG)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return old != val; +} + +static snd_kcontrol_new_t snd_vt1724_spdif_switch __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* FIXME: the following conflict with IEC958 Playback Route */ + // .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), + .name = "IEC958 Output Switch", + .info = snd_vt1724_spdif_sw_info, + .get = snd_vt1724_spdif_sw_get, + .put = snd_vt1724_spdif_sw_put +}; + + +#if 0 /* NOT USED YET */ +/* + * GPIO access from extern + */ + +int snd_vt1724_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +int snd_vt1724_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0; + + snd_ice1712_save_gpio_status(ice); + ucontrol->value.integer.value[0] = (snd_ice1712_gpio_read(ice) & (1 << shift) ? 1 : 0) ^ invert; + snd_ice1712_restore_gpio_status(ice); + return 0; +} + +int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? mask : 0; + unsigned int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = (ucontrol->value.integer.value[0] ? (1 << shift) : 0) ^ invert; + snd_ice1712_save_gpio_status(ice); + val = snd_ice1712_gpio_read(ice); + nval |= val & ~(1 << shift); + if (val != nval) + snd_ice1712_gpio_write(ice, nval); + snd_ice1712_restore_gpio_status(ice); + return val != nval; +} +#endif /* NOT USED YET */ + +/* + * rate + */ +static int snd_vt1724_pro_internal_clock_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { + "8000", /* 0: 6 */ + "9600", /* 1: 3 */ + "11025", /* 2: 10 */ + "12000", /* 3: 2 */ + "16000", /* 4: 5 */ + "22050", /* 5: 9 */ + "24000", /* 6: 1 */ + "32000", /* 7: 4 */ + "44100", /* 8: 8 */ + "48000", /* 9: 0 */ + "64000", /* 10: 15 */ + "88200", /* 11: 11 */ + "96000", /* 12: 7 */ + "176400", /* 13: 12 */ + "192000", /* 14: 14 */ + "IEC958 Input", /* 15: -- */ + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 16; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_vt1724_pro_internal_clock_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + static unsigned char xlate[16] = { + 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10 + }; + unsigned char val; + + spin_lock_irq(&ice->reg_lock); + if (is_spdif_master(ice)) { + ucontrol->value.enumerated.item[0] = 15; + } else { + val = xlate[inb(ICEMT1724(ice, RATE)) & 15]; + if (val == 255) { + snd_BUG(); + val = 0; + } + ucontrol->value.enumerated.item[0] = val; + } + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static int snd_vt1724_pro_internal_clock_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char oval; + int change = 0; + + spin_lock_irq(&ice->reg_lock); + oval = inb(ICEMT1724(ice, RATE)); + if (ucontrol->value.enumerated.item[0] == 15) { + outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + } else { + PRO_RATE_DEFAULT = rates[ucontrol->value.integer.value[0] % 15]; + spin_unlock_irq(&ice->reg_lock); + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1); + spin_lock_irq(&ice->reg_lock); + } + change = inb(ICEMT1724(ice, RATE)) != oval; + spin_unlock_irq(&ice->reg_lock); + + if ((oval & VT1724_SPDIF_MASTER) != (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER)) { + /* notify akm chips as well */ + if (is_spdif_master(ice)) { + unsigned int i; + for (i = 0; i < ice->akm_codecs; i++) { + if (ice->akm[i].ops.set_rate_val) + ice->akm[i].ops.set_rate_val(&ice->akm[i], 0); + } + } + } + return change; +} + +static snd_kcontrol_new_t snd_vt1724_pro_internal_clock = __devinitdata { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Internal Clock", + .info = snd_vt1724_pro_internal_clock_info, + .get = snd_vt1724_pro_internal_clock_get, + .put = snd_vt1724_pro_internal_clock_put +}; + +static int snd_vt1724_pro_rate_locking_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_vt1724_pro_rate_locking_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.integer.value[0] = PRO_RATE_LOCKED; + return 0; +} + +static int snd_vt1724_pro_rate_locking_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change = 0, nval; + + nval = ucontrol->value.integer.value[0] ? 1 : 0; + spin_lock_irq(&ice->reg_lock); + change = PRO_RATE_LOCKED != nval; + PRO_RATE_LOCKED = nval; + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_vt1724_pro_rate_locking __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Rate Locking", + .info = snd_vt1724_pro_rate_locking_info, + .get = snd_vt1724_pro_rate_locking_get, + .put = snd_vt1724_pro_rate_locking_put +}; + +static int snd_vt1724_pro_rate_reset_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_vt1724_pro_rate_reset_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.integer.value[0] = PRO_RATE_RESET ? 1 : 0; + return 0; +} + +static int snd_vt1724_pro_rate_reset_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change = 0, nval; + + nval = ucontrol->value.integer.value[0] ? 1 : 0; + spin_lock_irq(&ice->reg_lock); + change = PRO_RATE_RESET != nval; + PRO_RATE_RESET = nval; + spin_unlock_irq(&ice->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_vt1724_pro_rate_reset __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Rate Reset", + .info = snd_vt1724_pro_rate_reset_info, + .get = snd_vt1724_pro_rate_reset_get, + .put = snd_vt1724_pro_rate_reset_put +}; + + +/* + * routing + */ +static int snd_vt1724_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { + "PCM Out", /* 0 */ + "H/W In 0", "H/W In 1", /* 1-2 */ + "IEC958 In L", "IEC958 In R", /* 3-4 */ + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static inline int analog_route_shift(int idx) +{ + return (idx % 2) * 12 + ((idx / 2) * 3) + 8; +} + +static inline int digital_route_shift(int idx) +{ + return idx * 3; +} + +static int get_route_val(ice1712_t *ice, int shift) +{ + unsigned long val; + unsigned char eitem; + static unsigned char xlate[8] = { + 0, 255, 1, 2, 255, 255, 3, 4, + }; + + val = inl(ICEMT1724(ice, ROUTE_PLAYBACK)); + val >>= shift; + val &= 7; //we now have 3 bits per output + eitem = xlate[val]; + if (eitem == 255) { + snd_BUG(); + return 0; + } + return eitem; +} + +static int put_route_val(ice1712_t *ice, unsigned int val, int shift) +{ + unsigned int old_val, nval; + int change; + static unsigned char xroute[8] = { + 0, /* PCM */ + 2, /* PSDIN0 Left */ + 3, /* PSDIN0 Right */ + 6, /* SPDIN Left */ + 7, /* SPDIN Right */ + }; + + nval = xroute[val % 5]; + val = old_val = inl(ICEMT1724(ice, ROUTE_PLAYBACK)); + val &= ~(0x07 << shift); + val |= nval << shift; + change = val != old_val; + if (change) + outl(val, ICEMT1724(ice, ROUTE_PLAYBACK)); + return change; +} + +static int snd_vt1724_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + ucontrol->value.enumerated.item[0] = get_route_val(ice, analog_route_shift(idx)); + return 0; +} + +static int snd_vt1724_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + return put_route_val(ice, ucontrol->value.enumerated.item[0], + analog_route_shift(idx)); +} + +static int snd_vt1724_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + ucontrol->value.enumerated.item[0] = get_route_val(ice, digital_route_shift(idx)); + return 0; +} + +static int snd_vt1724_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + return put_route_val(ice, ucontrol->value.enumerated.item[0], + digital_route_shift(idx)); +} + +static snd_kcontrol_new_t snd_vt1724_mixer_pro_analog_route __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Playback Route", + .info = snd_vt1724_pro_route_info, + .get = snd_vt1724_pro_route_analog_get, + .put = snd_vt1724_pro_route_analog_put, +}; + +static snd_kcontrol_new_t snd_vt1724_mixer_pro_spdif_route __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Route", + .info = snd_vt1724_pro_route_info, + .get = snd_vt1724_pro_route_spdif_get, + .put = snd_vt1724_pro_route_spdif_put, +}; + + +static int snd_vt1724_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 22; /* FIXME: for compatibility with ice1712... */ + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_vt1724_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx; + + spin_lock_irq(&ice->reg_lock); + for (idx = 0; idx < 22; idx++) { + outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX)); + ucontrol->value.integer.value[idx] = inb(ICEMT1724(ice, MONITOR_PEAKDATA)); + } + spin_unlock_irq(&ice->reg_lock); + return 0; +} + +static snd_kcontrol_new_t snd_vt1724_mixer_pro_peak __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Multi Track Peak", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_vt1724_pro_peak_info, + .get = snd_vt1724_pro_peak_get +}; + +/* + * + */ + +static unsigned char __devinit snd_vt1724_read_i2c(ice1712_t *ice, + unsigned char dev, + unsigned char addr) +{ + long t = 0x10000; + + outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR)); + outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR)); + while (t-- > 0 && (inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY)) ; + return inb(ICEREG1724(ice, I2C_DATA)); +} + +static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice) +{ + int dev = 0xa0; /* EEPROM device address */ + unsigned int i; + + if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) == 0) { + snd_printk("ICE1724 has not detected EEPROM\n"); + return -EIO; + } + ice->eeprom.subvendor = (snd_vt1724_read_i2c(ice, dev, 0x00) << 0) | + (snd_vt1724_read_i2c(ice, dev, 0x01) << 8) | + (snd_vt1724_read_i2c(ice, dev, 0x02) << 16) | + (snd_vt1724_read_i2c(ice, dev, 0x03) << 24); + ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04); + if (ice->eeprom.size > 32) { + snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size); + return -EIO; + } + ice->eeprom.version = snd_vt1724_read_i2c(ice, dev, 0x05); + if (ice->eeprom.version != 2) { + snd_printk("invalid EEPROM version %i\n", ice->eeprom.version); + // return -EIO; + } + for (i = 0; i < ice->eeprom.size; i++) + ice->eeprom.data[i] = snd_vt1724_read_i2c(ice, dev, i + 6); + + ice->eeprom.gpiomask = eeprom_triple(ice, ICE_EEP2_GPIO_MASK); + ice->eeprom.gpiostate = eeprom_triple(ice, ICE_EEP2_GPIO_STATE); + ice->eeprom.gpiodir = eeprom_triple(ice, ICE_EEP2_GPIO_DIR); + + return 0; +} + + + +static int __devinit snd_vt1724_chip_init(ice1712_t *ice) +{ + outb(VT1724_RESET , ICEREG1724(ice, CONTROL)); + udelay(200); + outb(0, ICEREG1724(ice, CONTROL)); + udelay(200); + outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG)); + outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG)); + outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES)); + outb(ice->eeprom.data[ICE_EEP2_SPDIF], ICEREG1724(ice, SPDIF_CFG)); + + ice->gpio.write_mask = ice->eeprom.gpiomask; + ice->gpio.direction = ice->eeprom.gpiodir; + snd_vt1724_set_gpio_mask(ice, ice->eeprom.gpiomask); + snd_vt1724_set_gpio_dir(ice, ice->eeprom.gpiodir); + snd_vt1724_set_gpio_data(ice, ice->eeprom.gpiostate); + + outb(0, ICEREG1724(ice, POWERDOWN)); + + return 0; +} + +static int __devinit snd_vt1724_spdif_build_controls(ice1712_t *ice) +{ + int err; + unsigned int idx; + snd_kcontrol_t *kctl; + + snd_assert(ice->pcm != NULL, return -EIO); + + for (idx = 0; idx < 2; idx++) { + kctl = snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice)); + if (err < 0) + return err; + + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm->device; +#if 0 /* use default only */ + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm->device; + ice->spdif.stream_ctl = kctl; +#endif + return 0; +} + + +static int __devinit snd_vt1724_build_controls(ice1712_t *ice) +{ + unsigned int idx; + snd_kcontrol_t *kctl; + int err; + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_eeprom, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_internal_clock, ice)); + if (err < 0) + return err; + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_rate_locking, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_rate_reset, ice)); + if (err < 0) + return err; + + for (idx = 0; idx < ice->num_total_dacs; idx++) { + kctl = snd_ctl_new1(&snd_vt1724_mixer_pro_analog_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice)); + if (err < 0) + return err; + + return 0; +} + +static int snd_vt1724_free(ice1712_t *ice) +{ + if (ice->res_port == NULL) + goto __hw_end; + /* mask all interrupts */ + outb(0xff, ICEMT1724(ice, DMA_INT_MASK)); + outb(0xff, ICEREG1724(ice, IRQMASK)); + /* --- */ + __hw_end: + if (ice->irq >= 0) { + synchronize_irq(ice->irq); + free_irq(ice->irq, (void *) ice); + } + if (ice->res_port) { + release_resource(ice->res_port); + kfree_nocheck(ice->res_port); + } + if (ice->res_profi_port) { + release_resource(ice->res_profi_port); + kfree_nocheck(ice->res_profi_port); + } + if (ice->akm) + kfree(ice->akm); + snd_magic_kfree(ice); + return 0; +} + +static int snd_vt1724_dev_free(snd_device_t *device) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO); + return snd_vt1724_free(ice); +} + +static int __devinit snd_vt1724_create(snd_card_t * card, + struct pci_dev *pci, + ice1712_t ** r_ice1712) +{ + ice1712_t *ice; + int err; + unsigned char mask; + static snd_device_ops_t ops = { + .dev_free = snd_vt1724_dev_free, + }; + + *r_ice1712 = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + pci_set_dma_mask(pci, 0xffffffff); + + ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL); + if (ice == NULL) + return -ENOMEM; + ice->vt1724 = 1; + spin_lock_init(&ice->reg_lock); + init_MUTEX(&ice->gpio_mutex); + ice->gpio.set_mask = snd_vt1724_set_gpio_mask; + ice->gpio.set_dir = snd_vt1724_set_gpio_dir; + ice->gpio.set_data = snd_vt1724_set_gpio_data; + ice->gpio.get_data = snd_vt1724_get_gpio_data; + ice->card = card; + ice->pci = pci; + ice->irq = -1; + ice->port = pci_resource_start(pci, 0); + ice->profi_port = pci_resource_start(pci, 1); + pci_set_master(pci); + snd_vt1724_proc_init(ice); + synchronize_irq(pci->irq); + + if ((ice->res_port = request_region(ice->port, 32, "ICE1724 - Controller")) == NULL) { + snd_vt1724_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1); + return -EIO; + } + + if ((ice->res_profi_port = request_region(ice->profi_port, 128, "ICE1724 - Professional")) == NULL) { + snd_vt1724_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1); + return -EIO; + } + + if (request_irq(pci->irq, snd_vt1724_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1724", (void *) ice)) { + snd_vt1724_free(ice); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EIO; + } + + ice->irq = pci->irq; + + if (snd_vt1724_read_eeprom(ice) < 0) { + snd_vt1724_free(ice); + return -EIO; + } + if (snd_vt1724_chip_init(ice) < 0) { + snd_vt1724_free(ice); + return -EIO; + } + + /* unmask used interrupts */ + if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401)) + mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX; + else + mask = 0; + outb(mask, ICEREG1724(ice, IRQMASK)); + /* don't handle FIFO overrun/underruns (just yet), since they cause machine lockups */ + outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) { + snd_vt1724_free(ice); + return err; + } + + *r_ice1712 = ice; + return 0; +} + + +/* + * + * Registration + * + */ + +static struct snd_ice1712_card_info no_matched __devinitdata; + +static struct snd_ice1712_card_info *card_tables[] __devinitdata = { + snd_vt1724_revo_cards, + snd_vt1724_amp_cards, + 0, +}; + + +static int __devinit snd_vt1724_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + ice1712_t *ice; + int pcm_dev = 0, err; + struct snd_ice1712_card_info **tbl, *c; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "ICE1724"); + strcpy(card->shortname, "ICEnsemble ICE1724"); + + if ((err = snd_vt1724_create(card, pci, &ice)) < 0) { + snd_card_free(card); + return err; + } + + for (tbl = card_tables; *tbl; tbl++) { + for (c = *tbl; c->subvendor; c++) { + if (c->subvendor == ice->eeprom.subvendor) { + strcpy(card->shortname, c->name); + if (c->chip_init) { + if ((err = c->chip_init(ice)) < 0) { + snd_card_free(card); + return err; + } + } + goto __found; + } + } + } + c = &no_matched; + __found: + + if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_vt1724_pcm_spdif(ice, pcm_dev++)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_vt1724_ac97_mixer(ice)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_vt1724_build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + + if (ice->pcm) { /* has SPDIF I/O */ + if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + } + + if (c->build_controls) { + if ((err = c->build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + } + + if (! c->no_mpu401) { + if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, + ICEREG1724(ice, MPU_CTRL), 1, + ice->irq, 0, + &ice->rmidi[0])) < 0) { + snd_card_free(card); + return err; + } + } + } + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, ice->port, ice->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_vt1724_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ICE1724", + .id_table = snd_vt1724_ids, + .probe = snd_vt1724_probe, + .remove = __devexit_p(snd_vt1724_remove), +}; + +static int __init alsa_card_ice1724_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ICE1724 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ice1724_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ice1724_init) +module_exit(alsa_card_ice1724_exit) + +#ifndef MODULE + +/* format is: snd-ice1724=enable,index,id */ + +static int __init alsa_card_ice1724_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ice1724=", alsa_card_ice1724_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/revo.c linux/sound/pci/ice1712/revo.c --- linux-2.4.21-rc1.orig/sound/pci/ice1712/revo.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/revo.c 2003-02-25 08:05:08.000000000 -0700 @@ -0,0 +1,169 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for M-Audio Revolution 7.1 + * + * Copyright (c) 2003 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "envy24ht.h" +#include "revo.h" + +/* + * change the rate of envy24HT, AK4355 and AK4381 + */ +static void revo_set_rate_val(akm4xxx_t *ak, unsigned int rate) +{ + unsigned char old, tmp, dfs; + int reg, shift; + + if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + return; + + /* adjust DFS on codecs */ + if (rate > 96000) + dfs = 2; + else if (rate > 48000) + dfs = 1; + else + dfs = 0; + + if (ak->type == SND_AK4355) { + reg = 2; + shift = 4; + } else { + reg = 1; + shift = 3; + } + tmp = ak->images[0][reg]; + old = (tmp >> shift) & 0x03; + if (old == dfs) + return; + + /* reset DFS */ + snd_ice1712_akm4xxx_reset(ak, 1); + tmp = ak->images[0][reg]; + tmp &= ~(0x03 << shift); + tmp |= dfs << shift; + snd_ice1712_akm4xxx_write(ak, 0, reg, tmp); + snd_ice1712_akm4xxx_reset(ak, 0); +} + +/* + * initialize the chips on M-Audio Revolution cards + */ + +static akm4xxx_t akm_revo_front __devinitdata = { + .type = SND_AK4381, + .num_dacs = 2, + .caddr = 1, + .cif = 0, + .data_mask = VT1724_REVO_CDOUT, + .clk_mask = VT1724_REVO_CCLK, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .add_flags = VT1724_REVO_CCLK, /* high at init */ + .mask_flags = 0, + .ops = { + .set_rate_val = revo_set_rate_val + } +}; + +static akm4xxx_t akm_revo_surround __devinitdata = { + .type = SND_AK4355, + .idx_offset = 1, + .num_dacs = 6, + .caddr = 3, + .cif = 0, + .data_mask = VT1724_REVO_CDOUT, + .clk_mask = VT1724_REVO_CCLK, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS1, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .add_flags = VT1724_REVO_CCLK, /* high at init */ + .mask_flags = 0, + .ops = { + .set_rate_val = revo_set_rate_val + } +}; + +static int __devinit revo_init(ice1712_t *ice) +{ + akm4xxx_t *ak; + + /* determine I2C, DACs and ADCs */ + switch (ice->eeprom.subvendor) { + case VT1724_SUBDEVICE_REVOLUTION71: + ice->num_total_dacs = 8; + break; + default: + snd_BUG(); + return -EINVAL; + } + + /* second stage of initialization, analog parts and others */ + ak = ice->akm = kmalloc(sizeof(akm4xxx_t) * 2, GFP_KERNEL); + if (! ak) + return -ENOMEM; + ice->akm_codecs = 2; + switch (ice->eeprom.subvendor) { + case VT1724_SUBDEVICE_REVOLUTION71: + snd_ice1712_akm4xxx_init(ak, &akm_revo_front, ice); + snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, ice); + /* unmute all codecs */ + snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); + break; + } + + return 0; +} + + +static int __devinit revo_add_controls(ice1712_t *ice) +{ + int err; + + switch (ice->eeprom.subvendor) { + case VT1724_SUBDEVICE_REVOLUTION71: + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; + } + return 0; +} + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = { + { + VT1724_SUBDEVICE_REVOLUTION71, + "M Audio Revolution-7.1", + revo_init, + revo_add_controls, + }, + { } /* terminator */ +}; diff -urN linux-2.4.21-rc1.orig/sound/pci/ice1712/revo.h linux/sound/pci/ice1712/revo.h --- linux-2.4.21-rc1.orig/sound/pci/ice1712/revo.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ice1712/revo.h 2003-02-25 08:05:11.000000000 -0700 @@ -0,0 +1,48 @@ +#ifndef __SOUND_REVO_H +#define __SOUND_REVO_H + +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Lowlevel functions for M-Audio Revolution 7.1 + * + * Copyright (c) 2003 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define REVO_DEVICE_DESC \ + "{MidiMan M Audio,Revolution 7.1}," + +#define VT1724_SUBDEVICE_REVOLUTION71 0x12143036 + +/* entry point */ +extern struct snd_ice1712_card_info snd_vt1724_revo_cards[]; + + +/* + * MidiMan M-Audio Revolution GPIO definitions + */ + +#define VT1724_REVO_CCLK 0x02 +#define VT1724_REVO_CDIN 0x04 /* not used */ +#define VT1724_REVO_CDOUT 0x08 +#define VT1724_REVO_CS0 0x10 /* not used */ +#define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */ +#define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */ +#define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */ + +#endif /* __SOUND_REVO_H */ diff -urN linux-2.4.21-rc1.orig/sound/pci/intel8x0.c linux/sound/pci/intel8x0.c --- linux-2.4.21-rc1.orig/sound/pci/intel8x0.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/intel8x0.c 2003-03-10 09:08:16.000000000 -0700 @@ -0,0 +1,2600 @@ +/* + * ALSA driver for Intel ICH (i8x0) chipsets + * + * Copyright (c) 2000 Jaroslav Kysela + * + * + * This code also contains alpha support for SiS 735 chipsets provided + * by Mike Pieper . We have no datasheet + * for SiS735, so the code is not fully functional. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Intel,82801AA-ICH}," + "{Intel,82901AB-ICH0}," + "{Intel,82801BA-ICH2}," + "{Intel,82801CA-ICH3}," + "{Intel,82801DB-ICH4}," + "{Intel,ICH5}," + "{Intel,MX440}," + "{SiS,SI7012}," + "{NVidia,NForce Audio}," + "{NVidia,NForce2 Audio}," + "{AMD,AMD768}," + "{AMD,AMD8111}," + "{ALI,M5455}}"); + +#define SUPPORT_JOYSTICK 1 +#define SUPPORT_MIDI 1 + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +#ifdef SUPPORT_JOYSTICK +static int joystick_port[SNDRV_CARDS] = +#ifdef CONFIG_ISA + {0x200}; /* enable as default */ +#else + {0}; /* disabled */ +#endif +#endif +#ifdef SUPPORT_MIDI +static int mpu_port[SNDRV_CARDS]; /* disabled */ +#endif + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect)."); +MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:0"); +#ifdef SUPPORT_JOYSTICK +MODULE_PARM(joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(joystick_port, "Joystick port address for Intel i8x0 soundcard. (0 = disabled)"); +MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED ",allows:{{0},{0x200}},dialog:list"); +#endif +#ifdef SUPPORT_MIDI +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mpu_port, "MPU401 port # for Intel i8x0 driver."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog:list"); +#endif + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_INTEL_82801 +#define PCI_DEVICE_ID_INTEL_82801 0x2415 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82901 +#define PCI_DEVICE_ID_INTEL_82901 0x2425 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801BA +#define PCI_DEVICE_ID_INTEL_82801BA 0x2445 +#endif +#ifndef PCI_DEVICE_ID_INTEL_440MX +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH3 +#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH4 +#define PCI_DEVICE_ID_INTEL_ICH4 0x24c5 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH5 +#define PCI_DEVICE_ID_INTEL_ICH5 0x24d5 +#endif +#ifndef PCI_DEVICE_ID_SI_7012 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1 +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#endif + +enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI }; + +#define ICHREG(x) ICH_REG_##x + +#define DEFINE_REGSET(name,base) \ +enum { \ + ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \ + ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \ + ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \ + ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \ + ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \ + ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \ + ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \ +}; + +/* busmaster blocks */ +DEFINE_REGSET(OFF, 0); /* offset */ +DEFINE_REGSET(PI, 0x00); /* PCM in */ +DEFINE_REGSET(PO, 0x10); /* PCM out */ +DEFINE_REGSET(MC, 0x20); /* Mic in */ + +/* ICH4 busmaster blocks */ +DEFINE_REGSET(MC2, 0x40); /* Mic in 2 */ +DEFINE_REGSET(PI2, 0x50); /* PCM in 2 */ +DEFINE_REGSET(SP, 0x60); /* SPDIF out */ + +/* values for each busmaster block */ + +/* LVI */ +#define ICH_REG_LVI_MASK 0x1f + +/* SR */ +#define ICH_FIFOE 0x10 /* FIFO error */ +#define ICH_BCIS 0x08 /* buffer completion interrupt status */ +#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */ +#define ICH_CELV 0x02 /* current equals last valid */ +#define ICH_DCH 0x01 /* DMA controller halted */ + +/* PIV */ +#define ICH_REG_PIV_MASK 0x1f /* mask */ + +/* CR */ +#define ICH_IOCE 0x10 /* interrupt on completion enable */ +#define ICH_FEIE 0x08 /* fifo error interrupt enable */ +#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */ +#define ICH_RESETREGS 0x02 /* reset busmaster registers */ +#define ICH_STARTBM 0x01 /* start busmaster operation */ + + +/* global block */ +#define ICH_REG_GLOB_CNT 0x2c /* dword - global control */ +#define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */ +#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */ +#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */ +#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */ +#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */ +#define ICH_SIS_PCM_246_MASK 0x000000c0 /* 6 channels (SIS7012) */ +#define ICH_SIS_PCM_6 0x00000080 /* 6 channels (SIS7012) */ +#define ICH_SIS_PCM_4 0x00000040 /* 4 channels (SIS7012) */ +#define ICH_SIS_PCM_2 0x00000000 /* 2 channels (SIS7012) */ +#define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */ +#define ICH_PRIE 0x00000010 /* primary resume interrupt enable */ +#define ICH_ACLINK 0x00000008 /* AClink shut off */ +#define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */ +#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */ +#define ICH_GIE 0x00000001 /* GPI interrupt enable */ +#define ICH_REG_GLOB_STA 0x30 /* dword - global status */ +#define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */ +#define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */ +#define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */ +#define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */ +#define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */ +#define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */ +#define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */ +#define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */ +#define ICH_MD3 0x00020000 /* modem power down semaphore */ +#define ICH_AD3 0x00010000 /* audio power down semaphore */ +#define ICH_RCS 0x00008000 /* read completion status */ +#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */ +#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */ +#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */ +#define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */ +#define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */ +#define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */ +#define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */ +#define ICH_MCINT 0x00000080 /* MIC capture interrupt */ +#define ICH_POINT 0x00000040 /* playback interrupt */ +#define ICH_PIINT 0x00000020 /* capture interrupt */ +#define ICH_MOINT 0x00000004 /* modem playback interrupt */ +#define ICH_MIINT 0x00000002 /* modem capture interrupt */ +#define ICH_GSCI 0x00000001 /* GPI status change interrupt */ +#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */ +#define ICH_CAS 0x01 /* codec access semaphore */ +#define ICH_REG_SDM 0x80 +#define ICH_DI2L_MASK 0x000000c0 /* PCM In 2, Mic In 2 data in line */ +#define ICH_DI2L_SHIFT 6 +#define ICH_DI1L_MASK 0x00000030 /* PCM In 1, Mic In 1 data in line */ +#define ICH_DI1L_SHIFT 4 +#define ICH_SE 0x00000008 /* steer enable */ +#define ICH_LDI_MASK 0x00000003 /* last codec read data input */ + +#define ICH_MAX_FRAGS 32 /* max hw frags */ + + +/* + * registers for Ali5455 + */ + +/* ALi 5455 busmaster blocks */ +DEFINE_REGSET(AL_PI, 0x40); /* ALi PCM in */ +DEFINE_REGSET(AL_PO, 0x50); /* Ali PCM out */ +DEFINE_REGSET(AL_MC, 0x60); /* Ali Mic in */ +DEFINE_REGSET(AL_CDC_SPO, 0x70); /* Ali Codec SPDIF out */ +DEFINE_REGSET(AL_CLR_SPI, 0xa0); /* Ali Controller SPDIF in */ +DEFINE_REGSET(AL_CLR_SPO, 0xb0); /* Ali Controller SPDIF out */ + +enum { + ICH_REG_ALI_SCR = 0x00, /* System Control Register */ + ICH_REG_ALI_SSR = 0x04, /* System Status Register */ + ICH_REG_ALI_DMACR = 0x08, /* DMA Control Register */ + ICH_REG_ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ + ICH_REG_ALI_INTERFACECR = 0x10, /* Interface Control Register */ + ICH_REG_ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ + ICH_REG_ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ + ICH_REG_ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ + ICH_REG_ALI_CPR = 0x20, /* Command Port Register */ + ICH_REG_ALI_SPR = 0x24, /* Status Port Register */ + ICH_REG_ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ + ICH_REG_ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ + ICH_REG_ALI_RTSR = 0x34, /* Receive Tag Slot Register */ + ICH_REG_ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ + ICH_REG_ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ + ICH_REG_ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ + ICH_REG_ALI_SPDIFICS = 0xfc /* spdif interface control/status */ +}; + +/* interrupts for the whole chip by interrupt status register finish */ + +#define ALI_INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */ +#define ALI_INT_SPDIFIN (1<<22) +#define ALI_INT_CODECSPDIFOUT (1<<19) +#define ALI_INT_MICIN (1<<18) +#define ALI_INT_PCMOUT (1<<17) +#define ALI_INT_PCMIN (1<<16) +#define ALI_INT_CPRAIS (1<<7) +#define ALI_INT_SPRAIS (1<<5) +#define ALI_INT_GPIO (1<<1) +#define ALI_INT_MASK (ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN) + +#define ALI_PCM_CH4 0x100 +#define ALI_PCM_CH6 0x200 +#define ALI_PCM_MASK (ALI_PCM_CH4 | ALI_PCM_CH6) + +/* + * + */ + +enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR }; +enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT }; + +#define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data) + +typedef struct { + unsigned int ichd; /* ich device number */ + unsigned long reg_offset; /* offset to bmaddr */ + u32 *bdbar; /* CPU address (32bit) */ + unsigned int bdbar_addr; /* PCI bus address (32bit) */ + snd_pcm_substream_t *substream; + unsigned int physbuf; /* physical address (32bit) */ + unsigned int size; + unsigned int fragsize; + unsigned int fragsize1; + unsigned int position; + int frags; + int lvi; + int lvi_frag; + int ack; + int ack_reload; + unsigned int ack_bit; + unsigned int roff_sr; + unsigned int roff_picb; + unsigned int int_sta_mask; /* interrupt status mask */ + unsigned int ali_slot; /* ALI DMA slot */ + ac97_t *ac97; + unsigned short ac97_rate_regs[3]; + int ac97_rates_idx; +#ifdef CONFIG_PM + unsigned char civ_saved; + unsigned char piv_saved; + unsigned short picb_saved; +#endif +} ichdev_t; + +typedef struct _snd_intel8x0 intel8x0_t; +#define chip_t intel8x0_t + +struct _snd_intel8x0 { + unsigned int device_type; + char ac97_name[32]; + char ctrl_name[32]; + + int irq; + + unsigned int mmio; + unsigned long addr; + unsigned long remap_addr; + struct resource *res; + unsigned int bm_mmio; + unsigned long bmaddr; + unsigned long remap_bmaddr; + struct resource *res_bm; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + snd_pcm_t *pcm_mic2; + snd_pcm_t *pcm2; + snd_pcm_t *pcm_spdif; + snd_pcm_t *pcm_ac97spdif; + ichdev_t ichd[6]; + + int multi4: 1, + multi6: 1, + smp20bit: 1; + int in_ac97_init: 1, + in_sdin_init: 1; + + ac97_t *ac97[3]; + unsigned int ac97_sdin[3]; + + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + spinlock_t ac97_lock; + + u32 bdbars_count; + u32 *bdbars; + dma_addr_t bdbars_addr; + u32 int_sta_reg; /* interrupt status register */ + u32 int_sta_mask; /* interrupt status mask */ + +#ifdef CONFIG_PM + int in_suspend; +#endif +}; + +static struct pci_device_id snd_intel8x0_ids[] __devinitdata = { + { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */ + { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ + { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ + { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ + { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */ + { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH5 */ + { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ + { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ + { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */ + { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE2 */ + { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE3 */ + { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ + { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ + { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids); + +/* + * Lowlevel I/O - busmaster + */ + +static u8 igetbyte(intel8x0_t *chip, u32 offset) +{ + if (chip->bm_mmio) + return readb(chip->remap_bmaddr + offset); + else + return inb(chip->bmaddr + offset); +} + +static u16 igetword(intel8x0_t *chip, u32 offset) +{ + if (chip->bm_mmio) + return readw(chip->remap_bmaddr + offset); + else + return inw(chip->bmaddr + offset); +} + +static u32 igetdword(intel8x0_t *chip, u32 offset) +{ + if (chip->bm_mmio) + return readl(chip->remap_bmaddr + offset); + else + return inl(chip->bmaddr + offset); +} + +static void iputbyte(intel8x0_t *chip, u32 offset, u8 val) +{ + if (chip->bm_mmio) + writeb(val, chip->remap_bmaddr + offset); + else + outb(val, chip->bmaddr + offset); +} + +static void iputword(intel8x0_t *chip, u32 offset, u16 val) +{ + if (chip->bm_mmio) + writew(val, chip->remap_bmaddr + offset); + else + outw(val, chip->bmaddr + offset); +} + +static void iputdword(intel8x0_t *chip, u32 offset, u32 val) +{ + if (chip->bm_mmio) + writel(val, chip->remap_bmaddr + offset); + else + outl(val, chip->bmaddr + offset); +} + +/* + * Lowlevel I/O - AC'97 registers + */ + +static u16 iagetword(intel8x0_t *chip, u32 offset) +{ + if (chip->mmio) + return readw(chip->remap_addr + offset); + else + return inw(chip->addr + offset); +} + +static void iaputword(intel8x0_t *chip, u32 offset, u16 val) +{ + if (chip->mmio) + writew(val, chip->remap_addr + offset); + else + outw(val, chip->addr + offset); +} + +/* + * Basic I/O + */ + +/* + * access to AC97 codec via normal i/o (for ICH and SIS7012) + */ +static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec) +{ + int time; + + if (codec > 2) + return -EIO; + if (chip->in_sdin_init) { + /* we don't know the ready bit assignment at the moment */ + /* so we check any */ + codec = ICH_PCR | ICH_SCR | ICH_TCR; + } else { + if (chip->device_type == DEVICE_INTEL_ICH4) { + switch (chip->ac97_sdin[codec]) { + case 0: codec = ICH_PCR; break; + case 1: codec = ICH_SCR; break; + case 2: codec = ICH_TCR; break; + } + } else { + switch (codec) { + case 0: codec = ICH_PCR; break; + case 1: codec = ICH_SCR; break; + case 2: codec = ICH_TCR; break; + } + } + } + + /* codec ready ? */ + if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0) + return -EIO; + + /* Anyone holding a semaphore for 1 msec should be shot... */ + time = 100; + do { + if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS)) + return 0; + udelay(10); + } while (time--); + + /* access to some forbidden (non existant) ac97 registers will not + * reset the semaphore. So even if you don't get the semaphore, still + * continue the access. We don't need the semaphore anyway. */ + snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n", + igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA))); + iagetword(chip, 0); /* clear semaphore flag */ + /* I don't care about the semaphore */ + return -EBUSY; +} + +static void snd_intel8x0_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { + if (! chip->in_ac97_init) + snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); + } + iaputword(chip, reg + ac97->num * 0x80, val); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, + unsigned short reg) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + unsigned short res; + unsigned int tmp; + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { + if (! chip->in_ac97_init) + snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); + res = 0xffff; + } else { + res = iagetword(chip, reg + ac97->num * 0x80); + if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) { + /* reset RCS and preserve other R/WC bits */ + iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI)); + if (! chip->in_ac97_init) + snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg); + res = 0xffff; + } + } + spin_unlock(&chip->ac97_lock); + return res; +} + +/* + * access to AC97 for Ali5455 + */ +static int snd_intel8x0_ali_codec_ready(intel8x0_t *chip, int mask) +{ + int count = 0; + for (count = 0; count < 0x7f; count++) { + int val = igetbyte(chip, ICHREG(ALI_CSPSR)); + if (val & mask) + return 0; + } + snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n"); + return -EBUSY; +} + +static int snd_intel8x0_ali_codec_semaphore(intel8x0_t *chip) +{ + int time = 100; + do { + if (igetdword(chip, ICHREG(ALI_CAS)) & 0x80000000) + return snd_intel8x0_ali_codec_ready(chip, 0x08); + udelay(1); + } while (time--); + snd_printk(KERN_WARNING "intel8x0: AC97 codec semaphore timeout.\n"); + return -EBUSY; +} + +static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + unsigned short data, reg2; + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_ali_codec_semaphore(chip)) + goto __err; + reg |= 0x0080; + iputword(chip, ICHREG(ALI_CPR) + 2, reg | 0x0080); + if (snd_intel8x0_ali_codec_ready(chip, 0x02)) + goto __err; + data = igetword(chip, ICHREG(ALI_SPR)); + reg2 = igetword(chip, ICHREG(ALI_SPR) + 2); + if (reg != reg2) { + snd_printd(KERN_WARNING "intel8x0: AC97 read not completed?\n"); + goto __err; + } + spin_unlock(&chip->ac97_lock); + return data; + __err: + spin_unlock(&chip->ac97_lock); + return 0xffff; +} + +static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_ali_codec_semaphore(chip)) { + spin_unlock(&chip->ac97_lock); + return; + } + iputword(chip, ICHREG(ALI_CPR), val); + iputbyte(chip, ICHREG(ALI_CPR) + 2, reg); + snd_intel8x0_ali_codec_ready(chip, 0x01); + spin_unlock(&chip->ac97_lock); +} + + +/* + * DMA I/O + */ +static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) +{ + int idx; + u32 *bdbar = ichdev->bdbar; + unsigned long port = ichdev->reg_offset; + int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1; + + iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr); + if (ichdev->size == ichdev->fragsize) { + ichdev->ack_reload = ichdev->ack = 2; + ichdev->fragsize1 = ichdev->fragsize >> 1; + for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) { + bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf); + bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize1 >> shiftlen); + bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1)); + bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize1 >> shiftlen); + } + ichdev->frags = 2; + } else { + ichdev->ack_reload = ichdev->ack = 1; + ichdev->fragsize1 = ichdev->fragsize; + for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) { + bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size)); + bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize >> shiftlen); + // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]); + } + ichdev->frags = ichdev->size / ichdev->fragsize; + } + iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK); + ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags; + ichdev->position = 0; +#if 0 + printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n", + ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1); +#endif + /* clear interrupts */ + iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI); +} + +/* + * Interrupt handler + */ + +static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev) +{ + unsigned long port = ichdev->reg_offset; + int ack = 0; + + spin_lock(&chip->reg_lock); + ichdev->position += ichdev->fragsize1; + ichdev->position %= ichdev->size; + ichdev->lvi++; + ichdev->lvi &= ICH_REG_LVI_MASK; + iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi); + ichdev->lvi_frag++; + ichdev->lvi_frag %= ichdev->frags; + ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1); + // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR)); + if ((ack = (--ichdev->ack == 0)) != 0) + ichdev->ack = ichdev->ack_reload; + spin_unlock(&chip->reg_lock); + if (ack && ichdev->substream) + snd_pcm_period_elapsed(ichdev->substream); + iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI); +} + +static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return); + ichdev_t *ichdev; + unsigned int status; + unsigned int i; + + spin_lock(&chip->reg_lock); + status = igetdword(chip, chip->int_sta_reg); + if ((status & chip->int_sta_mask) == 0) { + spin_unlock(&chip->reg_lock); + return; + } + /* ack first */ + iputdword(chip, chip->int_sta_reg, status & ~chip->int_sta_mask); + spin_unlock(&chip->reg_lock); + + for (i = 0; i < chip->bdbars_count; i++) { + ichdev = &chip->ichd[i]; + if (status & ichdev->int_sta_mask) + snd_intel8x0_update(chip, ichdev); + } +} + +/* + * PCM part + */ + +static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + ichdev_t *ichdev = get_ichdev(substream); + unsigned char val = 0; + unsigned long port = ichdev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + val = ICH_IOCE | ICH_STARTBM; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + val = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = ICH_IOCE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = ICH_IOCE | ICH_STARTBM; + break; + default: + return -EINVAL; + } + iputbyte(chip, port + ICH_REG_OFF_CR, val); + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + /* reset whole DMA things */ + while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ; + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); + } + return 0; +} + +static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + ichdev_t *ichdev = get_ichdev(substream); + unsigned long port = ichdev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); + iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8)); + iputbyte(chip, port + ICH_REG_OFF_CR, 0); + /* reset whole DMA things */ + while (!(igetbyte(chip, port + ICH_REG_OFF_CR))) + ; + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); + /* clear interrupts */ + iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e); + iputdword(chip, ICHREG(ALI_INTERRUPTSR), + igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & (1 << (ichdev->ali_slot + 8))); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + iputbyte(chip, port + ICH_REG_OFF_CR, 0); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels) +{ + unsigned int cnt = igetdword(chip, ICHREG(GLOB_CNT)); + if (chip->device_type == DEVICE_SIS) { + cnt &= ~ICH_SIS_PCM_246_MASK; + if (chip->multi4 && channels == 4) + cnt |= ICH_SIS_PCM_4; + else if (chip->multi6 && channels == 6) + cnt |= ICH_SIS_PCM_6; + } else { + cnt &= ~ICH_PCM_246_MASK; + if (chip->multi4 && channels == 4) + cnt |= ICH_PCM_4; + else if (chip->multi6 && channels == 6) + cnt |= ICH_PCM_6; + } + iputdword(chip, ICHREG(GLOB_CNT), cnt); +} + +static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ichdev_t *ichdev = get_ichdev(substream); + int i; + + ichdev->physbuf = runtime->dma_addr; + ichdev->size = snd_pcm_lib_buffer_bytes(substream); + ichdev->fragsize = snd_pcm_lib_period_bytes(substream); + if (ichdev->ichd == ICHD_PCMOUT && chip->device_type != DEVICE_ALI) { + spin_lock(&chip->reg_lock); + snd_intel8x0_setup_multi_channels(chip, runtime->channels); + spin_unlock(&chip->reg_lock); + } + for (i = 0; i < 3; i++) + if (ichdev->ac97_rate_regs[i]) + snd_ac97_set_rate(ichdev->ac97, ichdev->ac97_rate_regs[i], runtime->rate); + snd_intel8x0_setup_periods(chip, ichdev); + return 0; +} + +static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + ichdev_t *ichdev = get_ichdev(substream); + size_t ptr; + + ptr = ichdev->fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb); + else + ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1; + ptr += ichdev->position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_intel8x0_stream = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 128 * 1024, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static unsigned int channels4[] = { + 2, 4, +}; + +#define CHANNELS4 sizeof(channels4) / sizeof(channels4[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = { + .count = CHANNELS4, + .list = channels4, + .mask = 0, +}; + +static unsigned int channels6[] = { + 2, 4, 6, +}; + +#define CHANNELS6 sizeof(channels6) / sizeof(channels6[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = { + .count = CHANNELS6, + .list = channels6, + .mask = 0, +}; + +static int snd_intel8x0_pcm_open(snd_pcm_substream_t * substream, ichdev_t *ichdev) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + ichdev->substream = substream; + runtime->hw = snd_intel8x0_stream; + if (ichdev->ac97_rates_idx >= 0) + runtime->hw.rates = ichdev->ac97->rates[ichdev->ac97_rates_idx]; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + runtime->private_data = ichdev; + return 0; +} + +static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]); + if (chip->multi6) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6); + } else if (chip->multi4) { + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4); + } + return 0; +} + +static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ICHD_PCMOUT].substream = NULL; + return 0; +} + +static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]); +} + +static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ICHD_PCMIN].substream = NULL; + return 0; +} + +static int snd_intel8x0_mic_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]); +} + +static int snd_intel8x0_mic_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ICHD_MIC].substream = NULL; + return 0; +} + +static int snd_intel8x0_mic2_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]); +} + +static int snd_intel8x0_mic2_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ICHD_MIC2].substream = NULL; + return 0; +} + +static int snd_intel8x0_capture2_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]); +} + +static int snd_intel8x0_capture2_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ICHD_PCM2IN].substream = NULL; + return 0; +} + +static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_SPBAR]); +} + +static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ICHD_SPBAR].substream = NULL; + return 0; +} + +static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]); +} + +static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ALID_AC97SPDIFOUT].substream = NULL; + return 0; +} + +static int snd_intel8x0_ali_spdifin_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]); +} + +static int snd_intel8x0_ali_spdifin_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ALID_SPDIFIN].substream = NULL; + return 0; +} + +static int snd_intel8x0_ali_spdifout_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]); +} + +static int snd_intel8x0_ali_spdifout_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->ichd[ALID_SPDIFOUT].substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_intel8x0_playback_ops = { + .open = snd_intel8x0_playback_open, + .close = snd_intel8x0_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_capture_ops = { + .open = snd_intel8x0_capture_open, + .close = snd_intel8x0_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = { + .open = snd_intel8x0_mic_open, + .close = snd_intel8x0_mic_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_capture_mic2_ops = { + .open = snd_intel8x0_mic2_open, + .close = snd_intel8x0_mic2_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_capture2_ops = { + .open = snd_intel8x0_capture2_open, + .close = snd_intel8x0_capture2_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_spdif_ops = { + .open = snd_intel8x0_spdif_open, + .close = snd_intel8x0_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_ali_playback_ops = { + .open = snd_intel8x0_playback_open, + .close = snd_intel8x0_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_ali_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_ali_capture_ops = { + .open = snd_intel8x0_capture_open, + .close = snd_intel8x0_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_ali_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_ali_capture_mic_ops = { + .open = snd_intel8x0_mic_open, + .close = snd_intel8x0_mic_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_ali_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_ali_ac97spdifout_ops = { + .open = snd_intel8x0_ali_ac97spdifout_open, + .close = snd_intel8x0_ali_ac97spdifout_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_ali_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_ali_spdifin_ops = { + .open = snd_intel8x0_ali_spdifin_open, + .close = snd_intel8x0_ali_spdifin_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_ali_spdifout_ops = { + .open = snd_intel8x0_ali_spdifout_open, + .close = snd_intel8x0_ali_spdifout_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intel8x0_hw_params, + .hw_free = snd_intel8x0_hw_free, + .prepare = snd_intel8x0_pcm_prepare, + .trigger = snd_intel8x0_pcm_trigger, + .pointer = snd_intel8x0_pcm_pointer, +}; + +static void snd_intel8x0_pcm_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH", device, 1, 1, &pcm); + if (err < 0) + return err; + + if (chip->device_type == DEVICE_ALI) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_capture_ops); + } else { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops); + } + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - MIC + */ + +static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_mic = NULL; +} + +static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 0, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + chip->device_type == DEVICE_ALI ? + &snd_intel8x0_ali_capture_mic_ops : + &snd_intel8x0_capture_mic_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_mic_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname); + + chip->pcm_mic = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - MIC2 + */ + +static void snd_intel8x0_pcm_mic2_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_mic2 = NULL; +} + +static int __devinit snd_intel8x0_pcm_mic2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - MIC2 ADC", device, 0, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic2_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_mic2_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - MIC2 ADC", chip->card->shortname); + + chip->pcm_mic2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - capture2 + */ + +static void snd_intel8x0_pcm_capture2_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm2 = NULL; +} + +static int __devinit snd_intel8x0_pcm_capture2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - ADC2", device, 0, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture2_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_capture2_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - ADC2", chip->card->shortname); + + chip->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - S/PDIF + */ + +static void snd_intel8x0_pcm_spdif_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_spdif = NULL; +} + +static int __devinit snd_intel8x0_pcm_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_spdif_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_spdif_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - IEC958", chip->card->shortname); + + chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - ALI S/PDIF + */ + +static void snd_intel8x0_ali_spdif_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_spdif = NULL; +} + +static int __devinit snd_intel8x0_ali_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_spdifout_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_spdifin_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_ali_spdif_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - IEC958", chip->card->shortname); + + chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - ALI AC'97 S/PDIF + */ + +static void snd_intel8x0_ali_ac97spdif_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_ac97spdif = NULL; +} + +static int __devinit snd_intel8x0_ali_ac97spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "ALI - AC97 IEC958", device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_ac97spdifout_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_ali_ac97spdif_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - AC97 IEC958", chip->card->shortname); + + chip->pcm_ac97spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer part + */ + +static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + chip->ac97[ac97->num] = NULL; +} + +static struct _ac97_rate_regs { + unsigned int ichd; + unsigned short regs[3]; + short rates_idx; +} ac97_rate_regs[] = { + { ICHD_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC }, + { ICHD_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC }, + { ICHD_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC }, + { ICHD_MIC2, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC }, + { ICHD_PCM2IN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC }, + { ICHD_SPBAR, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF }, +}; + +static struct _ac97_ali_rate_regs { + unsigned int ichd; + unsigned short regs[3]; + short rates_idx; +} ac97_ali_rate_regs[] = { + { ALID_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC }, + { ALID_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC }, + { ALID_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC }, + { ALID_AC97SPDIFOUT, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF }, + { ALID_SPDIFOUT, { 0, 0, 0 }, -1 }, + { ALID_SPDIFIN, { 0, 0, 0 }, -1 }, +}; + +static struct ac97_quirk ac97_quirks[] = { + { 0x1028, 0x0126, "Dell Optiplex GX260", AC97_TUNE_HP_ONLY }, + { 0x1734, 0x0088, "Fujisu-Siemens D1522", AC97_TUNE_HP_ONLY }, + { } /* terminator */ +}; + +static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) +{ + ac97_t ac97, *x97; + ichdev_t *ichdev; + int err, i, num, channels = 2, codecs, _codecs; + unsigned int glob_sta = 0; + + for (i = 0; i <= ICHD_LAST; i++) { + if (chip->device_type != DEVICE_ALI) { + struct _ac97_rate_regs *aregs; + aregs = &ac97_rate_regs[i]; + ichdev = &chip->ichd[aregs->ichd]; + ichdev->ac97_rate_regs[0] = aregs->regs[0]; + ichdev->ac97_rate_regs[1] = aregs->regs[1]; + ichdev->ac97_rate_regs[2] = aregs->regs[2]; + ichdev->ac97_rates_idx = aregs->rates_idx; + } else { + struct _ac97_ali_rate_regs *aregs; + aregs = &ac97_ali_rate_regs[i]; + ichdev = &chip->ichd[aregs->ichd]; + ichdev->ac97_rate_regs[0] = aregs->regs[0]; + ichdev->ac97_rate_regs[1] = aregs->regs[1]; + ichdev->ac97_rate_regs[2] = aregs->regs[2]; + ichdev->ac97_rates_idx = aregs->rates_idx; + } + } + chip->in_ac97_init = 1; + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + ac97.private_free = snd_intel8x0_mixer_free_ac97; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + ac97.clock = ac97_clock; + else + ac97.clock = 48000; + if (chip->device_type != DEVICE_ALI) { + glob_sta = igetdword(chip, ICHREG(GLOB_STA)); + ac97.write = snd_intel8x0_codec_write; + ac97.read = snd_intel8x0_codec_read; + if (glob_sta & ICH_PCM_6) + channels = 6; + else if (glob_sta & ICH_PCM_4) + channels = 4; + if (chip->device_type == DEVICE_INTEL_ICH4) { + codecs = 0; + if (glob_sta & ICH_PCR) + codecs++; + if (glob_sta & ICH_SCR) + codecs++; + if (glob_sta & ICH_TCR) + codecs++; + chip->in_sdin_init = 1; + for (i = 0; i < codecs; i++) { + ac97.num = i; + snd_intel8x0_codec_read(&ac97, 0); + chip->ac97_sdin[i] = igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK; + } + ac97.num = 0; + chip->in_sdin_init = 0; + } else { + codecs = glob_sta & ICH_SCR ? 2 : 1; + } + } else { + ac97.write = snd_intel8x0_ali_codec_write; + ac97.read = snd_intel8x0_ali_codec_read; + channels = 6; + codecs = 1; + /* detect the secondary codec */ + for (i = 0; i < 100; i++) { + unsigned int reg = igetdword(chip, ICHREG(ALI_RTSR)); + if (reg & 0x40) { + codecs = 2; + break; + } + iputdword(chip, ICHREG(ALI_RTSR), reg | 0x40); + udelay(1); + } + } + if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0) + return err; + chip->ac97[0] = x97; + snd_ac97_tune_hardware(chip->ac97[0], chip->pci, ac97_quirks); + chip->ichd[ICHD_PCMOUT].ac97 = x97; + chip->ichd[ICHD_PCMIN].ac97 = x97; + if (x97->ext_id & AC97_EI_VRM) + chip->ichd[ICHD_MIC].ac97 = x97; + if (x97->ext_id & AC97_EI_SPDIF) { + if (chip->device_type != DEVICE_ALI) + chip->ichd[ICHD_SPBAR].ac97 = x97; + else + chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97; + } + /* make sure, that we have DACs at right slot for rev2.2 */ + if (ac97_is_rev22(x97)) + snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 0); + /* AnalogDevices CNR boards uses special codec chaining */ + /* skip standard test method for secondary codecs in this case */ + if (x97->flags & AC97_AD_MULTI) + codecs = 1; + if (codecs < 2) + goto __skip_secondary; + for (i = 1, num = 1, _codecs = codecs; num < _codecs; num++) { + ac97.num = num; + if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0) { + snd_printk("Unable to initialize codec #%i [device = %i, GLOB_STA = 0x%x]\n", i, chip->device_type, glob_sta); + codecs--; + continue; + } + chip->ac97[i++] = x97; + if (!ac97_is_audio(x97)) + continue; + switch (chip->device_type) { + case DEVICE_INTEL_ICH4: + if (chip->ichd[ICHD_PCM2IN].ac97 == NULL) + chip->ichd[ICHD_PCM2IN].ac97 = x97; + if (x97->ext_id & AC97_EI_VRM) { + if (chip->ichd[ICHD_MIC].ac97 == NULL) + chip->ichd[ICHD_MIC].ac97 = x97; + else if (chip->ichd[ICHD_MIC2].ac97 == NULL && + chip->ichd[ICHD_PCM2IN].ac97 == x97) + chip->ichd[ICHD_MIC2].ac97 = x97; + } + if (x97->ext_id & AC97_EI_SPDIF) { + if (chip->ichd[ICHD_SPBAR].ac97 == NULL) + chip->ichd[ICHD_SPBAR].ac97 = x97; + } + break; + default: + if (x97->ext_id & AC97_EI_VRM) { + if (chip->ichd[ICHD_MIC].ac97 == NULL) + chip->ichd[ICHD_MIC].ac97 = x97; + } + break; + } + } + + __skip_secondary: + if (chip->device_type == DEVICE_INTEL_ICH4) { + u8 tmp = igetbyte(chip, ICHREG(SDM)); + tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK); + if (chip->ichd[ICHD_PCM2IN].ac97) { + tmp |= ICH_SE; /* steer enable for multiple SDINs */ + tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT; + tmp |= chip->ac97_sdin[chip->ichd[ICHD_PCM2IN].ac97->num] << ICH_DI2L_SHIFT; + } else { + tmp &= ~ICH_SE; + } + iputbyte(chip, ICHREG(SDM), tmp); + } + for (i = 0; i < codecs; i++) { + x97 = chip->ac97[i]; + if (!ac97_is_audio(x97)) + continue; + if (x97->scaps & AC97_SCAP_SURROUND_DAC) + chip->multi4 = 1; + } + for (i = 0; i < codecs && chip->multi4; i++) { + x97 = chip->ac97[i]; + if (!ac97_is_audio(x97)) + continue; + if (x97->scaps & AC97_SCAP_CENTER_LFE_DAC) + chip->multi6 = 1; + } + if (codecs > 1) { + /* assign right slots for rev2.2 codecs */ + i = 1; + if (chip->multi4) + goto __6ch; + for ( ; i < codecs; i++) { + x97 = chip->ac97[i]; + if (!ac97_is_audio(x97)) + continue; + if (ac97_is_rev22(x97)) { + snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 1); + chip->multi4 = 1; + break; + } + } + __6ch: + for ( ; i < codecs && chip->multi4; i++) { + x97 = chip->ac97[i]; + if (!ac97_is_audio(x97)) + continue; + if (ac97_is_rev22(x97)) { + snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 2); + chip->multi6 = 1; + break; + } + } + /* ok, some older codecs might support only AMAP */ + if (!chip->multi4) { + for (i = 1; i < codecs; i++) { + x97 = chip->ac97[i]; + if (!ac97_is_audio(x97)) + continue; + if (ac97_can_amap(x97)) { + if (x97->addr == 1) { + chip->multi4 = 1; + break; + } + } + } + for ( ; i < codecs && chip->multi4; i++) { + if (!ac97_is_audio(x97)) + continue; + if (ac97_can_amap(x97)) { + if (x97->addr == 2) { + chip->multi6 = 1; + break; + } + } + } + } + } + chip->in_ac97_init = 0; + return 0; +} + + +/* + * + */ + +static void do_ali_reset(intel8x0_t *chip) +{ + iputdword(chip, ICHREG(ALI_SCR), 0x8000000); + iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383); + iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383); + iputdword(chip, ICHREG(ALI_INTERFACECR), 0x04080002); /* no spdif? */ + iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000); + iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000); +} + +static void do_delay(intel8x0_t *chip) +{ +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay((1000 + HZ - 1) / HZ); + return; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +} + +static int snd_intel8x0_ich_chip_init(intel8x0_t *chip) +{ + signed long end_time; + unsigned int cnt, status, nstatus; + + /* put logic to right state */ + /* first clear status bits */ + cnt = igetdword(chip, ICHREG(GLOB_STA)); + iputdword(chip, ICHREG(GLOB_STA), cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT)); + + /* ACLink on, 2 channels */ + cnt = igetdword(chip, ICHREG(GLOB_CNT)); + cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); + /* finish cold or do warm reset */ + cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; + iputdword(chip, ICHREG(GLOB_CNT), cnt); + end_time = (jiffies + (HZ / 4)) + 1; + do { + if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0) + goto __ok; + do_delay(chip); + } while (time_after_eq(end_time, jiffies)); + snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT))); + return -EIO; + + __ok: + /* wait for any codec ready status. + * Once it becomes ready it should remain ready + * as long as we do not disable the ac97 link. + */ + end_time = jiffies + HZ; + do { + status = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR); + if (status) + goto __ok1; + do_delay(chip); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA))); + return -EIO; + + __ok1: + if (status == (ICH_PCR | ICH_SCR | ICH_TCR)) + goto __ok3; + /* wait for other codecs ready status. No secondary codecs? , ok */ + end_time = jiffies + HZ / 4; + do { + nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR); + if (nstatus != status) { + status = nstatus; + goto __ok2; + } + do_delay(chip); + } while (time_after_eq(end_time, jiffies)); + + __ok2: + if (status == (ICH_PCR | ICH_SCR | ICH_TCR)) + goto __ok3; + /* wait for other codecs ready status. No other secondary codecs? , ok */ + /* end_time is not initialized here */ + do { + nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR); + if (nstatus != status) { + status = nstatus; + goto __ok2; + } + do_delay(chip); + } while (time_after_eq(end_time, jiffies)); + + __ok3: + if (chip->device_type == DEVICE_SIS) { + /* unmute the output on SIS7012 */ + iputword(chip, 0x4c, igetword(chip, 0x4c) | 1); + } + return 0; +} + +static int snd_intel8x0_ali_chip_init(intel8x0_t *chip) +{ + u32 reg; + int i = 0; + + reg = igetdword(chip, ICHREG(ALI_SCR)); + if ((reg & 2) == 0) /* Cold required */ + reg |= 2; + else + reg |= 1; /* Warm */ + reg &= ~0x80000000; /* ACLink on */ + iputdword(chip, ICHREG(ALI_SCR), reg); + + for (i = 0; i < HZ / 2; i++) { + if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO)) + goto __ok; + do_delay(chip); + } + snd_printk(KERN_ERR "AC'97 reset failed.\n"); + return -EIO; + + __ok: + for (i = 0; i < HZ / 2; i++) { + reg = igetdword(chip, ICHREG(ALI_RTSR)); + if (reg & 0x80) /* primary codec */ + break; + iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80); + do_delay(chip); + } + + do_ali_reset(chip); + return 0; +} + +static int snd_intel8x0_chip_init(intel8x0_t *chip) +{ + unsigned int i; + int err; + + if (chip->device_type != DEVICE_ALI) + err = snd_intel8x0_ich_chip_init(chip); + else + err = snd_intel8x0_ali_chip_init(chip); + if (err < 0) + return err; + + iagetword(chip, 0); /* clear semaphore flag */ + + /* disable interrupts */ + for (i = 0; i < chip->bdbars_count; i++) + iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00); + /* reset channels */ + for (i = 0; i < chip->bdbars_count; i++) + iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS); + /* initialize Buffer Descriptor Lists */ + for (i = 0; i < chip->bdbars_count; i++) + iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr); + return 0; +} + +static int snd_intel8x0_free(intel8x0_t *chip) +{ + unsigned int i; + + if (chip->irq < 0) + goto __hw_end; + /* disable interrupts */ + for (i = 0; i < chip->bdbars_count; i++) + iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00); + /* reset channels */ + for (i = 0; i < chip->bdbars_count; i++) + iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS); + /* --- */ + synchronize_irq(chip->irq); + __hw_end: + if (chip->bdbars) + snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); + if (chip->remap_addr) + iounmap((void *) chip->remap_addr); + if (chip->remap_bmaddr) + iounmap((void *) chip->remap_bmaddr); + if (chip->res) { + release_resource(chip->res); + kfree_nocheck(chip->res); + } + if (chip->res_bm) { + release_resource(chip->res_bm); + kfree_nocheck(chip->res_bm); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +#ifdef CONFIG_PM +/* + * power management + */ +static void intel8x0_suspend(intel8x0_t *chip) +{ + snd_card_t *card = chip->card; + + if (chip->in_suspend || + card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + chip->in_suspend = 1; + snd_pcm_suspend_all(chip->pcm); + if (chip->pcm_mic) + snd_pcm_suspend_all(chip->pcm_mic); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void intel8x0_resume(intel8x0_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + if (! chip->in_suspend || + card->power_state == SNDRV_CTL_POWER_D0) + return; + + pci_enable_device(chip->pci); + snd_intel8x0_chip_init(chip); + for (i = 0; i < 3; i++) + if (chip->ac97[i]) + snd_ac97_resume(chip->ac97[i]); + + chip->in_suspend = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_intel8x0_suspend(struct pci_dev *dev, u32 state) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO); + intel8x0_suspend(chip); + return 0; +} +static int snd_intel8x0_resume(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO); + intel8x0_resume(chip); + return 0; +} +#else +static void snd_intel8x0_suspend(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return); + intel8x0_suspend(chip); +} +static void snd_intel8x0_resume(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return); + intel8x0_resume(chip); +} +#endif + +/* callback */ +static int snd_intel8x0_set_power_state(snd_card_t *card, unsigned int power_state) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + intel8x0_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + intel8x0_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ + +static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) +{ + snd_pcm_substream_t *subs; + ichdev_t *ichdev; + unsigned long port; + unsigned long pos, t; + unsigned long flags; + struct timeval start_time, stop_time; + + if (chip->ac97[0]->clock != 48000) + return; /* specified in module option */ + + subs = chip->pcm->streams[0].substream; + if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) { + snd_printk("no playback buffer allocated - aborting measure ac97 clock\n"); + return; + } + ichdev = &chip->ichd[ICHD_PCMOUT]; + ichdev->physbuf = subs->dma_buffer.addr; + ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE; + ichdev->substream = NULL; /* don't process interrupts */ + + /* set rate */ + if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) { + snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97[0]->clock); + return; + } + snd_intel8x0_setup_periods(chip, ichdev); + port = ichdev->reg_offset; + spin_lock_irqsave(&chip->reg_lock, flags); + /* trigger */ + if (chip->device_type != DEVICE_ALI) + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); + else { + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); + iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); + } + do_gettimeofday(&start_time); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); +#else + /* FIXME: schedule() can take too long time and overlap the boundary.. */ + mdelay(50); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + /* check the position */ + pos = ichdev->fragsize1; + if (chip->device_type == DEVICE_SIS) + pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb); + else + pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1; + pos += ichdev->position; + do_gettimeofday(&stop_time); + /* stop */ + if (chip->device_type == DEVICE_ALI) + iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8)); + iputbyte(chip, port + ICH_REG_OFF_CR, 0); + /* reset whole DMA things */ + while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) + ; + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + t = stop_time.tv_sec - start_time.tv_sec; + t *= 1000000; + if (stop_time.tv_usec < start_time.tv_usec) + t -= start_time.tv_usec - stop_time.tv_usec; + else + t += stop_time.tv_usec - start_time.tv_usec; + if (t == 0) { + snd_printk(KERN_ERR "?? calculation error..\n"); + return; + } + pos = (pos / 4) * 1000; + pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; + if (pos < 40000 || pos >= 60000) + /* abnormal value. hw problem? */ + printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos); + else if (pos < 47500 || pos > 48500) + /* not 48000Hz, tuning the clock.. */ + chip->ac97[0]->clock = (chip->ac97[0]->clock * 48000) / pos; + printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97[0]->clock); +} + +static void snd_intel8x0_proc_read(snd_info_entry_t * entry, + snd_info_buffer_t * buffer) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, entry->private_data, return); + unsigned int tmp; + + snd_iprintf(buffer, "Intel8x0\n\n"); + if (chip->device_type == DEVICE_ALI) + return; + tmp = igetdword(chip, ICHREG(GLOB_STA)); + snd_iprintf(buffer, "Global control : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT))); + snd_iprintf(buffer, "Global status : 0x%08x\n", tmp); + if (chip->device_type == DEVICE_INTEL_ICH4) + snd_iprintf(buffer, "SDM : 0x%08x\n", igetdword(chip, ICHREG(SDM))); + snd_iprintf(buffer, "AC'97 codecs ready :%s%s%s%s\n", + tmp & ICH_PCR ? " primary" : "", + tmp & ICH_SCR ? " secondary" : "", + tmp & ICH_TCR ? " tertiary" : "", + (tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : ""); + if (chip->device_type == DEVICE_INTEL_ICH4) + snd_iprintf(buffer, "AC'97 codecs SDIN : %i %i %i\n", + chip->ac97_sdin[0], + chip->ac97_sdin[1], + chip->ac97_sdin[2]); +} + +static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) + snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); +} + +static int snd_intel8x0_dev_free(snd_device_t *device) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, device->device_data, return -ENXIO); + return snd_intel8x0_free(chip); +} + +static int __devinit snd_intel8x0_create(snd_card_t * card, + struct pci_dev *pci, + unsigned long device_type, + intel8x0_t ** r_intel8x0) +{ + intel8x0_t *chip; + int err; + unsigned int i; + unsigned int int_sta_masks; + ichdev_t *ichdev; + static snd_device_ops_t ops = { + .dev_free = snd_intel8x0_dev_free, + }; + static u32 intel_int_sta_masks[6] = { + ICH_PIINT, ICH_POINT, ICH_MCINT, ICH_M2INT, ICH_P2INT, ICH_SPINT + }; + static u32 ali_int_sta_masks[6] = { + ALI_INT_PCMIN, ALI_INT_PCMOUT, ALI_INT_MICIN, + ALI_INT_CODECSPDIFOUT, ALI_INT_SPDIFIN, ALI_INT_SPDIFOUT + }; + static u32 ali_reg_offsets[6] = { + 0x40, 0x50, 0x60, 0x70, 0xa0, 0xb0 + }; + + *r_intel8x0 = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(intel8x0_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + chip->device_type = device_type; + chip->card = card; + chip->pci = pci; + chip->irq = -1; + snd_intel8x0_proc_init(chip); + sprintf(chip->ac97_name, "%s - AC'97", card->shortname); + sprintf(chip->ctrl_name, "%s - Controller", card->shortname); + if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */ + chip->mmio = 1; + chip->addr = pci_resource_start(pci, 2); + if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1); + return -EBUSY; + } + chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512); + if (chip->remap_addr == 0) { + snd_intel8x0_free(chip); + snd_printk("AC'97 space ioremap problem\n"); + return -EIO; + } + } else { + chip->addr = pci_resource_start(pci, 0); + if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1); + return -EBUSY; + } + } + if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */ + chip->bm_mmio = 1; + chip->bmaddr = pci_resource_start(pci, 3); + if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1); + return -EBUSY; + } + chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256); + if (chip->remap_bmaddr == 0) { + snd_intel8x0_free(chip); + snd_printk("Controller space ioremap problem\n"); + return -EIO; + } + } else { + chip->bmaddr = pci_resource_start(pci, 1); + if ((chip->res_bm = request_region(chip->bmaddr, 64, chip->ctrl_name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1); + return -EBUSY; + } + } + if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { + snd_intel8x0_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + synchronize_irq(chip->irq); + + /* initialize offsets */ + for (i = 0; i <= ICHD_LAST; i++) { + ichdev = &chip->ichd[i]; + ichdev->ichd = i; + ichdev->reg_offset = i * 0x10 + (i >= 0x30 ? 0x10 : 0); + ichdev->roff_sr = ICH_REG_OFF_SR; + ichdev->roff_picb = ICH_REG_OFF_PICB; + ichdev->int_sta_mask = device_type == DEVICE_ALI ? ali_int_sta_masks[i] : intel_int_sta_masks[i]; + } + switch (device_type) { + case DEVICE_SIS: + for (i = 0; i <= ICHD_LAST; i++) { + ichdev = &chip->ichd[i]; + ichdev->roff_sr = ICH_REG_OFF_PICB; + ichdev->roff_picb = ICH_REG_OFF_SR; + } + break; + case DEVICE_ALI: + for (i = 0; i <= ALID_LAST; i++) { + ichdev = &chip->ichd[i]; + ichdev->reg_offset = ali_reg_offsets[i]; + ichdev->ali_slot = i + 1; /* is this right for last three devices? --jk */ + } + } + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->bdbars_count = 3; + if (device_type == DEVICE_INTEL_ICH4 || device_type == DEVICE_ALI) + chip->bdbars_count = 6; + chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); + if (chip->bdbars == NULL) { + snd_intel8x0_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes here, but the kernel pages + are much bigger, so we don't care (on i386) */ +#ifndef __i386__ + /* .. not sure on other architectures, so we check now. */ + if (chip->bdbars_addr & ~((dma_addr_t)0xffffffff | 0x07)) { + snd_printk("invalid i/o port address %lx\n", (unsigned long)chip->bdbars_addr); + snd_intel8x0_free(chip); + return -ENOMEM; + } +#endif + int_sta_masks = 0; + for (i = 0; i < chip->bdbars_count; i++) { + ichdev = &chip->ichd[i]; + ichdev->bdbar = chip->bdbars + (i * ICH_MAX_FRAGS * 2); + ichdev->bdbar_addr = chip->bdbars_addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2); + int_sta_masks |= ichdev->int_sta_mask; + } + chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA; + chip->int_sta_mask = int_sta_masks; + + if ((err = snd_intel8x0_chip_init(chip)) < 0) { + snd_intel8x0_free(chip); + return err; + } + +#ifdef CONFIG_PM + card->set_power_state = snd_intel8x0_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_intel8x0_free(chip); + return err; + } + + *r_intel8x0 = chip; + return 0; +} + +static struct shortname_table { + unsigned int id; + const char *s; +} shortnames[] __devinitdata = { + { PCI_DEVICE_ID_INTEL_82801, "Intel 82801AA-ICH" }, + { PCI_DEVICE_ID_INTEL_82901, "Intel 82901AB-ICH0" }, + { PCI_DEVICE_ID_INTEL_82801BA, "Intel 82801BA-ICH2" }, + { PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" }, + { PCI_DEVICE_ID_INTEL_ICH3, "Intel 82801CA-ICH3" }, + { PCI_DEVICE_ID_INTEL_ICH4, "Intel 82801DB-ICH4" }, + { PCI_DEVICE_ID_INTEL_ICH5, "Intel ICH5" }, + { PCI_DEVICE_ID_SI_7012, "SiS SI7012" }, + { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" }, + { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia NForce2" }, + { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia NForce3" }, + { 0x746d, "AMD AMD8111" }, + { 0x7445, "AMD AMD768" }, + { 0x5455, "ALi M5455" }, + { 0, 0 }, +}; + +static int __devinit snd_intel8x0_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + intel8x0_t *chip; + int pcm_dev = 0, err; + struct shortname_table *name; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "ICH"); + strcpy(card->shortname, "Intel ICH"); + for (name = shortnames; name->id; name++) { + if (pci->device == name->id) { + strcpy(card->shortname, name->s); + break; + } + } + + if ((err = snd_intel8x0_create(card, pci, pci_id->driver_data, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_intel8x0_mixer(chip, ac97_clock[dev])) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_intel8x0_pcm(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + /* activate MIC PCM only when associated AC'97 codec */ + if (chip->ichd[ICHD_MIC].ac97) { + if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if (chip->device_type == DEVICE_ALI) { + if ((err = snd_intel8x0_ali_spdif(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + /* activate AC'97 S/PDIF only when associated AC'97 codec */ + if (chip->bdbars_count > 3) { + err = 0; + if (chip->device_type == DEVICE_ALI) { + if (chip->ichd[ALID_AC97SPDIFOUT].ac97) + err = snd_intel8x0_ali_ac97spdif(chip, pcm_dev++, NULL); + } else { + if (chip->ichd[ICHD_SPBAR].ac97) + err = snd_intel8x0_pcm_spdif(chip, pcm_dev++, NULL); + } + if (err < 0) { + snd_card_free(card); + return err; + } + if (chip->device_type != DEVICE_ALI) { + /* activate MIC2 only when associated AC'97 codec */ + if (chip->ichd[ICHD_MIC2].ac97) + if ((err = snd_intel8x0_pcm_mic2(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + /* activate PCM2IN only when associated AC'97 codec */ + if (chip->ichd[ICHD_PCM2IN].ac97) + if ((err = snd_intel8x0_pcm_capture2(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + } + + + if (mpu_port[dev] == 0x300 || mpu_port[dev] == 0x330) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_INTEL8X0, + mpu_port[dev], 0, + -1, 0, &chip->rmidi)) < 0) { + printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", mpu_port[dev]); + mpu_port[dev] = 0; + } + } else + mpu_port[dev] = 0; + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->addr, chip->irq); + + if (! ac97_clock[dev]) + intel8x0_measure_ac97_clock(chip); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_intel8x0_remove(struct pci_dev *pci) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Intel ICH", + .id_table = snd_intel8x0_ids, + .probe = snd_intel8x0_probe, + .remove = __devexit_p(snd_intel8x0_remove), +#ifdef CONFIG_PM + .suspend = snd_intel8x0_suspend, + .resume = snd_intel8x0_resume, +#endif +}; + + +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) +/* + * initialize joystick/midi addresses + */ + +static int __devinit snd_intel8x0_joystick_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev; + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + if (joystick_port[dev] > 0 || mpu_port[dev] > 0) { + u16 val; + pci_read_config_word(pci, 0xe6, &val); + if (joystick_port[dev] > 0) + val |= 0x100; + if (mpu_port[dev] == 0x300 || mpu_port[dev] == 0x330) + val |= 0x20; + pci_write_config_word(pci, 0xe6, val | 0x100); + + if (mpu_port[dev] == 0x300 || mpu_port[dev] == 0x330) { + u8 b; + pci_read_config_byte(pci, 0xe2, &b); + if (mpu_port[dev] == 0x300) + b |= 0x08; + else + b &= ~0x08; + pci_write_config_byte(pci, 0xe2, b); + } + } + return 0; +} + +static struct pci_device_id snd_intel8x0_joystick_ids[] __devinitdata = { + { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 82801AA */ + { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 82901AB */ + { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2 */ + { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2M */ + { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH3 */ + // { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 440MX */ + // { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SI7012 */ + { 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE */ + { 0x10de, 0x006b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE2 */ + { 0x10de, 0x00db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE3 */ + { 0, } +}; + +static struct pci_driver joystick_driver = { + .name = "Intel ICH Joystick", + .id_table = snd_intel8x0_joystick_ids, + .probe = snd_intel8x0_joystick_probe, +}; + +static int have_joystick; +#endif + +static int __init alsa_card_intel8x0_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Intel ICH soundcard not found or device busy\n"); +#endif + return err; + } +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) + if (pci_module_init(&joystick_driver) < 0) { + snd_printdd(KERN_INFO "no joystick found\n"); + have_joystick = 0; + } else { + snd_printdd(KERN_INFO "joystick(s) found\n"); + have_joystick = 1; + } +#endif + return 0; + +} + +static void __exit alsa_card_intel8x0_exit(void) +{ + pci_unregister_driver(&driver); +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) + if (have_joystick) + pci_unregister_driver(&joystick_driver); +#endif +} + +module_init(alsa_card_intel8x0_init) +module_exit(alsa_card_intel8x0_exit) + +#ifndef MODULE + +/* format is: snd-intel8x0=enable,index,id,ac97_clock */ + +static int __init alsa_card_intel8x0_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-intel8x0=", alsa_card_intel8x0_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/korg1212/Makefile linux/sound/pci/korg1212/Makefile --- linux-2.4.21-rc1.orig/sound/pci/korg1212/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/korg1212/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _korg1212.o + +list-multi := snd-korg1212.o + +snd-korg1212-objs := korg1212.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o + +include $(TOPDIR)/Rules.make + +snd-korg1212.o: $(snd-korg1212-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-korg1212-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/korg1212/korg1212-firmware.h linux/sound/pci/korg1212/korg1212-firmware.h --- linux-2.4.21-rc1.orig/sound/pci/korg1212/korg1212-firmware.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/korg1212/korg1212-firmware.h 2001-12-18 09:05:18.000000000 -0700 @@ -0,0 +1,987 @@ +static char dspCode [] = { +0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff, +0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff, +0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff, +0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff, +0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff, +0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff, +0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff, +0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff, +0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff, +0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff, +0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff, +0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff, +0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff, +0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff, +0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff, +0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff, +0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff, +0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff, +0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff, +0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff, +0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff, +0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff, +0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff, +0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff, +0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff, +0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff, +0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff, +0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff, +0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff, +0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff, +0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff, +0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff, +0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff, +0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff, +0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff, +0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff, +0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff, +0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff, +0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff, +0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff, +0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff, +0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff, +0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff, +0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff, +0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff, +0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff, +0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff, +0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff, +0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff, +0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff, +0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff, +0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff, +0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff, +0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff, +0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff, +0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff, +0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff, +0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff, +0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff, +0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff, +0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff, +0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff, +0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff, +0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff, +0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff, +0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff, +0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff, +0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff, +0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff, +0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff, +0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff, +0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff, +0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff, +0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff, +0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff, +0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff, +0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff, +0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff, +0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff, +0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff, +0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff, +0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff, +0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff, +0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff, +0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff, +0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff, +0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff, +0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff, +0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff, +0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff, +0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff, +0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff, +0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff, +0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff, +0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff, +0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff }; diff -urN linux-2.4.21-rc1.orig/sound/pci/korg1212/korg1212.c linux/sound/pci/korg1212/korg1212.c --- linux-2.4.21-rc1.orig/sound/pci/korg1212/korg1212.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/korg1212/korg1212.c 2003-02-16 11:03:31.000000000 -0700 @@ -0,0 +1,2502 @@ +/* + * Driver for the Korg 1212 IO PCI card + * + * Copyright (c) 2001 Haroldo Gamal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +// ---------------------------------------------------------------------------- +// Debug Stuff +// ---------------------------------------------------------------------------- +#define K1212_DEBUG_LEVEL 0 +#define K1212_DEBUG_PRINTK printk +//#define K1212_DEBUG_PRINTK(x...) printk("<0>" x) + +// ---------------------------------------------------------------------------- +// Record/Play Buffer Allocation Method. If K1212_LARGEALLOC is defined all +// buffers are alocated as a large piece inside KorgSharedBuffer. +// ---------------------------------------------------------------------------- +//#define K1212_LARGEALLOC 1 + +// ---------------------------------------------------------------------------- +// the following enum defines the valid states of the Korg 1212 I/O card. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_STATE_NONEXISTENT, // there is no card here + K1212_STATE_UNINITIALIZED, // the card is awaiting DSP download + K1212_STATE_DSP_IN_PROCESS, // the card is currently downloading its DSP code + K1212_STATE_DSP_COMPLETE, // the card has finished the DSP download + K1212_STATE_READY, // the card can be opened by an application. Any application + // requests prior to this state should fail. Only an open + // request can be made at this state. + K1212_STATE_OPEN, // an application has opened the card + K1212_STATE_SETUP, // the card has been setup for play + K1212_STATE_PLAYING, // the card is playing + K1212_STATE_MONITOR, // the card is in the monitor mode + K1212_STATE_CALIBRATING, // the card is currently calibrating + K1212_STATE_ERRORSTOP, // the card has stopped itself because of an error and we + // are in the process of cleaning things up. + K1212_STATE_MAX_STATE // state values of this and beyond are invalid +} CardState; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants written to the card's +// host-to-card doorbell to initiate a command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_DB_RequestForData = 0, // sent by the card to request a buffer fill. + K1212_DB_TriggerPlay = 1, // starts playback/record on the card. + K1212_DB_SelectPlayMode = 2, // select monitor, playback setup, or stop. + K1212_DB_ConfigureBufferMemory = 3, // tells card where the host audio buffers are. + K1212_DB_RequestAdatTimecode = 4, // asks the card for the latest ADAT timecode value. + K1212_DB_SetClockSourceRate = 5, // sets the clock source and rate for the card. + K1212_DB_ConfigureMiscMemory = 6, // tells card where other buffers are. + K1212_DB_TriggerFromAdat = 7, // tells card to trigger from Adat at a specific + // timecode value. + K1212_DB_RebootCard = 0xA0, // instructs the card to reboot. + K1212_DB_BootFromDSPPage4 = 0xA4, // instructs the card to boot from the DSP microcode + // on page 4 (local page to card). + K1212_DB_DSPDownloadDone = 0xAE, // sent by the card to indicate the download has + // completed. + K1212_DB_StartDSPDownload = 0xAF // tells the card to download its DSP firmware. +} korg1212_dbcnst_t; + +#define K1212_ISRCODE_DMAERROR 0x80 +#define K1212_ISRCODE_CARDSTOPPED 0x81 + +// ---------------------------------------------------------------------------- +// The following enumeration defines return codes for DeviceIoControl() calls +// to the Korg 1212 I/O driver. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_CMDRET_Success = 0, // command was successfully placed + K1212_CMDRET_DIOCFailure, // the DeviceIoControl call failed + K1212_CMDRET_PMFailure, // the protected mode call failed + K1212_CMDRET_FailUnspecified, // unspecified failure + K1212_CMDRET_FailBadState, // the specified command can not be given in + // the card's current state. (or the wave device's + // state) + K1212_CMDRET_CardUninitialized, // the card is uninitialized and cannot be used + K1212_CMDRET_BadIndex, // an out of range card index was specified + K1212_CMDRET_BadHandle, // an invalid card handle was specified + K1212_CMDRET_NoFillRoutine, // a play request has been made before a fill routine set + K1212_CMDRET_FillRoutineInUse, // can't set a new fill routine while one is in use + K1212_CMDRET_NoAckFromCard, // the card never acknowledged a command + K1212_CMDRET_BadParams, // bad parameters were provided by the caller + + // -------------------------------------------------------------- + // the following return errors are specific to the wave device + // driver interface. These will not be encountered by users of + // the 32 bit DIOC interface (a.k.a. custom or native API). + // -------------------------------------------------------------- + K1212_CMDRET_BadDevice, // the specified wave device was out of range + K1212_CMDRET_BadFormat // the specified wave format is unsupported +} snd_korg1212rc; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants used to select the play +// mode for the card in the SelectPlayMode command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_MODE_SetupPlay = 0x00000001, // provides card with pre-play information + K1212_MODE_MonitorOn = 0x00000002, // tells card to turn on monitor mode + K1212_MODE_MonitorOff = 0x00000004, // tells card to turn off monitor mode + K1212_MODE_StopPlay = 0x00000008 // stops playback on the card +} PlayModeSelector; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants used to select the monitor +// mode for the card in the SetMonitorMode command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_MONMODE_Off = 0, // tells card to turn off monitor mode + K1212_MONMODE_On // tells card to turn on monitor mode +} MonitorModeSelector; + +#define MAILBOX0_OFFSET 0x40 // location of mailbox 0 relative to base address +#define MAILBOX1_OFFSET 0x44 // location of mailbox 1 relative to base address +#define MAILBOX2_OFFSET 0x48 // location of mailbox 2 relative to base address +#define MAILBOX3_OFFSET 0x4c // location of mailbox 3 relative to base address +#define OUT_DOORBELL_OFFSET 0x60 // location of PCI to local doorbell " +#define IN_DOORBELL_OFFSET 0x64 // location of local to PCI doorbell " +#define STATUS_REG_OFFSET 0x68 // location of interrupt control/status register " +#define PCI_CONTROL_OFFSET 0x6c // location of the EEPROM, PCI, User I/O, init control + // register +#define SENS_CONTROL_OFFSET 0x6e // location of the input sensitivity setting register. + // this is the upper word of the PCI control reg. +#define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register + +#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement + // from the card after sending a command. +#define INTERCOMMAND_DELAY 40 +#define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt + // to send a command before giving up. +#define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from + // the card. +#define DOORBELL_VAL_MASK 0x00FF // the doorbell value is one byte + +#define CARD_BOOT_DELAY_IN_MS 10 + +#define DSP_BOOT_DELAY_IN_MS 200 + +#define kNumBuffers 8 +#define k1212MaxCards 4 +#define k1212NumWaveDevices 6 +#define k16BitChannels 10 +#define k32BitChannels 2 +#define kAudioChannels (k16BitChannels + k32BitChannels) +#define kPlayBufferFrames 1024 + +#define K1212_ANALOG_CHANNELS 2 +#define K1212_SPDIF_CHANNELS 2 +#define K1212_ADAT_CHANNELS 8 +#define K1212_CHANNELS (K1212_ADAT_CHANNELS + K1212_ANALOG_CHANNELS) +#define K1212_MIN_CHANNELS 1 +#define K1212_MAX_CHANNELS K1212_CHANNELS +#define K1212_FRAME_SIZE (sizeof(KorgAudioFrame)) +#define K1212_MAX_SAMPLES (kPlayBufferFrames*kNumBuffers) +#define K1212_PERIODS (kNumBuffers) +#define K1212_PERIOD_BYTES (K1212_FRAME_SIZE*kPlayBufferFrames) +#define K1212_BUF_SIZE (K1212_PERIOD_BYTES*kNumBuffers) +#define K1212_ANALOG_BUF_SIZE (K1212_ANALOG_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers) +#define K1212_SPDIF_BUF_SIZE (K1212_SPDIF_CHANNELS * 3 * kPlayBufferFrames * kNumBuffers) +#define K1212_ADAT_BUF_SIZE (K1212_ADAT_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers) +#define K1212_MAX_BUF_SIZE (K1212_ANALOG_BUF_SIZE + K1212_ADAT_BUF_SIZE) + +#define k1212MinADCSens 0x7f +#define k1212MaxADCSens 0x00 +#define k1212MaxVolume 0x7fff +#define k1212MaxWaveVolume 0xffff +#define k1212MinVolume 0x0000 +#define k1212MaxVolInverted 0x8000 + +// ----------------------------------------------------------------- +// the following bits are used for controlling interrupts in the +// interrupt control/status reg +// ----------------------------------------------------------------- +#define PCI_INT_ENABLE_BIT 0x00000100 +#define PCI_DOORBELL_INT_ENABLE_BIT 0x00000200 +#define LOCAL_INT_ENABLE_BIT 0x00010000 +#define LOCAL_DOORBELL_INT_ENABLE_BIT 0x00020000 +#define LOCAL_DMA1_INT_ENABLE_BIT 0x00080000 + +// ----------------------------------------------------------------- +// the following bits are defined for the PCI command register +// ----------------------------------------------------------------- +#define PCI_CMD_MEM_SPACE_ENABLE_BIT 0x0002 +#define PCI_CMD_IO_SPACE_ENABLE_BIT 0x0001 +#define PCI_CMD_BUS_MASTER_ENABLE_BIT 0x0004 + +// ----------------------------------------------------------------- +// the following bits are defined for the PCI status register +// ----------------------------------------------------------------- +#define PCI_STAT_PARITY_ERROR_BIT 0x8000 +#define PCI_STAT_SYSTEM_ERROR_BIT 0x4000 +#define PCI_STAT_MASTER_ABORT_RCVD_BIT 0x2000 +#define PCI_STAT_TARGET_ABORT_RCVD_BIT 0x1000 +#define PCI_STAT_TARGET_ABORT_SENT_BIT 0x0800 + +// ------------------------------------------------------------------------ +// the following constants are used in setting the 1212 I/O card's input +// sensitivity. +// ------------------------------------------------------------------------ +#define SET_SENS_LOCALINIT_BITPOS 15 +#define SET_SENS_DATA_BITPOS 10 +#define SET_SENS_CLOCK_BITPOS 8 +#define SET_SENS_LOADSHIFT_BITPOS 0 + +#define SET_SENS_LEFTCHANID 0x00 +#define SET_SENS_RIGHTCHANID 0x01 + +#define K1212SENSUPDATE_DELAY_IN_MS 50 + +// -------------------------------------------------------------------------- +// WaitRTCTicks +// +// This function waits the specified number of real time clock ticks. +// According to the DDK, each tick is ~0.8 microseconds. +// The defines following the function declaration can be used for the +// numTicksToWait parameter. +// -------------------------------------------------------------------------- +#define ONE_RTC_TICK 1 +#define SENSCLKPULSE_WIDTH 4 +#define LOADSHIFT_DELAY 4 +#define INTERCOMMAND_DELAY 40 +#define STOPCARD_DELAY 300 // max # RTC ticks for the card to stop once we write + // the command register. (could be up to 180 us) +#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement + // from the card after sending a command. + +#include "korg1212-firmware.h" + +typedef struct _snd_korg1212 korg1212_t; + +typedef u16 K1212Sample; // channels 0-9 use 16 bit samples +typedef u32 K1212SpdifSample; // channels 10-11 use 32 bits - only 20 are sent + // across S/PDIF. +typedef u32 K1212TimeCodeSample; // holds the ADAT timecode value + +typedef enum { + K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz + K1212_CLKIDX_AdatAt48K, // selects source as ADAT at 48 kHz + K1212_CLKIDX_WordAt44_1K, // selects source as S/PDIF at 44.1 kHz + K1212_CLKIDX_WordAt48K, // selects source as S/PDIF at 48 kHz + K1212_CLKIDX_LocalAt44_1K, // selects source as local clock at 44.1 kHz + K1212_CLKIDX_LocalAt48K, // selects source as local clock at 48 kHz + K1212_CLKIDX_Invalid // used to check validity of the index +} ClockSourceIndex; + +typedef enum { + K1212_CLKIDX_Adat = 0, // selects source as ADAT + K1212_CLKIDX_Word, // selects source as S/PDIF + K1212_CLKIDX_Local // selects source as local clock +} ClockSourceType; + +typedef struct KorgAudioFrame { + K1212Sample frameData16[k16BitChannels]; + K1212SpdifSample frameData32[k32BitChannels]; + K1212TimeCodeSample timeCodeVal; +} KorgAudioFrame; + +typedef struct KorgAudioBuffer { + KorgAudioFrame bufferData[kPlayBufferFrames]; /* buffer definition */ +} KorgAudioBuffer; + +typedef struct KorgSharedBuffer { +#ifdef K1212_LARGEALLOC + KorgAudioBuffer playDataBufs[kNumBuffers]; + KorgAudioBuffer recordDataBufs[kNumBuffers]; +#endif + short volumeData[kAudioChannels]; + u32 cardCommand; + u16 routeData [kAudioChannels]; + u32 AdatTimeCode; // ADAT timecode value +} KorgSharedBuffer; + +typedef struct SensBits { + union { + struct { + unsigned int leftChanVal:8; + unsigned int leftChanId:8; + } v; + u16 leftSensBits; + } l; + union { + struct { + unsigned int rightChanVal:8; + unsigned int rightChanId:8; + } v; + u16 rightSensBits; + } r; +} SensBits; + +struct _snd_korg1212 { + snd_card_t *card; + struct pci_dev *pci; + snd_pcm_t *pcm; + int irq; + + spinlock_t lock; + + wait_queue_head_t wait; + + unsigned long iomem; + unsigned long ioport; + unsigned long iomem2; + unsigned long irqcount; + unsigned long inIRQ; + unsigned long iobase; + + struct resource *res_iomem; + struct resource *res_ioport; + struct resource *res_iomem2; + + u32 dspCodeSize; + u32 dspMemPhy; // DSP memory block handle (Physical Address) + void * dspMemPtr; // block memory (Virtual Address) + + u32 DataBufsSize; + + KorgAudioBuffer * playDataBufsPtr; + KorgAudioBuffer * recordDataBufsPtr; + + KorgSharedBuffer * sharedBufferPtr; + u32 RecDataPhy; + u32 PlayDataPhy; + unsigned long sharedBufferPhy; + u32 VolumeTablePhy; + u32 RoutingTablePhy; + u32 AdatTimeCodePhy; + + u32 * statusRegPtr; // address of the interrupt status/control register + u32 * outDoorbellPtr; // address of the host->card doorbell register + u32 * inDoorbellPtr; // address of the card->host doorbell register + u32 * mailbox0Ptr; // address of mailbox 0 on the card + u32 * mailbox1Ptr; // address of mailbox 1 on the card + u32 * mailbox2Ptr; // address of mailbox 2 on the card + u32 * mailbox3Ptr; // address of mailbox 3 on the card + u32 * controlRegPtr; // address of the EEPROM, PCI, I/O, Init ctrl reg + u16 * sensRegPtr; // address of the sensitivity setting register + u32 * idRegPtr; // address of the device and vendor ID registers + + size_t periodsize; + int channels; + int currentBuffer; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + CardState cardState; + int running; + int idleMonitorOn; // indicates whether the card is in idle monitor mode. + u32 cmdRetryCount; // tracks how many times we have retried sending to the card. + + ClockSourceIndex clkSrcRate; // sample rate and clock source + + ClockSourceType clkSource; // clock source + int clkRate; // clock rate + + int volumePhase[kAudioChannels]; + + u16 leftADCInSens; // ADC left channel input sensitivity + u16 rightADCInSens; // ADC right channel input sensitivity + + int opencnt; // Open/Close count + int setcnt; // SetupForPlay count + int playcnt; // TriggerPlay count + +}; + +MODULE_DESCRIPTION("korg1212"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{KORG,korg1212}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_AUTHOR("Haroldo Gamal "); + +static struct pci_device_id snd_korg1212_ids[] __devinitdata = { + { + .vendor = 0x10b5, + .device = 0x906d, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; + +static char* stateName[] = { + "Non-existent", + "Uninitialized", + "DSP download in process", + "DSP download complete", + "Ready", + "Open", + "Setup for play", + "Playing", + "Monitor mode on", + "Calibrating" + "Invalid" +}; + +static char* clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; + +static char* clockSourceName[] = { + "ADAT at 44.1 kHz", + "ADAT at 48 kHz", + "S/PDIF at 44.1 kHz", + "S/PDIF at 48 kHz", + "local clock at 44.1 kHz", + "local clock at 48 kHz" +}; + +static char* channelName[] = { + "ADAT-1", + "ADAT-2", + "ADAT-3", + "ADAT-4", + "ADAT-5", + "ADAT-6", + "ADAT-7", + "ADAT-8", + "Analog-L", + "Analog-R", + "SPDIF-L", + "SPDIF-R", +}; + +u16 ClockSourceSelector[] = {0x8000, // selects source as ADAT at 44.1 kHz + 0x0000, // selects source as ADAT at 48 kHz + 0x8001, // selects source as S/PDIF at 44.1 kHz + 0x0001, // selects source as S/PDIF at 48 kHz + 0x8002, // selects source as local clock at 44.1 kHz + 0x0002 // selects source as local clock at 48 kHz + }; + +static snd_korg1212rc rc; + +MODULE_DEVICE_TABLE(pci, snd_korg1212_ids); + +typedef union swap_u32 { unsigned char c[4]; u32 i; } swap_u32; + +#ifdef SNDRV_BIG_ENDIAN +static u32 LowerWordSwap(u32 swappee) +#else +static u32 UpperWordSwap(u32 swappee) +#endif +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[2] = swapper.c[3]; + retVal.c[3] = swapper.c[2]; + retVal.c[1] = swapper.c[1]; + retVal.c[0] = swapper.c[0]; + + return retVal.i; +} + +#ifdef SNDRV_BIG_ENDIAN +static u32 UpperWordSwap(u32 swappee) +#else +static u32 LowerWordSwap(u32 swappee) +#endif +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[2] = swapper.c[2]; + retVal.c[3] = swapper.c[3]; + retVal.c[1] = swapper.c[0]; + retVal.c[0] = swapper.c[1]; + + return retVal.i; +} + +#if 0 /* not used */ + +static u32 EndianSwap(u32 swappee) +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[0] = swapper.c[3]; + retVal.c[1] = swapper.c[2]; + retVal.c[2] = swapper.c[1]; + retVal.c[3] = swapper.c[0]; + + return retVal.i; +} + +#endif /* not used */ + +#define SetBitInWord(theWord,bitPosition) (*theWord) |= (0x0001 << bitPosition) +#define SetBitInDWord(theWord,bitPosition) (*theWord) |= (0x00000001 << bitPosition) +#define ClearBitInWord(theWord,bitPosition) (*theWord) &= ~(0x0001 << bitPosition) +#define ClearBitInDWord(theWord,bitPosition) (*theWord) &= ~(0x00000001 << bitPosition) + +static snd_korg1212rc snd_korg1212_Send1212Command(korg1212_t *korg1212, korg1212_dbcnst_t doorbellVal, + u32 mailBox0Val, u32 mailBox1Val, u32 mailBox2Val, u32 mailBox3Val) +{ + u32 retryCount; + u16 mailBox3Lo; + snd_korg1212rc rc = K1212_CMDRET_Success; + + if (!korg1212->outDoorbellPtr) { +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: CardUninitialized\n"); +#endif + return K1212_CMDRET_CardUninitialized; + } + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: Card <- 0x%08x 0x%08x [%s]\n", doorbellVal, mailBox0Val, stateName[korg1212->cardState]); +#endif + for (retryCount = 0; retryCount < MAX_COMMAND_RETRIES; retryCount++) { + writel(mailBox3Val, korg1212->mailbox3Ptr); + writel(mailBox2Val, korg1212->mailbox2Ptr); + writel(mailBox1Val, korg1212->mailbox1Ptr); + writel(mailBox0Val, korg1212->mailbox0Ptr); + writel(doorbellVal, korg1212->outDoorbellPtr); // interrupt the card + + // -------------------------------------------------------------- + // the reboot command will not give an acknowledgement. + // -------------------------------------------------------------- + if ( doorbellVal == K1212_DB_RebootCard || + doorbellVal == K1212_DB_BootFromDSPPage4 || + doorbellVal == K1212_DB_StartDSPDownload ) { + rc = K1212_CMDRET_Success; + break; + } + + // -------------------------------------------------------------- + // See if the card acknowledged the command. Wait a bit, then + // read in the low word of mailbox3. If the MSB is set and the + // low byte is equal to the doorbell value, then it ack'd. + // -------------------------------------------------------------- + udelay(COMMAND_ACK_DELAY); + mailBox3Lo = readl(korg1212->mailbox3Ptr); + if (mailBox3Lo & COMMAND_ACK_MASK) { + if ((mailBox3Lo & DOORBELL_VAL_MASK) == (doorbellVal & DOORBELL_VAL_MASK)) { +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: Card <- Success\n"); +#endif + rc = K1212_CMDRET_Success; + break; + } + } + } + korg1212->cmdRetryCount += retryCount; + + if (retryCount >= MAX_COMMAND_RETRIES) { +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: Card <- NoAckFromCard\n"); +#endif + rc = K1212_CMDRET_NoAckFromCard; + } + + return rc; +} + +static void snd_korg1212_WaitForCardStopAck(korg1212_t *korg1212) +{ + u32 endtime = jiffies + 2 * HZ; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: WaitForCardStopAck.in [%s] %lu %lu\n", stateName[korg1212->cardState], jiffies, korg1212->inIRQ); +#endif + + if (korg1212->inIRQ) + return; + + do { + if (readl(&korg1212->sharedBufferPtr->cardCommand) == 0) { +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: WaitForCardStopAck.out [%s] %lu %lu\n", stateName[korg1212->cardState], jiffies, korg1212->inIRQ); +#endif + return; + } + if (!korg1212->inIRQ) + schedule(); + } while (time_before(jiffies, endtime)); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: WaitForCardStopAck.out TO [%s] %lu %lu\n", stateName[korg1212->cardState], jiffies, korg1212->inIRQ); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); +} + +static void snd_korg1212_TurnOnIdleMonitor(korg1212_t *korg1212) +{ + udelay(INTERCOMMAND_DELAY); + korg1212->idleMonitorOn = 1; + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); +} + +static void snd_korg1212_TurnOffIdleMonitor(korg1212_t *korg1212) +{ + if (korg1212->idleMonitorOn) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + korg1212->idleMonitorOn = 0; + } +} + +static void snd_korg1212_setCardState(korg1212_t * korg1212, CardState csState) +{ + switch (csState) { + case K1212_STATE_READY: + snd_korg1212_TurnOnIdleMonitor(korg1212); + break; + + case K1212_STATE_OPEN: + snd_korg1212_TurnOffIdleMonitor(korg1212); + break; + + default: + break; + } + + korg1212->cardState = csState; +} + +static int snd_korg1212_OpenCard(korg1212_t * korg1212) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: OpenCard [%s] %d\n", stateName[korg1212->cardState], korg1212->opencnt); +#endif + if (korg1212->opencnt++ == 0) + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + return 1; +} + +static int snd_korg1212_CloseCard(korg1212_t * korg1212) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard [%s] %d\n", stateName[korg1212->cardState], korg1212->opencnt); +#endif + + if (--(korg1212->opencnt)) + return 0; + + if (korg1212->cardState == K1212_STATE_SETUP) { + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_StopPlay, 0, 0, 0); +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + if (rc != K1212_CMDRET_Success) + return 0; + } else if (korg1212->cardState > K1212_STATE_SETUP) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + } + + if (korg1212->cardState > K1212_STATE_READY) + snd_korg1212_setCardState(korg1212, K1212_STATE_READY); + + return 0; +} + +static int snd_korg1212_SetupForPlay(korg1212_t * korg1212) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: SetupForPlay [%s] %d\n", stateName[korg1212->cardState], korg1212->setcnt); +#endif + + if (korg1212->setcnt++) + return 0; + + snd_korg1212_setCardState(korg1212, K1212_STATE_SETUP); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_SetupPlay, 0, 0, 0); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: SetupForPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + if (rc != K1212_CMDRET_Success) { + return 1; + } + return 0; +} + +static int snd_korg1212_TriggerPlay(korg1212_t * korg1212) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: TriggerPlay [%s] %d\n", stateName[korg1212->cardState], korg1212->playcnt); +#endif + + if (korg1212->playcnt++) + return 0; + + snd_korg1212_setCardState(korg1212, K1212_STATE_PLAYING); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_TriggerPlay, 0, 0, 0, 0); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: TriggerPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + if (rc != K1212_CMDRET_Success) { + return 1; + } + return 0; +} + +static int snd_korg1212_StopPlay(korg1212_t * korg1212) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: StopPlay [%s] %d\n", stateName[korg1212->cardState], korg1212->playcnt); +#endif + + if (--(korg1212->playcnt)) + return 0; + + korg1212->setcnt = 0; + + if (korg1212->cardState != K1212_STATE_ERRORSTOP) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + } + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + return 0; +} + +static void snd_korg1212_EnableCardInterrupts(korg1212_t * korg1212) +{ + * korg1212->statusRegPtr = PCI_INT_ENABLE_BIT | + PCI_DOORBELL_INT_ENABLE_BIT | + LOCAL_INT_ENABLE_BIT | + LOCAL_DOORBELL_INT_ENABLE_BIT | + LOCAL_DMA1_INT_ENABLE_BIT; +} + +#if 0 /* not used */ + +static int snd_korg1212_SetMonitorMode(korg1212_t *korg1212, MonitorModeSelector mode) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: SetMonitorMode [%s]\n", stateName[korg1212->cardState]); +#endif + + switch (mode) { + case K1212_MONMODE_Off: + if (korg1212->cardState != K1212_STATE_MONITOR) { + return 0; + } else { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + } + break; + + case K1212_MONMODE_On: + if (korg1212->cardState != K1212_STATE_OPEN) { + return 0; + } else { + snd_korg1212_setCardState(korg1212, K1212_STATE_MONITOR); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); + if (rc != K1212_CMDRET_Success) { + return 0; + } + } + break; + + default: + return 0; + } + + return 1; +} + +#endif /* not used */ + +static int snd_korg1212_SetRate(korg1212_t *korg1212, int rate) +{ + static ClockSourceIndex s44[] = { K1212_CLKIDX_AdatAt44_1K, + K1212_CLKIDX_WordAt44_1K, + K1212_CLKIDX_LocalAt44_1K }; + static ClockSourceIndex s48[] = { + K1212_CLKIDX_AdatAt48K, + K1212_CLKIDX_WordAt48K, + K1212_CLKIDX_LocalAt48K }; + int parm; + + switch(rate) { + case 44100: + parm = s44[korg1212->clkSource]; + break; + + case 48000: + parm = s48[korg1212->clkSource]; + break; + + default: + return -EINVAL; + } + + korg1212->clkSrcRate = parm; + korg1212->clkRate = rate; + + udelay(INTERCOMMAND_DELAY); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate, + ClockSourceSelector[korg1212->clkSrcRate], + 0, 0, 0); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + return 0; +} + +static int snd_korg1212_SetClockSource(korg1212_t *korg1212, int source) +{ + + if (source<0 || source >2) + return -EINVAL; + + korg1212->clkSource = source; + + snd_korg1212_SetRate(korg1212, korg1212->clkRate); + + return 0; +} + +static void snd_korg1212_DisableCardInterrupts(korg1212_t *korg1212) +{ + * korg1212->statusRegPtr = 0; +} + +static int snd_korg1212_WriteADCSensitivity(korg1212_t *korg1212) +{ + SensBits sensVals; + int bitPosition; + int channel; + int clkIs48K; + int monModeSet; + u16 controlValue; // this keeps the current value to be written to + // the card's eeprom control register. + u16 count; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: WriteADCSensivity [%s]\n", stateName[korg1212->cardState]); +#endif + + // ---------------------------------------------------------------------------- + // initialize things. The local init bit is always set when writing to the + // card's control register. + // ---------------------------------------------------------------------------- + controlValue = 0; + SetBitInWord(&controlValue, SET_SENS_LOCALINIT_BITPOS); // init the control value + + // ---------------------------------------------------------------------------- + // make sure the card is not in monitor mode when we do this update. + // ---------------------------------------------------------------------------- + if (korg1212->cardState == K1212_STATE_MONITOR || korg1212->idleMonitorOn) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + monModeSet = 1; + snd_korg1212_WaitForCardStopAck(korg1212); + } else + monModeSet = 0; + + // ---------------------------------------------------------------------------- + // we are about to send new values to the card, so clear the new values queued + // flag. Also, clear out mailbox 3, so we don't lockup. + // ---------------------------------------------------------------------------- + writel(0, korg1212->mailbox3Ptr); + udelay(LOADSHIFT_DELAY); + + // ---------------------------------------------------------------------------- + // determine whether we are running a 48K or 44.1K clock. This info is used + // later when setting the SPDIF FF after the volume has been shifted in. + // ---------------------------------------------------------------------------- + switch (korg1212->clkSrcRate) { + case K1212_CLKIDX_AdatAt44_1K: + case K1212_CLKIDX_WordAt44_1K: + case K1212_CLKIDX_LocalAt44_1K: + clkIs48K = 0; + break; + + case K1212_CLKIDX_WordAt48K: + case K1212_CLKIDX_AdatAt48K: + case K1212_CLKIDX_LocalAt48K: + default: + clkIs48K = 1; + break; + } + + // ---------------------------------------------------------------------------- + // start the update. Setup the bit structure and then shift the bits. + // ---------------------------------------------------------------------------- + sensVals.l.v.leftChanId = SET_SENS_LEFTCHANID; + sensVals.r.v.rightChanId = SET_SENS_RIGHTCHANID; + sensVals.l.v.leftChanVal = korg1212->leftADCInSens; + sensVals.r.v.rightChanVal = korg1212->rightADCInSens; + + // ---------------------------------------------------------------------------- + // now start shifting the bits in. Start with the left channel then the right. + // ---------------------------------------------------------------------------- + for (channel = 0; channel < 2; channel++) { + + // ---------------------------------------------------------------------------- + // Bring the load/shift line low, then wait - the spec says >150ns from load/ + // shift low to the first rising edge of the clock. + // ---------------------------------------------------------------------------- + ClearBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS); + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // load/shift goes low + udelay(LOADSHIFT_DELAY); + + for (bitPosition = 15; bitPosition >= 0; bitPosition--) { // for all the bits + if (channel == 0) { + if (sensVals.l.leftSensBits & (0x0001 << bitPosition)) { + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high + } else { + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low + } + } else { + if (sensVals.r.rightSensBits & (0x0001 << bitPosition)) { + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high + } else { + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low + } + } + + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes low + udelay(SENSCLKPULSE_WIDTH); + SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes high + udelay(SENSCLKPULSE_WIDTH); + } + + // ---------------------------------------------------------------------------- + // finish up SPDIF for left. Bring the load/shift line high, then write a one + // bit if the clock rate is 48K otherwise write 0. + // ---------------------------------------------------------------------------- + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + SetBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // load shift goes high - clk low + udelay(SENSCLKPULSE_WIDTH); + + if (clkIs48K) + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + + writew(controlValue, korg1212->sensRegPtr); // set/clear data bit + udelay(ONE_RTC_TICK); + SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes high + udelay(SENSCLKPULSE_WIDTH); + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes low + udelay(SENSCLKPULSE_WIDTH); + } + + // ---------------------------------------------------------------------------- + // The update is complete. Set a timeout. This is the inter-update delay. + // Also, if the card was in monitor mode, restore it. + // ---------------------------------------------------------------------------- + for (count = 0; count < 10; count++) + udelay(SENSCLKPULSE_WIDTH); + + if (monModeSet) { + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: WriteADCSensivity - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + } + + return 1; +} + +static void snd_korg1212_OnDSPDownloadComplete(korg1212_t *korg1212) +{ + int channel; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: DSP download is complete. [%s]\n", stateName[korg1212->cardState]); +#endif + + // ---------------------------------------------------- + // tell the card to boot + // ---------------------------------------------------- + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_BootFromDSPPage4, 0, 0, 0, 0); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Boot from Page 4 - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + mdelay(DSP_BOOT_DELAY_IN_MS); + + // -------------------------------------------------------------------------------- + // Let the card know where all the buffers are. + // -------------------------------------------------------------------------------- + rc = snd_korg1212_Send1212Command(korg1212, + K1212_DB_ConfigureBufferMemory, + LowerWordSwap(korg1212->PlayDataPhy), + LowerWordSwap(korg1212->RecDataPhy), + ((kNumBuffers * kPlayBufferFrames) / 2), // size given to the card + // is based on 2 buffers + 0 + ); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Configure Buffer Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + udelay(INTERCOMMAND_DELAY); + + rc = snd_korg1212_Send1212Command(korg1212, + K1212_DB_ConfigureMiscMemory, + LowerWordSwap(korg1212->VolumeTablePhy), + LowerWordSwap(korg1212->RoutingTablePhy), + LowerWordSwap(korg1212->AdatTimeCodePhy), + 0 + ); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Configure Misc Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + + // -------------------------------------------------------------------------------- + // Initialize the routing and volume tables, then update the card's state. + // -------------------------------------------------------------------------------- + udelay(INTERCOMMAND_DELAY); + + for (channel = 0; channel < kAudioChannels; channel++) { + korg1212->sharedBufferPtr->volumeData[channel] = k1212MaxVolume; + //korg1212->sharedBufferPtr->routeData[channel] = channel; + korg1212->sharedBufferPtr->routeData[channel] = 8 + (channel & 1); + } + + snd_korg1212_WriteADCSensitivity(korg1212); + + udelay(INTERCOMMAND_DELAY); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate, + ClockSourceSelector[korg1212->clkSrcRate], + 0, 0, 0); +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_READY); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Set Monitor On - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + wake_up_interruptible(&korg1212->wait); +} + +static void snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 doorbellValue; + korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return); + + if(irq != korg1212->irq) + return; + + doorbellValue = readl(korg1212->inDoorbellPtr); + + if (!doorbellValue) + return; + + writel(doorbellValue, korg1212->inDoorbellPtr); + + korg1212->irqcount++; + + korg1212->inIRQ++; + + + switch (doorbellValue) { + case K1212_DB_DSPDownloadDone: +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ DNLD count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + if (korg1212->cardState == K1212_STATE_DSP_IN_PROCESS) { + snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_COMPLETE); + snd_korg1212_OnDSPDownloadComplete(korg1212); + } + break; + + // ------------------------------------------------------------------------ + // an error occurred - stop the card + // ------------------------------------------------------------------------ + case K1212_ISRCODE_DMAERROR: +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_setCardState(korg1212, K1212_STATE_ERRORSTOP); + break; + + // ------------------------------------------------------------------------ + // the card has stopped by our request. Clear the command word and signal + // the semaphore in case someone is waiting for this. + // ------------------------------------------------------------------------ + case K1212_ISRCODE_CARDSTOPPED: +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ CSTP count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); + break; + + default: +#if K1212_DEBUG_LEVEL > 3 + K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ DFLT count - %ld, %x, cpos=%d [%s].\n", korg1212->irqcount, doorbellValue, + korg1212->currentBuffer, stateName[korg1212->cardState]); +#endif + if ((korg1212->cardState > K1212_STATE_SETUP) || korg1212->idleMonitorOn) { + korg1212->currentBuffer++; + + if (korg1212->currentBuffer >= kNumBuffers) + korg1212->currentBuffer = 0; + + if (!korg1212->running) + break; + + if (korg1212->capture_substream) { + snd_pcm_period_elapsed(korg1212->capture_substream); + } + + if (korg1212->playback_substream) { + snd_pcm_period_elapsed(korg1212->playback_substream); + } + } + break; + } + + korg1212->inIRQ--; +} + +static int snd_korg1212_downloadDSPCode(korg1212_t *korg1212) +{ + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: DSP download is starting... [%s]\n", stateName[korg1212->cardState]); +#endif + + // --------------------------------------------------------------- + // verify the state of the card before proceeding. + // --------------------------------------------------------------- + if (korg1212->cardState >= K1212_STATE_DSP_IN_PROCESS) { + return 1; + } + + snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS); + + memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize); + + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload, + UpperWordSwap(korg1212->dspMemPhy), + 0, 0, 0); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Start DSP Download RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + interruptible_sleep_on_timeout(&korg1212->wait, HZ * 4); + + return 0; +} + +static snd_pcm_hardware_t snd_korg1212_playback_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = K1212_MIN_CHANNELS, + .channels_max = K1212_MAX_CHANNELS, + .buffer_bytes_max = K1212_MAX_BUF_SIZE, + .period_bytes_min = K1212_MIN_CHANNELS * 2 * kPlayBufferFrames, + .period_bytes_max = K1212_MAX_CHANNELS * 2 * kPlayBufferFrames, + .periods_min = K1212_PERIODS, + .periods_max = K1212_PERIODS, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_korg1212_capture_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = K1212_MIN_CHANNELS, + .channels_max = K1212_MAX_CHANNELS, + .buffer_bytes_max = K1212_MAX_BUF_SIZE, + .period_bytes_min = K1212_MIN_CHANNELS * 2 * kPlayBufferFrames, + .period_bytes_max = K1212_MAX_CHANNELS * 2 * kPlayBufferFrames, + .periods_min = K1212_PERIODS, + .periods_max = K1212_PERIODS, + .fifo_size = 0, +}; + +static int snd_korg1212_silence(korg1212_t *korg1212, int pos, int count, int offset, int size) +{ + KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; + int i; + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_silence pos=%d offset=%d size=%d count=%d\n", pos, offset, size, count); +#endif + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + for (i=0; i < count; i++) { +#if K1212_DEBUG_LEVEL > 0 + if ( (void *) dst < (void *) korg1212->playDataBufsPtr || + (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) { + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_silence KERNEL EFAULT dst=%p iter=%d\n", dst, i); + return -EFAULT; + } +#endif + memset((void*) dst + offset, 0, size); + dst++; + } + + return 0; +} + +static int snd_korg1212_copy_to(korg1212_t *korg1212, void *dst, int pos, int count, int offset, int size) +{ + KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos; + int i, rc; + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n", pos, offset, size); +#endif + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + for (i=0; i < count; i++) { +#if K1212_DEBUG_LEVEL > 0 + if ( (void *) src < (void *) korg1212->recordDataBufsPtr || + (void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) { + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i); + return -EFAULT; + } +#endif + rc = copy_to_user((void*) dst + offset, src, size); + if (rc) { +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i); +#endif + return -EFAULT; + } + src++; + dst += size; + } + + return 0; +} + +static int snd_korg1212_copy_from(korg1212_t *korg1212, void *src, int pos, int count, int offset, int size) +{ + KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; + int i, rc; + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n", pos, offset, size, count); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + for (i=0; i < count; i++) { +#if K1212_DEBUG_LEVEL > 0 + if ( (void *) dst < (void *) korg1212->playDataBufsPtr || + (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) { + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i); + return -EFAULT; + } +#endif + rc = copy_from_user((void*) dst + offset, src, size); + if (rc) { +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i); +#endif + return -EFAULT; + } + dst++; + src += size; + } + + return 0; +} + +static void snd_korg1212_free_pcm(snd_pcm_t *pcm) +{ + korg1212_t *korg1212 = (korg1212_t *) pcm->private_data; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_free_pcm [%s]\n", stateName[korg1212->cardState]); +#endif + + korg1212->pcm = NULL; +} + +static int snd_korg1212_playback_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_pcm_set_sync(substream); // ??? + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_OpenCard(korg1212); + + runtime->hw = snd_korg1212_playback_info; + runtime->dma_area = (char *) korg1212->playDataBufsPtr; + runtime->dma_bytes = K1212_BUF_SIZE; + + korg1212->playback_substream = substream; + korg1212->periodsize = K1212_PERIODS; + korg1212->channels = K1212_CHANNELS; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames); + return 0; +} + + +static int snd_korg1212_capture_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_pcm_set_sync(substream); // ??? + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_OpenCard(korg1212); + + runtime->hw = snd_korg1212_capture_info; + runtime->dma_area = (char *) korg1212->recordDataBufsPtr; + runtime->dma_bytes = K1212_BUF_SIZE; + + korg1212->capture_substream = substream; + korg1212->periodsize = K1212_PERIODS; + korg1212->channels = K1212_CHANNELS; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames); + return 0; +} + +static int snd_korg1212_playback_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_korg1212_silence(korg1212, 0, K1212_MAX_SAMPLES, 0, korg1212->channels * 2); + + spin_lock_irqsave(&korg1212->lock, flags); + + korg1212->playback_substream = NULL; + korg1212->periodsize = 0; + + snd_korg1212_CloseCard(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_capture_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + korg1212->capture_substream = NULL; + korg1212->periodsize = 0; + + snd_korg1212_CloseCard(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_ioctl: cmd=%d\n", cmd); +#endif + + if (cmd == SNDRV_PCM_IOCTL1_CHANNEL_INFO ) { + snd_pcm_channel_info_t *info = arg; + info->offset = 0; + info->first = info->channel * 16; + info->step = 256; +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: channel_info %d:, offset=%ld, first=%d, step=%d\n", info->channel, info->offset, info->first, info->step); +#endif + return 0; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_korg1212_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + int err; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_hw_params [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) { + spin_unlock_irqrestore(&korg1212->lock, flags); + return err; + } +/* + if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { + spin_unlock_irqrestore(&korg1212->lock, flags); + return -EINVAL; + } +*/ + korg1212->channels = params_channels(params); + korg1212->periodsize = K1212_PERIOD_BYTES; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_prepare(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + unsigned long flags; + int rc; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + rc = snd_korg1212_SetupForPlay(korg1212); + korg1212->currentBuffer = 0; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return rc ? -EINVAL : 0; +} + +static int snd_korg1212_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + int rc; + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger [%s] cmd=%d\n", stateName[korg1212->cardState], cmd); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +/* + if (korg1212->running) { +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger: Already running?\n"); +#endif + break; + } +*/ + korg1212->running++; + rc = snd_korg1212_TriggerPlay(korg1212); + break; + + case SNDRV_PCM_TRIGGER_STOP: +/* + if (!korg1212->running) { +#if K1212_DEBUG_LEVEL > 1 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger: Already stopped?\n"); +#endif + break; + } +*/ + korg1212->running--; + rc = snd_korg1212_StopPlay(korg1212); + break; + + default: + rc = 1; + break; + } + return rc ? -EINVAL : 0; +} + +static snd_pcm_uframes_t snd_korg1212_playback_pointer(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_uframes_t pos; + + pos = korg1212->currentBuffer * kPlayBufferFrames; + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_pointer [%s] %ld\n", + stateName[korg1212->cardState], pos); +#endif + + return pos; +} + +static snd_pcm_uframes_t snd_korg1212_capture_pointer(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_uframes_t pos; + + pos = korg1212->currentBuffer * kPlayBufferFrames; + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_pointer [%s] %ld\n", + stateName[korg1212->cardState], pos); +#endif + + return pos; +} + +static int snd_korg1212_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); +#endif + + return snd_korg1212_copy_from(korg1212, src, pos, count, 0, korg1212->channels * 2); + +} + +static int snd_korg1212_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]); +#endif + + return snd_korg1212_silence(korg1212, pos, count, 0, korg1212->channels * 2); +} + +static int snd_korg1212_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#if K1212_DEBUG_LEVEL > 2 + K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); +#endif + + return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2); +} + +static snd_pcm_ops_t snd_korg1212_playback_ops = { + .open = snd_korg1212_playback_open, + .close = snd_korg1212_playback_close, + .ioctl = snd_korg1212_ioctl, + .hw_params = snd_korg1212_hw_params, + .prepare = snd_korg1212_prepare, + .trigger = snd_korg1212_trigger, + .pointer = snd_korg1212_playback_pointer, + .copy = snd_korg1212_playback_copy, + .silence = snd_korg1212_playback_silence, +}; + +static snd_pcm_ops_t snd_korg1212_capture_ops = { + .open = snd_korg1212_capture_open, + .close = snd_korg1212_capture_close, + .ioctl = snd_korg1212_ioctl, + .hw_params = snd_korg1212_hw_params, + .prepare = snd_korg1212_prepare, + .trigger = snd_korg1212_trigger, + .pointer = snd_korg1212_capture_pointer, + .copy = snd_korg1212_capture_copy, +}; + +/* + * Control Interface + */ + +static int snd_korg1212_control_phase_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + return 0; +} + +static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i = kcontrol->private_value; + + spin_lock_irqsave(&korg1212->lock, flags); + + u->value.integer.value[0] = korg1212->volumePhase[i]; + + if (i >= 8) + u->value.integer.value[1] = korg1212->volumePhase[i+1]; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + int i, val; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + korg1212->volumePhase[i] = u->value.integer.value[0]; + + val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value]; + + if ((u->value.integer.value[0] > 0) != (val < 0)) { + val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1); + korg1212->sharedBufferPtr->volumeData[i] = val; + change = 1; + } + + if (i >= 8) { + korg1212->volumePhase[i+1] = u->value.integer.value[1]; + + val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1]; + + if ((u->value.integer.value[1] > 0) != (val < 0)) { + val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1); + korg1212->sharedBufferPtr->volumeData[i+1] = val; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + uinfo->value.integer.min = k1212MinVolume; + uinfo->value.integer.max = k1212MaxVolume; + return 0; +} + +static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]); + + if (i >= 8) + u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]); + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + int i; + int val; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) { + val = korg1212->volumePhase[i] > 0 ? -1 : 1; + val *= u->value.integer.value[0]; + korg1212->sharedBufferPtr->volumeData[i] = val; + change = 1; + } + + if (i >= 8) { + if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) { + val = korg1212->volumePhase[i+1] > 0 ? -1 : 1; + val *= u->value.integer.value[1]; + korg1212->sharedBufferPtr->volumeData[i+1] = val; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + uinfo->value.enumerated.items = kAudioChannels; + if (uinfo->value.enumerated.item > kAudioChannels-1) { + uinfo->value.enumerated.item = kAudioChannels-1; + } + strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i]; + + if (i >= 8) + u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1]; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0, i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) { + korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0]; + change = 1; + } + + if (i >= 8) { + if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) { + korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1]; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = k1212MaxADCSens; + uinfo->value.integer.max = k1212MinADCSens; + return 0; +} + +static int snd_korg1212_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&korg1212->lock, flags); + + u->value.integer.value[0] = korg1212->leftADCInSens; + u->value.integer.value[1] = korg1212->rightADCInSens; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + + spin_lock_irqsave(&korg1212->lock, flags); + + if (u->value.integer.value[0] != korg1212->leftADCInSens) { + korg1212->leftADCInSens = u->value.integer.value[0]; + change = 1; + } + if (u->value.integer.value[1] != korg1212->rightADCInSens) { + korg1212->rightADCInSens = u->value.integer.value[1]; + change = 1; + } + + if (change) + snd_korg1212_WriteADCSensitivity(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_sync_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) { + uinfo->value.enumerated.item = 2; + } + strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&korg1212->lock, flags); + + ucontrol->value.enumerated.item[0] = korg1212->clkSource; + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&korg1212->lock, flags); + change = val != korg1212->clkSource; + snd_korg1212_SetClockSource(korg1212, val); + spin_unlock_irqrestore(&korg1212->lock, flags); + return change; +} + +#define MON_MIXER(ord,c_name) \ + { \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = c_name " Monitor Volume", \ + .info = snd_korg1212_control_volume_info, \ + .get = snd_korg1212_control_volume_get, \ + .put = snd_korg1212_control_volume_put, \ + .private_value = ord, \ + }, \ + { \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = c_name " Monitor Route", \ + .info = snd_korg1212_control_route_info, \ + .get = snd_korg1212_control_route_get, \ + .put = snd_korg1212_control_route_put, \ + .private_value = ord, \ + }, \ + { \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = c_name " Monitor Phase Invert", \ + .info = snd_korg1212_control_phase_info, \ + .get = snd_korg1212_control_phase_get, \ + .put = snd_korg1212_control_phase_put, \ + .private_value = ord, \ + } + +static snd_kcontrol_new_t snd_korg1212_controls[] = { + MON_MIXER(8, "Analog"), + MON_MIXER(10, "SPDIF"), + MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"), + MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Sync Source", + .info = snd_korg1212_control_sync_info, + .get = snd_korg1212_control_sync_get, + .put = snd_korg1212_control_sync_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Attenuation", + .info = snd_korg1212_control_info, + .get = snd_korg1212_control_get, + .put = snd_korg1212_control_put, + } +}; + +#define K1212_CONTROL_ELEMENTS (sizeof(snd_korg1212_controls) / sizeof(snd_korg1212_controls[0])) + +/* + * proc interface + */ + +static void snd_korg1212_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + int n; + korg1212_t *korg1212 = (korg1212_t *)entry->private_data; + + snd_iprintf(buffer, korg1212->card->longname); + snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1); + snd_iprintf(buffer, "\nGeneral settings\n"); + snd_iprintf(buffer, " period size: %d bytes\n", K1212_PERIOD_BYTES); + snd_iprintf(buffer, " clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] ); + snd_iprintf(buffer, " left ADC Sens: %d\n", korg1212->leftADCInSens ); + snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens ); + snd_iprintf(buffer, " Volume Info:\n"); + for (n=0; n %s [%d]\n", n, + channelName[n], + channelName[korg1212->sharedBufferPtr->routeData[n]], + korg1212->sharedBufferPtr->volumeData[n]); + snd_iprintf(buffer, "\nGeneral status\n"); + snd_iprintf(buffer, " ADAT Time Code: %d\n", korg1212->sharedBufferPtr->AdatTimeCode); + snd_iprintf(buffer, " Card State: %s\n", stateName[korg1212->cardState]); + snd_iprintf(buffer, "Idle mon. State: %d\n", korg1212->idleMonitorOn); + snd_iprintf(buffer, "Cmd retry count: %d\n", korg1212->cmdRetryCount); + snd_iprintf(buffer, " Irq count: %ld\n", korg1212->irqcount); +} + +static void __devinit snd_korg1212_proc_init(korg1212_t *korg1212) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(korg1212->card, "korg1212", &entry)) + snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read); +} + +static int +snd_korg1212_free(korg1212_t *korg1212) +{ + snd_korg1212_TurnOffIdleMonitor(korg1212); + + if (korg1212->irq >= 0) { + synchronize_irq(korg1212->irq); + snd_korg1212_DisableCardInterrupts(korg1212); + free_irq(korg1212->irq, (void *)korg1212); + korg1212->irq = -1; + } + + if (korg1212->iobase != 0) { + iounmap((void *)korg1212->iobase); + korg1212->iobase = 0; + } + + if (korg1212->res_iomem != NULL) { + release_resource(korg1212->res_iomem); + kfree_nocheck(korg1212->res_iomem); + korg1212->res_iomem = NULL; + } + + if (korg1212->res_ioport != NULL) { + release_resource(korg1212->res_ioport); + kfree_nocheck(korg1212->res_ioport); + korg1212->res_ioport = NULL; + } + + if (korg1212->res_iomem2 != NULL) { + release_resource(korg1212->res_iomem2); + kfree_nocheck(korg1212->res_iomem2); + korg1212->res_iomem2 = NULL; + } + + // ---------------------------------------------------- + // free up memory resources used for the DSP download. + // ---------------------------------------------------- + if (korg1212->dspMemPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize, + korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy); + korg1212->dspMemPhy = 0; + korg1212->dspMemPtr = 0; + korg1212->dspCodeSize = 0; + } + +#ifndef K1212_LARGEALLOC + + // ------------------------------------------------------ + // free up memory resources used for the Play/Rec Buffers + // ------------------------------------------------------ + if (korg1212->playDataBufsPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, + korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy); + korg1212->PlayDataPhy = 0; + korg1212->playDataBufsPtr = NULL; + } + + if (korg1212->recordDataBufsPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, + korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy); + korg1212->RecDataPhy = 0; + korg1212->recordDataBufsPtr = NULL; + } + +#endif + + // ---------------------------------------------------- + // free up memory resources used for the Shared Buffers + // ---------------------------------------------------- + if (korg1212->sharedBufferPtr) { + snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer), + korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy); + korg1212->sharedBufferPhy = 0; + korg1212->sharedBufferPtr = NULL; + } + + snd_magic_kfree(korg1212); + return 0; +} + +static int snd_korg1212_dev_free(snd_device_t *device) +{ + korg1212_t *korg1212 = snd_magic_cast(korg1212_t, device->device_data, return -ENXIO); +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: Freeing device\n"); +#endif + return snd_korg1212_free(korg1212); +} + +static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, + korg1212_t ** rchip) + +{ + int err; + unsigned int i; + unsigned ioport_size, iomem_size, iomem2_size; + dma_addr_t phys_addr; + korg1212_t * korg1212; + + static snd_device_ops_t ops = { + .dev_free = snd_korg1212_dev_free, + }; + + * rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + + korg1212 = snd_magic_kcalloc(korg1212_t, 0, GFP_KERNEL); + if (korg1212 == NULL) + return -ENOMEM; + + korg1212->card = card; + korg1212->pci = pci; + + init_waitqueue_head(&korg1212->wait); + spin_lock_init(&korg1212->lock); + + korg1212->irq = -1; + korg1212->clkSource = K1212_CLKIDX_Local; + korg1212->clkRate = 44100; + korg1212->inIRQ = 0; + korg1212->running = 0; + korg1212->opencnt = 0; + korg1212->playcnt = 0; + korg1212->setcnt = 0; + snd_korg1212_setCardState(korg1212, K1212_STATE_UNINITIALIZED); + korg1212->idleMonitorOn = 0; + korg1212->clkSrcRate = K1212_CLKIDX_LocalAt44_1K; + korg1212->leftADCInSens = k1212MaxADCSens; + korg1212->rightADCInSens = k1212MaxADCSens; + + for (i=0; ivolumePhase[i] = 0; + + korg1212->iomem = pci_resource_start(korg1212->pci, 0); + korg1212->ioport = pci_resource_start(korg1212->pci, 1); + korg1212->iomem2 = pci_resource_start(korg1212->pci, 2); + + iomem_size = pci_resource_len(korg1212->pci, 0); + ioport_size = pci_resource_len(korg1212->pci, 1); + iomem2_size = pci_resource_len(korg1212->pci, 2); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: resources:\n" + " iomem = 0x%lx (%d)\n" + " ioport = 0x%lx (%d)\n" + " iomem = 0x%lx (%d)\n" + " [%s]\n", + korg1212->iomem, iomem_size, + korg1212->ioport, ioport_size, + korg1212->iomem2, iomem2_size, + stateName[korg1212->cardState]); +#endif + + korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212"); + if (korg1212->res_iomem == NULL) { + snd_printk(KERN_ERR "unable to grab region 0x%lx-0x%lx\n", + korg1212->iomem, korg1212->iomem + iomem_size - 1); + return -EBUSY; + } + + korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212"); + if (korg1212->res_ioport == NULL) { + snd_printk(KERN_ERR "unable to grab region 0x%lx-0x%lx\n", + korg1212->ioport, korg1212->ioport + ioport_size - 1); + return -EBUSY; + } + + korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212"); + if (korg1212->res_iomem2 == NULL) { + snd_printk(KERN_ERR "unable to grab region 0x%lx-0x%lx\n", + korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1); + return -EBUSY; + } + + if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) { + snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase, + korg1212->iobase + iomem_size - 1); + return -EBUSY; + } + + err = request_irq(pci->irq, snd_korg1212_interrupt, + SA_INTERRUPT|SA_SHIRQ, + "korg1212", (void *) korg1212); + + if (err) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + + korg1212->irq = pci->irq; + + pci_set_master(korg1212->pci); + + korg1212->statusRegPtr = (u32 *) (korg1212->iobase + STATUS_REG_OFFSET); + korg1212->outDoorbellPtr = (u32 *) (korg1212->iobase + OUT_DOORBELL_OFFSET); + korg1212->inDoorbellPtr = (u32 *) (korg1212->iobase + IN_DOORBELL_OFFSET); + korg1212->mailbox0Ptr = (u32 *) (korg1212->iobase + MAILBOX0_OFFSET); + korg1212->mailbox1Ptr = (u32 *) (korg1212->iobase + MAILBOX1_OFFSET); + korg1212->mailbox2Ptr = (u32 *) (korg1212->iobase + MAILBOX2_OFFSET); + korg1212->mailbox3Ptr = (u32 *) (korg1212->iobase + MAILBOX3_OFFSET); + korg1212->controlRegPtr = (u32 *) (korg1212->iobase + PCI_CONTROL_OFFSET); + korg1212->sensRegPtr = (u16 *) (korg1212->iobase + SENS_CONTROL_OFFSET); + korg1212->idRegPtr = (u32 *) (korg1212->iobase + DEV_VEND_ID_OFFSET); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: card registers:\n" + " Status register = 0x%p\n" + " OutDoorbell = 0x%p\n" + " InDoorbell = 0x%p\n" + " Mailbox0 = 0x%p\n" + " Mailbox1 = 0x%p\n" + " Mailbox2 = 0x%p\n" + " Mailbox3 = 0x%p\n" + " ControlReg = 0x%p\n" + " SensReg = 0x%p\n" + " IDReg = 0x%p\n" + " [%s]\n", + korg1212->statusRegPtr, + korg1212->outDoorbellPtr, + korg1212->inDoorbellPtr, + korg1212->mailbox0Ptr, + korg1212->mailbox1Ptr, + korg1212->mailbox2Ptr, + korg1212->mailbox3Ptr, + korg1212->controlRegPtr, + korg1212->sensRegPtr, + korg1212->idRegPtr, + stateName[korg1212->cardState]); +#endif + + korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr); + korg1212->sharedBufferPhy = (unsigned long)phys_addr; + + if (korg1212->sharedBufferPtr == NULL) { + snd_printk(KERN_ERR "can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer)); + return -ENOMEM; + } + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer)); +#endif + +#ifndef K1212_LARGEALLOC + + korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers; + + korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); + korg1212->PlayDataPhy = (u32)phys_addr; + + if (korg1212->playDataBufsPtr == NULL) { + snd_printk(KERN_ERR "can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + return -ENOMEM; + } + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n", + korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize); +#endif + + korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); + korg1212->RecDataPhy = (u32)phys_addr; + + if (korg1212->recordDataBufsPtr == NULL) { + snd_printk(KERN_ERR "can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + return -ENOMEM; + } + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n", + korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize); +#endif + +#else // K1212_LARGEALLOC + + korg1212->recordDataBufsPtr = korg1212->sharedBufferPtr->recordDataBufs; + korg1212->playDataBufsPtr = korg1212->sharedBufferPtr->playDataBufs; + korg1212->PlayDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->playDataBufs; + korg1212->RecDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->recordDataBufs; + +#endif // K1212_LARGEALLOC + + korg1212->dspCodeSize = sizeof (dspCode); + + korg1212->VolumeTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->volumeData; + korg1212->RoutingTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->routeData; + korg1212->AdatTimeCodePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->AdatTimeCode; + + korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr); + korg1212->dspMemPhy = (u32)phys_addr; + + if (korg1212->dspMemPtr == NULL) { + snd_printk(KERN_ERR "can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); + return -ENOMEM; + } + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", + korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize, + stateName[korg1212->cardState]); +#endif + + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0); + +#if K1212_DEBUG_LEVEL > 0 + if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + snd_korg1212_EnableCardInterrupts(korg1212); + + mdelay(CARD_BOOT_DELAY_IN_MS); + + if (snd_korg1212_downloadDSPCode(korg1212)) + return -EBUSY; + + printk(KERN_INFO "dspMemPhy = %08x U[%08x]\n" + "PlayDataPhy = %08x L[%08x]\n" + "RecDataPhy = %08x L[%08x]\n" + "VolumeTablePhy = %08x L[%08x]\n" + "RoutingTablePhy = %08x L[%08x]\n" + "AdatTimeCodePhy = %08x L[%08x]\n", + korg1212->dspMemPhy, UpperWordSwap(korg1212->dspMemPhy), + korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy), + korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy), + korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy), + korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy), + korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy)); + + if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm)) < 0) + return err; + + korg1212->pcm->private_data = korg1212; + korg1212->pcm->private_free = snd_korg1212_free_pcm; + strcpy(korg1212->pcm->name, "korg1212"); + + snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops); + + snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_korg1212_capture_ops); + + korg1212->pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + //snd_pcm_lib_preallocate_pages_for_all(korg1212->pcm, + // K1212_MAX_BUF_SIZE, K1212_MAX_BUF_SIZE, GFP_KERNEL); + + for (i = 0; i < K1212_CONTROL_ELEMENTS; i++) { + err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212)); + if (err < 0) + return err; + } + + snd_korg1212_proc_init(korg1212); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, korg1212, &ops)) < 0) { + snd_korg1212_free(korg1212); + return err; + } + + * rchip = korg1212; + return 0; + +} + +/* + * Card initialisation + */ + +static int __devinit +snd_korg1212_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + korg1212_t *korg1212; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if (!enable[dev]) { + dev++; + return -ENOENT; + } + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_korg1212_create(card, pci, &korg1212)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "korg1212"); + strcpy(card->shortname, "korg1212"); + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, + korg1212->iomem, korg1212->irq); + +#if K1212_DEBUG_LEVEL > 0 + K1212_DEBUG_PRINTK("K1212_DEBUG: %s\n", card->longname); +#endif + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, korg1212); + dev++; + return 0; +} + +static void __devexit snd_korg1212_remove(struct pci_dev *pci) +{ + korg1212_t *korg1212 = pci_get_drvdata(pci); + snd_card_free(korg1212->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "korg1212", + .id_table = snd_korg1212_ids, + .probe = snd_korg1212_probe, + .remove = __devexit_p(snd_korg1212_remove), +}; + +static int __init alsa_card_korg1212_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "No Korg 1212IO cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_korg1212_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_korg1212_init) +module_exit(alsa_card_korg1212_exit) + +#ifndef MODULE + +/* format is: snd-korg1212=enable,index,id */ + +static int __init alsa_card_korg1212_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-korg1212=", alsa_card_korg1212_setup); + +#endif /* ifndef MODULE */ + diff -urN linux-2.4.21-rc1.orig/sound/pci/maestro3.c linux/sound/pci/maestro3.c --- linux-2.4.21-rc1.orig/sound/pci/maestro3.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/maestro3.c 2003-03-19 04:55:32.000000000 -0700 @@ -0,0 +1,2765 @@ +/* + * Driver for ESS Maestro3/Allegro (ES1988) soundcards. + * Copyright (c) 2000 by Zach Brown + * Takashi Iwai + * + * Most of the hardware init stuffs are based on maestro3 driver for + * OSS/Free by Zach Brown. Many thanks to Zach! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * ChangeLog: + * Aug. 27, 2001 + * - Fixed deadlock on capture + * - Added Canyon3D-2 support by Rob Riggs + * + */ + +#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2" +#define DRIVER_NAME "Maestro3" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Zach Brown , Takashi Iwai "); +MODULE_DESCRIPTION("ESS Maestro3 PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,Maestro3 PCI}," + "{ESS,ES1988}," + "{ESS,Allegro PCI}," + "{ESS,Allegro-1 PCI}," + "{ESS,Canyon3D-2/LE PCI}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */ +static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static int amp_gpio[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(external_amp, "Enable external amp for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +MODULE_PARM(amp_gpio, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)"); +MODULE_PARM_SYNTAX(amp_gpio, SNDRV_ENABLED); + +#define MAX_PLAYBACKS 2 +#define MAX_CAPTURES 1 +#define NR_DSPS (MAX_PLAYBACKS + MAX_CAPTURES) + + +/* + * maestro3 registers + */ + +/* Allegro PCI configuration registers */ +#define PCI_LEGACY_AUDIO_CTRL 0x40 +#define SOUND_BLASTER_ENABLE 0x00000001 +#define FM_SYNTHESIS_ENABLE 0x00000002 +#define GAME_PORT_ENABLE 0x00000004 +#define MPU401_IO_ENABLE 0x00000008 +#define MPU401_IRQ_ENABLE 0x00000010 +#define ALIAS_10BIT_IO 0x00000020 +#define SB_DMA_MASK 0x000000C0 +#define SB_DMA_0 0x00000040 +#define SB_DMA_1 0x00000040 +#define SB_DMA_R 0x00000080 +#define SB_DMA_3 0x000000C0 +#define SB_IRQ_MASK 0x00000700 +#define SB_IRQ_5 0x00000000 +#define SB_IRQ_7 0x00000100 +#define SB_IRQ_9 0x00000200 +#define SB_IRQ_10 0x00000300 +#define MIDI_IRQ_MASK 0x00003800 +#define SERIAL_IRQ_ENABLE 0x00004000 +#define DISABLE_LEGACY 0x00008000 + +#define PCI_ALLEGRO_CONFIG 0x50 +#define SB_ADDR_240 0x00000004 +#define MPU_ADDR_MASK 0x00000018 +#define MPU_ADDR_330 0x00000000 +#define MPU_ADDR_300 0x00000008 +#define MPU_ADDR_320 0x00000010 +#define MPU_ADDR_340 0x00000018 +#define USE_PCI_TIMING 0x00000040 +#define POSTED_WRITE_ENABLE 0x00000080 +#define DMA_POLICY_MASK 0x00000700 +#define DMA_DDMA 0x00000000 +#define DMA_TDMA 0x00000100 +#define DMA_PCPCI 0x00000200 +#define DMA_WBDMA16 0x00000400 +#define DMA_WBDMA4 0x00000500 +#define DMA_WBDMA2 0x00000600 +#define DMA_WBDMA1 0x00000700 +#define DMA_SAFE_GUARD 0x00000800 +#define HI_PERF_GP_ENABLE 0x00001000 +#define PIC_SNOOP_MODE_0 0x00002000 +#define PIC_SNOOP_MODE_1 0x00004000 +#define SOUNDBLASTER_IRQ_MASK 0x00008000 +#define RING_IN_ENABLE 0x00010000 +#define SPDIF_TEST_MODE 0x00020000 +#define CLK_MULT_MODE_SELECT_2 0x00040000 +#define EEPROM_WRITE_ENABLE 0x00080000 +#define CODEC_DIR_IN 0x00100000 +#define HV_BUTTON_FROM_GD 0x00200000 +#define REDUCED_DEBOUNCE 0x00400000 +#define HV_CTRL_ENABLE 0x00800000 +#define SPDIF_ENABLE 0x01000000 +#define CLK_DIV_SELECT 0x06000000 +#define CLK_DIV_BY_48 0x00000000 +#define CLK_DIV_BY_49 0x02000000 +#define CLK_DIV_BY_50 0x04000000 +#define CLK_DIV_RESERVED 0x06000000 +#define PM_CTRL_ENABLE 0x08000000 +#define CLK_MULT_MODE_SELECT 0x30000000 +#define CLK_MULT_MODE_SHIFT 28 +#define CLK_MULT_MODE_0 0x00000000 +#define CLK_MULT_MODE_1 0x10000000 +#define CLK_MULT_MODE_2 0x20000000 +#define CLK_MULT_MODE_3 0x30000000 +#define INT_CLK_SELECT 0x40000000 +#define INT_CLK_MULT_RESET 0x80000000 + +/* M3 */ +#define INT_CLK_SRC_NOT_PCI 0x00100000 +#define INT_CLK_MULT_ENABLE 0x80000000 + +#define PCI_ACPI_CONTROL 0x54 +#define PCI_ACPI_D0 0x00000000 +#define PCI_ACPI_D1 0xB4F70000 +#define PCI_ACPI_D2 0xB4F7B4F7 + +#define PCI_USER_CONFIG 0x58 +#define EXT_PCI_MASTER_ENABLE 0x00000001 +#define SPDIF_OUT_SELECT 0x00000002 +#define TEST_PIN_DIR_CTRL 0x00000004 +#define AC97_CODEC_TEST 0x00000020 +#define TRI_STATE_BUFFER 0x00000080 +#define IN_CLK_12MHZ_SELECT 0x00000100 +#define MULTI_FUNC_DISABLE 0x00000200 +#define EXT_MASTER_PAIR_SEL 0x00000400 +#define PCI_MASTER_SUPPORT 0x00000800 +#define STOP_CLOCK_ENABLE 0x00001000 +#define EAPD_DRIVE_ENABLE 0x00002000 +#define REQ_TRI_STATE_ENABLE 0x00004000 +#define REQ_LOW_ENABLE 0x00008000 +#define MIDI_1_ENABLE 0x00010000 +#define MIDI_2_ENABLE 0x00020000 +#define SB_AUDIO_SYNC 0x00040000 +#define HV_CTRL_TEST 0x00100000 +#define SOUNDBLASTER_TEST 0x00400000 + +#define PCI_USER_CONFIG_C 0x5C + +#define PCI_DDMA_CTRL 0x60 +#define DDMA_ENABLE 0x00000001 + + +/* Allegro registers */ +#define HOST_INT_CTRL 0x18 +#define SB_INT_ENABLE 0x0001 +#define MPU401_INT_ENABLE 0x0002 +#define ASSP_INT_ENABLE 0x0010 +#define RING_INT_ENABLE 0x0020 +#define HV_INT_ENABLE 0x0040 +#define CLKRUN_GEN_ENABLE 0x0100 +#define HV_CTRL_TO_PME 0x0400 +#define SOFTWARE_RESET_ENABLE 0x8000 + +/* + * should be using the above defines, probably. + */ +#define REGB_ENABLE_RESET 0x01 +#define REGB_STOP_CLOCK 0x10 + +#define HOST_INT_STATUS 0x1A +#define SB_INT_PENDING 0x01 +#define MPU401_INT_PENDING 0x02 +#define ASSP_INT_PENDING 0x10 +#define RING_INT_PENDING 0x20 +#define HV_INT_PENDING 0x40 + +#define HARDWARE_VOL_CTRL 0x1B +#define SHADOW_MIX_REG_VOICE 0x1C +#define HW_VOL_COUNTER_VOICE 0x1D +#define SHADOW_MIX_REG_MASTER 0x1E +#define HW_VOL_COUNTER_MASTER 0x1F + +#define CODEC_COMMAND 0x30 +#define CODEC_READ_B 0x80 + +#define CODEC_STATUS 0x30 +#define CODEC_BUSY_B 0x01 + +#define CODEC_DATA 0x32 + +#define RING_BUS_CTRL_A 0x36 +#define RAC_PME_ENABLE 0x0100 +#define RAC_SDFS_ENABLE 0x0200 +#define LAC_PME_ENABLE 0x0400 +#define LAC_SDFS_ENABLE 0x0800 +#define SERIAL_AC_LINK_ENABLE 0x1000 +#define IO_SRAM_ENABLE 0x2000 +#define IIS_INPUT_ENABLE 0x8000 + +#define RING_BUS_CTRL_B 0x38 +#define SECOND_CODEC_ID_MASK 0x0003 +#define SPDIF_FUNC_ENABLE 0x0010 +#define SECOND_AC_ENABLE 0x0020 +#define SB_MODULE_INTF_ENABLE 0x0040 +#define SSPE_ENABLE 0x0040 +#define M3I_DOCK_ENABLE 0x0080 + +#define SDO_OUT_DEST_CTRL 0x3A +#define COMMAND_ADDR_OUT 0x0003 +#define PCM_LR_OUT_LOCAL 0x0000 +#define PCM_LR_OUT_REMOTE 0x0004 +#define PCM_LR_OUT_MUTE 0x0008 +#define PCM_LR_OUT_BOTH 0x000C +#define LINE1_DAC_OUT_LOCAL 0x0000 +#define LINE1_DAC_OUT_REMOTE 0x0010 +#define LINE1_DAC_OUT_MUTE 0x0020 +#define LINE1_DAC_OUT_BOTH 0x0030 +#define PCM_CLS_OUT_LOCAL 0x0000 +#define PCM_CLS_OUT_REMOTE 0x0040 +#define PCM_CLS_OUT_MUTE 0x0080 +#define PCM_CLS_OUT_BOTH 0x00C0 +#define PCM_RLF_OUT_LOCAL 0x0000 +#define PCM_RLF_OUT_REMOTE 0x0100 +#define PCM_RLF_OUT_MUTE 0x0200 +#define PCM_RLF_OUT_BOTH 0x0300 +#define LINE2_DAC_OUT_LOCAL 0x0000 +#define LINE2_DAC_OUT_REMOTE 0x0400 +#define LINE2_DAC_OUT_MUTE 0x0800 +#define LINE2_DAC_OUT_BOTH 0x0C00 +#define HANDSET_OUT_LOCAL 0x0000 +#define HANDSET_OUT_REMOTE 0x1000 +#define HANDSET_OUT_MUTE 0x2000 +#define HANDSET_OUT_BOTH 0x3000 +#define IO_CTRL_OUT_LOCAL 0x0000 +#define IO_CTRL_OUT_REMOTE 0x4000 +#define IO_CTRL_OUT_MUTE 0x8000 +#define IO_CTRL_OUT_BOTH 0xC000 + +#define SDO_IN_DEST_CTRL 0x3C +#define STATUS_ADDR_IN 0x0003 +#define PCM_LR_IN_LOCAL 0x0000 +#define PCM_LR_IN_REMOTE 0x0004 +#define PCM_LR_RESERVED 0x0008 +#define PCM_LR_IN_BOTH 0x000C +#define LINE1_ADC_IN_LOCAL 0x0000 +#define LINE1_ADC_IN_REMOTE 0x0010 +#define LINE1_ADC_IN_MUTE 0x0020 +#define MIC_ADC_IN_LOCAL 0x0000 +#define MIC_ADC_IN_REMOTE 0x0040 +#define MIC_ADC_IN_MUTE 0x0080 +#define LINE2_DAC_IN_LOCAL 0x0000 +#define LINE2_DAC_IN_REMOTE 0x0400 +#define LINE2_DAC_IN_MUTE 0x0800 +#define HANDSET_IN_LOCAL 0x0000 +#define HANDSET_IN_REMOTE 0x1000 +#define HANDSET_IN_MUTE 0x2000 +#define IO_STATUS_IN_LOCAL 0x0000 +#define IO_STATUS_IN_REMOTE 0x4000 + +#define SPDIF_IN_CTRL 0x3E +#define SPDIF_IN_ENABLE 0x0001 + +#define GPIO_DATA 0x60 +#define GPIO_DATA_MASK 0x0FFF +#define GPIO_HV_STATUS 0x3000 +#define GPIO_PME_STATUS 0x4000 + +#define GPIO_MASK 0x64 +#define GPIO_DIRECTION 0x68 +#define GPO_PRIMARY_AC97 0x0001 +#define GPI_LINEOUT_SENSE 0x0004 +#define GPO_SECONDARY_AC97 0x0008 +#define GPI_VOL_DOWN 0x0010 +#define GPI_VOL_UP 0x0020 +#define GPI_IIS_CLK 0x0040 +#define GPI_IIS_LRCLK 0x0080 +#define GPI_IIS_DATA 0x0100 +#define GPI_DOCKING_STATUS 0x0100 +#define GPI_HEADPHONE_SENSE 0x0200 +#define GPO_EXT_AMP_SHUTDOWN 0x1000 + +#define GPO_EXT_AMP_M3 1 /* default m3 amp */ +#define GPO_EXT_AMP_ALLEGRO 8 /* default allegro amp */ + +/* M3 */ +#define GPO_M3_EXT_AMP_SHUTDN 0x0002 + +#define ASSP_INDEX_PORT 0x80 +#define ASSP_MEMORY_PORT 0x82 +#define ASSP_DATA_PORT 0x84 + +#define MPU401_DATA_PORT 0x98 +#define MPU401_STATUS_PORT 0x99 + +#define CLK_MULT_DATA_PORT 0x9C + +#define ASSP_CONTROL_A 0xA2 +#define ASSP_0_WS_ENABLE 0x01 +#define ASSP_CTRL_A_RESERVED1 0x02 +#define ASSP_CTRL_A_RESERVED2 0x04 +#define ASSP_CLK_49MHZ_SELECT 0x08 +#define FAST_PLU_ENABLE 0x10 +#define ASSP_CTRL_A_RESERVED3 0x20 +#define DSP_CLK_36MHZ_SELECT 0x40 + +#define ASSP_CONTROL_B 0xA4 +#define RESET_ASSP 0x00 +#define RUN_ASSP 0x01 +#define ENABLE_ASSP_CLOCK 0x00 +#define STOP_ASSP_CLOCK 0x10 +#define RESET_TOGGLE 0x40 + +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOST_INT_ENABLE 0x01 +#define FM_ADDR_REMAP_DISABLE 0x02 +#define HOST_WRITE_PORT_ENABLE 0x08 + +#define ASSP_HOST_INT_STATUS 0xAC +#define DSP2HOST_REQ_PIORECORD 0x01 +#define DSP2HOST_REQ_I2SRATE 0x02 +#define DSP2HOST_REQ_TIMER 0x04 + +/* AC97 registers */ +/* XXX fix this crap up */ +/*#define AC97_RESET 0x00*/ + +#define AC97_VOL_MUTE_B 0x8000 +#define AC97_VOL_M 0x1F +#define AC97_LEFT_VOL_S 8 + +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 +#define AC97_PC_BEEP_VOL 0x0A +#define AC97_PC_BEEP_VOL_M 0x0F +#define AC97_SROUND_MASTER_VOL 0x38 +#define AC97_PC_BEEP_VOL_S 1 + +/*#define AC97_PHONE_VOL 0x0C +#define AC97_MIC_VOL 0x0E*/ +#define AC97_MIC_20DB_ENABLE 0x40 + +/*#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16*/ +#define AC97_PCM_OUT_VOL 0x18 +/*#define AC97_RECORD_SELECT 0x1A*/ +#define AC97_RECORD_MIC 0x00 +#define AC97_RECORD_CD 0x01 +#define AC97_RECORD_VIDEO 0x02 +#define AC97_RECORD_AUX 0x03 +#define AC97_RECORD_MONO_MUX 0x02 +#define AC97_RECORD_DIGITAL 0x03 +#define AC97_RECORD_LINE 0x04 +#define AC97_RECORD_STEREO 0x05 +#define AC97_RECORD_MONO 0x06 +#define AC97_RECORD_PHONE 0x07 + +/*#define AC97_RECORD_GAIN 0x1C*/ +#define AC97_RECORD_VOL_M 0x0F + +/*#define AC97_GENERAL_PURPOSE 0x20*/ +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_ADC_READY 0x0001 +#define AC97_DAC_READY 0x0002 +#define AC97_ANALOG_READY 0x0004 +#define AC97_VREF_ON 0x0008 +#define AC97_PR0 0x0100 +#define AC97_PR1 0x0200 +#define AC97_PR2 0x0400 +#define AC97_PR3 0x0800 +#define AC97_PR4 0x1000 + +#define AC97_RESERVED1 0x28 + +#define AC97_VENDOR_TEST 0x5A + +#define AC97_CLOCK_DELAY 0x5C +#define AC97_LINEOUT_MUX_SEL 0x0001 +#define AC97_MONO_MUX_SEL 0x0002 +#define AC97_CLOCK_DELAY_SEL 0x1F +#define AC97_DAC_CDS_SHIFT 6 +#define AC97_ADC_CDS_SHIFT 11 + +#define AC97_MULTI_CHANNEL_SEL 0x74 + +/*#define AC97_VENDOR_ID1 0x7C +#define AC97_VENDOR_ID2 0x7E*/ + +/* + * ASSP control regs + */ +#define DSP_PORT_TIMER_COUNT 0x06 + +#define DSP_PORT_MEMORY_INDEX 0x80 + +#define DSP_PORT_MEMORY_TYPE 0x82 +#define MEMTYPE_INTERNAL_CODE 0x0002 +#define MEMTYPE_INTERNAL_DATA 0x0003 +#define MEMTYPE_MASK 0x0003 + +#define DSP_PORT_MEMORY_DATA 0x84 + +#define DSP_PORT_CONTROL_REG_A 0xA2 +#define DSP_PORT_CONTROL_REG_B 0xA4 +#define DSP_PORT_CONTROL_REG_C 0xA6 + +#define REV_A_CODE_MEMORY_BEGIN 0x0000 +#define REV_A_CODE_MEMORY_END 0x0FFF +#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) + +#define REV_B_CODE_MEMORY_BEGIN 0x0000 +#define REV_B_CODE_MEMORY_END 0x0BFF +#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) + +#define REV_A_DATA_MEMORY_BEGIN 0x1000 +#define REV_A_DATA_MEMORY_END 0x2FFF +#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) + +#define REV_B_DATA_MEMORY_BEGIN 0x1000 +#define REV_B_DATA_MEMORY_END 0x2BFF +#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) + + +#define NUM_UNITS_KERNEL_CODE 16 +#define NUM_UNITS_KERNEL_DATA 2 + +#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 +#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 + +/* + * Kernel data layout + */ + +#define DP_SHIFT_COUNT 7 + +#define KDATA_BASE_ADDR 0x1000 +#define KDATA_BASE_ADDR2 0x1080 + +#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) +#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) +#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) +#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) +#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) +#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) +#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) +#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) +#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) + +#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) +#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) + +#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) +#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) +#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) +#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) +#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) +#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) +#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) +#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) +#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) +#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) + +#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) +#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) + +#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) +#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) + +#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) +#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) + +#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) +#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) +#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) + +#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) +#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) +#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) +#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) +#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) + +#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) +#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) +#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) + +#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) +#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) +#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) + +#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) +#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) +#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) +#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) +#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) +#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) +#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) +#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) +#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) +#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) + +#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) +#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) +#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) + +#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) +#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) + +#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) +#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) +#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) + +#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) +#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) +#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) +#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) +#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) +#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) + +#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) +#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) +#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) +#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) +#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) +#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) + +#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) +#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) +#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) +#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) +#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) +#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) + +#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) +#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) +#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) +#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) + +#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) +#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) + +#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) +#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) + +#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) +#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) +#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) +#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) +#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) + +#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) +#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) + +#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) +#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) +#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) + +#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) +#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) + +#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) + +#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) +#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) +#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) +#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) +#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) +#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) +#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) +#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) +#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) +#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) +#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) +#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) + +#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) +#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) +#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) +#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) + +#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) +#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) + +#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) +#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) +#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) +#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) + +#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) +#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) +#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) +#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) +#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) + +/* + * second 'segment' (?) reserved for mixer + * buffers.. + */ + +#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) +#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) +#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) +#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) +#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) +#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) +#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) +#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) +#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) +#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) +#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) +#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) +#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) +#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) +#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) +#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) + +#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) +#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) +#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) +#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) +#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) +#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) +#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) +#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) +#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) +#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) +#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) + +#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) +#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) +#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) +#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) +#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) +#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) + +#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) +#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) +#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) +#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) + +/* + * client data area offsets + */ +#define CDATA_INSTANCE_READY 0x00 + +#define CDATA_HOST_SRC_ADDRL 0x01 +#define CDATA_HOST_SRC_ADDRH 0x02 +#define CDATA_HOST_SRC_END_PLUS_1L 0x03 +#define CDATA_HOST_SRC_END_PLUS_1H 0x04 +#define CDATA_HOST_SRC_CURRENTL 0x05 +#define CDATA_HOST_SRC_CURRENTH 0x06 + +#define CDATA_IN_BUF_CONNECT 0x07 +#define CDATA_OUT_BUF_CONNECT 0x08 + +#define CDATA_IN_BUF_BEGIN 0x09 +#define CDATA_IN_BUF_END_PLUS_1 0x0A +#define CDATA_IN_BUF_HEAD 0x0B +#define CDATA_IN_BUF_TAIL 0x0C +#define CDATA_OUT_BUF_BEGIN 0x0D +#define CDATA_OUT_BUF_END_PLUS_1 0x0E +#define CDATA_OUT_BUF_HEAD 0x0F +#define CDATA_OUT_BUF_TAIL 0x10 + +#define CDATA_DMA_CONTROL 0x11 +#define CDATA_RESERVED 0x12 + +#define CDATA_FREQUENCY 0x13 +#define CDATA_LEFT_VOLUME 0x14 +#define CDATA_RIGHT_VOLUME 0x15 +#define CDATA_LEFT_SUR_VOL 0x16 +#define CDATA_RIGHT_SUR_VOL 0x17 + +#define CDATA_HEADER_LEN 0x18 + +#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN +#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) +#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) +#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) +#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) +#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) +#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) +#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) + +#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) +#define MINISRC_BIQUAD_STAGE 2 +#define MINISRC_COEF_LOC 0x175 + +#define DMACONTROL_BLOCK_MASK 0x000F +#define DMAC_BLOCK0_SELECTOR 0x0000 +#define DMAC_BLOCK1_SELECTOR 0x0001 +#define DMAC_BLOCK2_SELECTOR 0x0002 +#define DMAC_BLOCK3_SELECTOR 0x0003 +#define DMAC_BLOCK4_SELECTOR 0x0004 +#define DMAC_BLOCK5_SELECTOR 0x0005 +#define DMAC_BLOCK6_SELECTOR 0x0006 +#define DMAC_BLOCK7_SELECTOR 0x0007 +#define DMAC_BLOCK8_SELECTOR 0x0008 +#define DMAC_BLOCK9_SELECTOR 0x0009 +#define DMAC_BLOCKA_SELECTOR 0x000A +#define DMAC_BLOCKB_SELECTOR 0x000B +#define DMAC_BLOCKC_SELECTOR 0x000C +#define DMAC_BLOCKD_SELECTOR 0x000D +#define DMAC_BLOCKE_SELECTOR 0x000E +#define DMAC_BLOCKF_SELECTOR 0x000F +#define DMACONTROL_PAGE_MASK 0x00F0 +#define DMAC_PAGE0_SELECTOR 0x0030 +#define DMAC_PAGE1_SELECTOR 0x0020 +#define DMAC_PAGE2_SELECTOR 0x0010 +#define DMAC_PAGE3_SELECTOR 0x0000 +#define DMACONTROL_AUTOREPEAT 0x1000 +#define DMACONTROL_STOPPED 0x2000 +#define DMACONTROL_DIRECTION 0x0100 + +/* + * an arbitrary volume we set the internal + * volume settings to so that the ac97 volume + * range is a little less insane. 0x7fff is + * max. + */ +#define ARB_VOLUME ( 0x6800 ) + +/* + */ + +typedef struct snd_m3_dma m3_dma_t; +typedef struct snd_m3 m3_t; +#define chip_t m3_t + + +/* quirk lists */ +struct m3_quirk { + const char *name; /* device name */ + u16 vendor, device; /* subsystem ids */ + int amp_gpio; /* gpio pin # for external amp, -1 = default */ + int irda_workaround; /* non-zero if avoid to touch 0x10 on GPIO_DIRECTION + (e.g. for IrDA on Dell Inspirons) */ +}; + +struct m3_list { + int curlen; + int mem_addr; + int max; +}; + +struct snd_m3_dma { + + int number; + m3_t *chip; + snd_pcm_substream_t *substream; + + struct assp_instance { + unsigned short code, data; + } inst; + + int running; + int opened; + + unsigned long buffer_addr; + int dma_size; + int period_size; + unsigned int hwptr; + int count; + + int index[3]; + struct m3_list *index_list[3]; + + int in_lists; + + struct list_head list; + +}; + +struct snd_m3 { + + snd_card_t *card; + + unsigned long iobase; + struct resource *iobase_res; + + int irq; + int allegro_flag : 1; + + ac97_t *ac97; + + snd_pcm_t *pcm; + + struct pci_dev *pci; + struct m3_quirk *quirk; + + int dacs_active; + int timer_users; + + struct m3_list msrc_list; + struct m3_list mixer_list; + struct m3_list adc1_list; + struct m3_list dma_list; + + /* for storing reset state..*/ + u8 reset_state; + + int external_amp; + int amp_gpio; + + /* pcm streams */ + int num_substreams; + m3_dma_t *substreams; + + spinlock_t reg_lock; + +#ifdef CONFIG_PM + u16 *suspend_mem; +#endif +}; + +/* + * pci ids + */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125D +#endif +#ifndef PCI_DEVICE_ID_ESS_ALLEGRO_1 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#endif +#ifndef PCI_DEVICE_ID_ESS_ALLEGRO +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#endif +#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2LE +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#endif +#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_1 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_HW +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_2 +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b +#endif + +static struct pci_device_id snd_m3_ids[] __devinitdata = { + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2LE, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_1, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_HW, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_2, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, snd_m3_ids); + +static struct m3_quirk m3_quirk_list[] = { + /* panasonic CF-28 "toughbook" */ + { + .name = "Panasonic CF-28", + .vendor = 0x10f7, + .device = 0x833e, + .amp_gpio = 0x0d, + }, + /* panasonic CF-72 "toughbook" */ + { + .name = "Panasonic CF-72", + .vendor = 0x10f7, + .device = 0x833d, + .amp_gpio = 0x0d, + }, + /* Dell Inspiron 4000 */ + { + .name = "Dell Inspiron 4000", + .vendor = 0x1028, + .device = 0x00b0, + .amp_gpio = -1, + .irda_workaround = 1, + }, + /* Dell Inspiron 8000 */ + { + .name = "Dell Inspiron 8000", + .vendor = 0x1028, + .device = 0x00a4, + .amp_gpio = -1, + .irda_workaround = 1, + }, + /* FIXME: Inspiron 8100 id should probably be here, too + * (8200 irrelevant: has intel8x0 with CS4205) */ + /* END */ + { 0 } +}; + + +/* + * lowlevel functions + */ + +inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg) +{ + outw(value, chip->iobase + reg); +} + +inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg) +{ + return inw(chip->iobase + reg); +} + +inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg) +{ + outb(value, chip->iobase + reg); +} + +inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg) +{ + return inb(chip->iobase + reg); +} + +/* + * access 16bit words to the code or data regions of the dsp's memory. + * index addresses 16bit words. + */ +static u16 snd_m3_assp_read(m3_t *chip, u16 region, u16 index) +{ + snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX); + return snd_m3_inw(chip, DSP_PORT_MEMORY_DATA); +} + +static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data) +{ + snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX); + snd_m3_outw(chip, data, DSP_PORT_MEMORY_DATA); +} + +static void snd_m3_assp_halt(m3_t *chip) +{ + chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; + mdelay(10); + snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +static void snd_m3_assp_continue(m3_t *chip) +{ + snd_m3_outb(chip, chip->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + + +/* + * This makes me sad. the maestro3 has lists + * internally that must be packed.. 0 terminates, + * apparently, or maybe all unused entries have + * to be 0, the lists have static lengths set + * by the binary code images. + */ + +static int snd_m3_add_list(m3_t *chip, struct m3_list *list, u16 val) +{ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + list->curlen, + val); + return list->curlen++; +} + +static void snd_m3_remove_list(m3_t *chip, struct m3_list *list, int index) +{ + u16 val; + int lastindex = list->curlen - 1; + + if (index != lastindex) { + val = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + index, + val); + } + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex, + 0); + + list->curlen--; +} + +static void snd_m3_inc_timer_users(m3_t *chip) +{ + chip->timer_users++; + if (chip->timer_users != 1) + return; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 240); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 240); + + snd_m3_outw(chip, + snd_m3_inw(chip, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +} + +static void snd_m3_dec_timer_users(m3_t *chip) +{ + chip->timer_users--; + if (chip->timer_users > 0) + return; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 0); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 0); + + snd_m3_outw(chip, + snd_m3_inw(chip, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +} + +/* + * start/stop + */ + +/* spinlock held! */ +static int snd_m3_pcm_start(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + if (! s || ! subs) + return -EINVAL; + + snd_m3_inc_timer_users(chip); + switch (subs->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + chip->dacs_active++; + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 1); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + chip->dacs_active); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_m3_assp_write(s->chip, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 1); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 1); + break; + } + return 0; +} + +/* spinlock held! */ +static int snd_m3_pcm_stop(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + if (! s || ! subs) + return -EINVAL; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 0); + snd_m3_dec_timer_users(chip); + switch (subs->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + chip->dacs_active--; + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + chip->dacs_active); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 0); + break; + } + return 0; +} + +static int +snd_m3_pcm_trigger(snd_pcm_substream_t *subs, int cmd) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; + unsigned long flags; + int err = -EINVAL; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (s->running) + err = -EBUSY; + else { + s->running = 1; + err = snd_m3_pcm_start(chip, s, subs); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (! s->running) + err = 0; /* should return error? */ + else { + s->running = 0; + err = snd_m3_pcm_stop(chip, s, subs); + } + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + +/* + * setup + */ +static void +snd_m3_pcm_setup1(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int dsp_in_size, dsp_out_size, dsp_in_buffer, dsp_out_buffer; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + } else { + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x10 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + } + dsp_in_buffer = s->inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + + s->dma_size = frames_to_bytes(runtime, runtime->buffer_size); + s->period_size = frames_to_bytes(runtime, runtime->period_size); + s->hwptr = 0; + s->count = 0; + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_ADDRL, + LO(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_ADDRH, + HI(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(s->buffer_addr + s->dma_size)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(s->buffer_addr + s->dma_size)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTL, + LO(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH, + HI(s->buffer_addr)); +#undef LO +#undef HI + + /* dsp buffers */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); +} + +static void snd_m3_pcm_setup2(m3_t *chip, m3_dma_t *s, snd_pcm_runtime_t *runtime) +{ + u32 freq; + + /* + * put us in the lists if we're not already there + */ + if (! s->in_lists) { + s->index[0] = snd_m3_add_list(chip, s->index_list[0], + s->inst.data >> DP_SHIFT_COUNT); + s->index[1] = snd_m3_add_list(chip, s->index_list[1], + s->inst.data >> DP_SHIFT_COUNT); + s->index[2] = snd_m3_add_list(chip, s->index_list[2], + s->inst.data >> DP_SHIFT_COUNT); + s->in_lists = 1; + } + + /* write to 'mono' word */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 1, + runtime->channels == 2 ? 0 : 1); + /* write to '8bit' word */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 2, + snd_pcm_format_width(runtime->format) == 16 ? 0 : 1); + + /* set up dac/adc rate */ + freq = ((runtime->rate << 15) + 24000 ) / 48000; + if (freq) + freq--; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_FREQUENCY, + freq); +} + + +static struct play_vals { + u16 addr, val; +} pv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 0} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ + {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ +}; + + +/* the mode passed should be already shifted and masked */ +static void +snd_m3_playback_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + unsigned int i; + + /* + * some per client initializers + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 12, + s->inst.data + 40 + 8); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 19, + s->inst.code + MINISRC_COEF_LOC); + + /* enable or disable low pass filter? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 22, + subs->runtime->rate > 45000 ? 0xff : 0); + + /* tell it which way dma is going? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_DMA_CONTROL, + DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for (i = 0; i < ARRAY_SIZE(pv); i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + pv[i].addr, pv[i].val); +} + +/* + * Native record driver + */ +static struct rec_vals { + u16 addr, val; +} rv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 1} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ + {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ + {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ +}; + +static void +snd_m3_capture_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + unsigned int i; + + /* + * some per client initializers + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 12, + s->inst.data + 40 + 8); + + /* tell it which way dma is going? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_DMA_CONTROL, + DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for (i = 0; i < ARRAY_SIZE(rv); i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + rv[i].addr, rv[i].val); +} + +static int snd_m3_pcm_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + m3_dma_t *s = (m3_dma_t*) substream->runtime->private_data; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + /* set buffer address */ + s->buffer_addr = substream->runtime->dma_addr; + if (s->buffer_addr & 0x3) { + snd_printk("oh my, not aligned\n"); + s->buffer_addr = s->buffer_addr & ~0x3; + } + return 0; +} + +static int snd_m3_pcm_hw_free(snd_pcm_substream_t * substream) +{ + m3_dma_t *s; + + if (substream->runtime->private_data == NULL) + return 0; + s = (m3_dma_t*) substream->runtime->private_data; + snd_pcm_lib_free_pages(substream); + s->buffer_addr = 0; + return 0; +} + +static int +snd_m3_pcm_prepare(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + m3_dma_t *s = (m3_dma_t*)runtime->private_data; + unsigned long flags; + + snd_assert(s != NULL, return -ENXIO); + + if (runtime->format != SNDRV_PCM_FORMAT_U8 && + runtime->format != SNDRV_PCM_FORMAT_S16_LE) + return -EINVAL; + if (runtime->rate > 48000 || + runtime->rate < 8000) + return -EINVAL; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_m3_pcm_setup1(chip, s, subs); + + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_m3_playback_setup(chip, s, subs); + else + snd_m3_capture_setup(chip, s, subs); + + snd_m3_pcm_setup2(chip, s, runtime); + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +/* + * get current pointer + */ +static unsigned int +snd_m3_get_pointer(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + u16 hi = 0, lo = 0; + int retry = 10; + u32 addr; + + /* + * try and get a valid answer + */ + while (retry--) { + hi = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH); + + lo = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTL); + + if (hi == snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH)) + break; + } + addr = lo | ((u32)hi<<16); + return (unsigned int)(addr - s->buffer_addr); +} + +static snd_pcm_uframes_t +snd_m3_pcm_pointer(snd_pcm_substream_t * subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; + snd_assert(s != NULL, return 0); + return bytes_to_frames(subs->runtime, snd_m3_get_pointer(chip, s, subs)); +} + + +/* update pointer */ +/* spinlock held! */ +static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s) +{ + snd_pcm_substream_t *subs = s->substream; + unsigned int hwptr; + int diff; + + if (! s->running) + return; + + hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size; + diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size; + s->hwptr = hwptr; + s->count += diff; + if (s->count >= (signed)s->period_size) { + s->count %= s->period_size; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(subs); + spin_lock(&chip->reg_lock); + } +} + +static void +snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + m3_t *chip = snd_magic_cast(m3_t, dev_id, ); + u8 status; + int i; + + status = inb(chip->iobase + 0x1A); + + if (status == 0xff) + return; + + /* presumably acking the ints? */ + outw(status, chip->iobase + 0x1A); + + /*if (in_suspend) + return;*/ + + /* + * ack an assp int if its running + * and has an int pending + */ + if (status & ASSP_INT_PENDING) { + u8 ctl = inb(chip->iobase + ASSP_CONTROL_B); + if (!(ctl & STOP_ASSP_CLOCK)) { + ctl = inb(chip->iobase + ASSP_HOST_INT_STATUS); + if (ctl & DSP2HOST_REQ_TIMER) { + outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS); + /* update adc/dac info if it was a timer int */ + spin_lock(&chip->reg_lock); + for (i = 0; i < chip->num_substreams; i++) { + m3_dma_t *s = &chip->substreams[i]; + if (s->running) + snd_m3_update_ptr(chip, s); + } + spin_unlock(&chip->reg_lock); + } + } + } + + /* XXX is this needed? */ + if (status & 0x40) + outb(0x40, chip->iobase+0x1A); +} + + +/* + */ + +static snd_pcm_hardware_t snd_m3_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (512*1024), + .period_bytes_min = 64, + .period_bytes_max = (512*1024), + .periods_min = 1, + .periods_max = 1024, +}; + +static snd_pcm_hardware_t snd_m3_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (512*1024), + .period_bytes_min = 64, + .period_bytes_max = (512*1024), + .periods_min = 1, + .periods_max = 1024, +}; + + +/* + */ + +static int +snd_m3_substream_open(m3_t *chip, snd_pcm_substream_t *subs) +{ + int i; + m3_dma_t *s; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < chip->num_substreams; i++) { + s = &chip->substreams[i]; + if (! s->opened) + goto __found; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENOMEM; +__found: + s->opened = 1; + s->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + subs->runtime->private_data = s; + s->substream = subs; + + /* set list owners */ + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) { + s->index_list[0] = &chip->mixer_list; + } else + s->index_list[0] = &chip->adc1_list; + s->index_list[1] = &chip->msrc_list; + s->index_list[2] = &chip->dma_list; + + return 0; +} + +static void +snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs) +{ + m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data; + unsigned long flags; + + if (s == NULL) + return; /* not opened properly */ + + spin_lock_irqsave(&chip->reg_lock, flags); + if (s->substream && s->running) + snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */ + if (s->in_lists) { + snd_m3_remove_list(chip, s->index_list[0], s->index[0]); + snd_m3_remove_list(chip, s->index_list[1], s->index[1]); + snd_m3_remove_list(chip, s->index_list[2], s->index[2]); + s->in_lists = 0; + } + s->running = 0; + s->opened = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int +snd_m3_playback_open(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int err; + + if ((err = snd_m3_substream_open(chip, subs)) < 0) + return err; + + runtime->hw = snd_m3_playback; + snd_pcm_set_sync(subs); + + return 0; +} + +static int +snd_m3_playback_close(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + + snd_m3_substream_close(chip, subs); + return 0; +} + +static int +snd_m3_capture_open(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int err; + + if ((err = snd_m3_substream_open(chip, subs)) < 0) + return err; + + runtime->hw = snd_m3_capture; + snd_pcm_set_sync(subs); + + return 0; +} + +static int +snd_m3_capture_close(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + + snd_m3_substream_close(chip, subs); + return 0; +} + +/* + * create pcm instance + */ + +static snd_pcm_ops_t snd_m3_playback_ops = { + .open = snd_m3_playback_open, + .close = snd_m3_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_m3_pcm_hw_params, + .hw_free = snd_m3_pcm_hw_free, + .prepare = snd_m3_pcm_prepare, + .trigger = snd_m3_pcm_trigger, + .pointer = snd_m3_pcm_pointer, +}; + +static snd_pcm_ops_t snd_m3_capture_ops = { + .open = snd_m3_capture_open, + .close = snd_m3_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_m3_pcm_hw_params, + .hw_free = snd_m3_pcm_hw_free, + .prepare = snd_m3_pcm_prepare, + .trigger = snd_m3_pcm_trigger, + .pointer = snd_m3_pcm_pointer, +}; + +static int __devinit +snd_m3_pcm(m3_t * chip, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(chip->card, chip->card->driver, device, + MAX_PLAYBACKS, MAX_CAPTURES, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_m3_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_m3_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->driver); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + return 0; +} + + +/* + * ac97 interface + */ + +/* + * Wait for the ac97 serial bus to be free. + * return nonzero if the bus is still busy. + */ +static int snd_m3_ac97_wait(m3_t *chip) +{ + int i = 10000; + + do { + if (! (snd_m3_inb(chip, 0x30) & 1)) + return 0; + } while (i-- > 0); + + snd_printk("ac97 serial bus busy\n"); + return 1; +} + +static unsigned short +snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) +{ + m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return -ENXIO); + unsigned short ret = 0; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_m3_ac97_wait(chip)) + goto __error; + snd_m3_outb(chip, 0x80 | (reg & 0x7f), 0x30); + if (snd_m3_ac97_wait(chip)) + goto __error; + ret = snd_m3_inw(chip, 0x32); +__error: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +static void +snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_m3_ac97_wait(chip)) + goto __error; + snd_m3_outw(chip, val, 0x32); + snd_m3_outb(chip, reg & 0x7f, 0x30); +__error: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + + +static void snd_m3_remote_codec_config(int io, int isremote) +{ + isremote = isremote ? 1 : 0; + + outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, + io + RING_BUS_CTRL_B); + outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, + io + SDO_OUT_DEST_CTRL); + outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, + io + SDO_IN_DEST_CTRL); +} + +/* + * hack, returns non zero on err + */ +static int snd_m3_try_read_vendor(m3_t *chip) +{ + u16 ret; + + if (snd_m3_ac97_wait(chip)) + return 1; + + snd_m3_outb(chip, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); + + if (snd_m3_ac97_wait(chip)) + return 1; + + ret = snd_m3_inw(chip, 0x32); + + return (ret == 0) || (ret == 0xffff); +} + +static void snd_m3_ac97_reset(m3_t *chip, int busywait) +{ + u16 dir; + int delay1 = 0, delay2 = 0, i; + int io = chip->iobase; + + if (chip->allegro_flag) { + /* + * the onboard codec on the allegro seems + * to want to wait a very long time before + * coming back to life + */ + delay1 = 50; + delay2 = 800; + } else { + /* maestro3 */ + delay1 = 20; + delay2 = 500; + } + + for (i = 0; i < 5; i++) { + dir = inw(io + GPIO_DIRECTION); + if (! chip->quirk || ! chip->quirk->irda_workaround) + dir |= 0x10; /* assuming pci bus master? */ + + snd_m3_remote_codec_config(io, 0); + + outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); + udelay(20); + + outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); + outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); + outw(0, io + GPIO_DATA); + outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); + + if (busywait) { + mdelay(delay1); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay1 * HZ) / 1000); + } + + outw(GPO_PRIMARY_AC97, io + GPIO_DATA); + udelay(5); + /* ok, bring back the ac-link */ + outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); + outw(~0, io + GPIO_MASK); + + if (busywait) { + mdelay(delay2); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay2 * HZ) / 1000); + } + if (! snd_m3_try_read_vendor(chip)) + break; + + delay1 += 10; + delay2 += 100; + + snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n", + delay1, delay2); + } + +#if 0 + /* more gung-ho reset that doesn't + * seem to work anywhere :) + */ + tmp = inw(io + RING_BUS_CTRL_A); + outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); + mdelay(20); + outw(tmp, io + RING_BUS_CTRL_A); + mdelay(50); +#endif +} + +static int snd_m3_mixer(m3_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_m3_ac97_write; + ac97.read = snd_m3_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + /* seems ac97 PCM needs initialization.. hack hack.. */ + snd_ac97_write(chip->ac97, AC97_PCM, 0x8000 | (15 << 8) | 15); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 10); + snd_ac97_write(chip->ac97, AC97_PCM, 0); + + return 0; +} + + +/* + * DSP Code images + */ + +static u16 assp_kernel_image[] = { + 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, + 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, + 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, + 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, + 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, + 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, + 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, + 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, + 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, + 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, + 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, + 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, + 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, + 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, + 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, + 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, + 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, + 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, + 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, + 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, + 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, + 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, + 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, + 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, + 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, + 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, + 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, + 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, + 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, + 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, + 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, + 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, + 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, + 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, + 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, + 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, + 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, + 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, + 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, + 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, + 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, + 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, + 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, + 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, + 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, + 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, + 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, + 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, + 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, + 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, + 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, + 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, + 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, + 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, + 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, + 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, + 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, + 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, + 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, + 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, + 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, + 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, + 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, + 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, + 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, + 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, + 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, + 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, + 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, + 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, + 0xBE3A, +}; + +/* + * Mini sample rate converter code image + * that is to be loaded at 0x400 on the DSP. + */ +static u16 assp_minisrc_image[] = { + + 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, + 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, + 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, + 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, + 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, + 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, + 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, + 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, + 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, + 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, + 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, + 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, + 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, + 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, + 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, + 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, + 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, + 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, + 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, + 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, + 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, + 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, + 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, + 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, + 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, + 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, + 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, + 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, + 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, + 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, + 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, + 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + + +/* + * initialize ASSP + */ + +#define MINISRC_LPF_LEN 10 +static u16 minisrc_lpf[MINISRC_LPF_LEN] = { + 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, + 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F +}; + +static void snd_m3_assp_init(m3_t *chip) +{ + unsigned int i; + + /* zero kernel data */ + for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR + i, 0); + + /* zero mixer data? */ + for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR2 + i, 0); + + /* init dma pointer */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_CURRENT_DMA, + KDATA_DMA_XFER0); + + /* write kernel into code memory.. */ + for (i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + REV_B_CODE_MEMORY_BEGIN + i, + assp_kernel_image[i]); + } + + /* + * We only have this one client and we know that 0x400 + * is free in our kernel's mem map, so lets just + * drop it there. It seems that the minisrc doesn't + * need vectors, so we won't bother with them.. + */ + for (i = 0; i < sizeof(assp_minisrc_image) / 2; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + i, + assp_minisrc_image[i]); + } + + /* + * write the coefficients for the low pass filter? + */ + for (i = 0; i < MINISRC_LPF_LEN ; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + i, + minisrc_lpf[i]); + } + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, + 0x8000); + + /* + * the minisrc is the only thing on + * our task list.. + */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TASK0, + 0x400); + + /* + * init the mixer number.. + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER,0); + + /* + * EXTREME KERNEL MASTER VOLUME + */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); + + chip->mixer_list.curlen = 0; + chip->mixer_list.mem_addr = KDATA_MIXER_XFER0; + chip->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; + chip->adc1_list.curlen = 0; + chip->adc1_list.mem_addr = KDATA_ADC1_XFER0; + chip->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; + chip->dma_list.curlen = 0; + chip->dma_list.mem_addr = KDATA_DMA_XFER0; + chip->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; + chip->msrc_list.curlen = 0; + chip->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; + chip->msrc_list.max = MAX_INSTANCE_MINISRC; +} + + +static int snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index) +{ + int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + + MINISRC_IN_BUFFER_SIZE / 2 + + 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); + int address, i; + + /* + * the revb memory map has 0x1100 through 0x1c00 + * free. + */ + + /* + * align instance address to 256 bytes so that it's + * shifted list address is aligned. + * list address = (mem address >> 1) >> 7; + */ + data_bytes = (data_bytes + 255) & ~255; + address = 0x1100 + ((data_bytes/2) * index); + + if ((address + (data_bytes/2)) >= 0x1c00) { + snd_printk("no memory for %d bytes at ind %d (addr 0x%x)\n", + data_bytes, index, address); + return -ENOMEM; + } + + s->number = index; + s->inst.code = 0x400; + s->inst.data = address; + + for (i = data_bytes / 2; i > 0; address++, i--) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + address, 0); + } + + return 0; +} + + +/* + * this works for the reference board, have to find + * out about others + * + * this needs more magic for 4 speaker, but.. + */ +static void +snd_m3_amp_enable(m3_t *chip, int enable) +{ + int io = chip->iobase; + u16 gpo, polarity; + + if (! chip->external_amp) + return; + + polarity = enable ? 0 : 1; + polarity = polarity << chip->amp_gpio; + gpo = 1 << chip->amp_gpio; + + outw(~gpo, io + GPIO_MASK); + + outw(inw(io + GPIO_DIRECTION) | gpo, + io + GPIO_DIRECTION); + + outw((GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity), + io + GPIO_DATA); + + outw(0xffff, io + GPIO_MASK); +} + +static int +snd_m3_chip_init(m3_t *chip) +{ + struct pci_dev *pcidev = chip->pci; + u32 n; + u8 t; /* makes as much sense as 'n', no? */ + + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= REDUCED_DEBOUNCE; + n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + outb(RESET_ASSP, chip->iobase + ASSP_CONTROL_B); + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= ~INT_CLK_SELECT; + if (!chip->allegro_flag) { + n &= ~INT_CLK_MULT_ENABLE; + n |= INT_CLK_SRC_NOT_PCI; + } + n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + if (chip->allegro_flag) { + pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); + n |= IN_CLK_12MHZ_SELECT; + pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); + } + + t = inb(chip->iobase + ASSP_CONTROL_A); + t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); + t |= ASSP_CLK_49MHZ_SELECT; + t |= ASSP_0_WS_ENABLE; + outb(t, chip->iobase + ASSP_CONTROL_A); + + outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); + + return 0; +} + +static void +snd_m3_enable_ints(m3_t *chip) +{ + unsigned long io = chip->iobase; + + outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); + outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, + io + ASSP_CONTROL_C); +} + + +/* + */ + +static int snd_m3_free(m3_t *chip) +{ + unsigned long flags; + m3_dma_t *s; + int i; + + if (chip->substreams) { + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < chip->num_substreams; i++) { + s = &chip->substreams[i]; + /* check surviving pcms; this should not happen though.. */ + if (s->substream && s->running) + snd_m3_pcm_stop(chip, s, s->substream); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + kfree(chip->substreams); + } +#ifdef CONFIG_PM + if (chip->suspend_mem) + vfree(chip->suspend_mem); +#endif + + if (chip->irq >= 0) + synchronize_irq(chip->irq); + + if (chip->iobase_res) { + release_resource(chip->iobase_res); + kfree_nocheck(chip->iobase_res); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + + +/* + * APM support + */ +#ifdef CONFIG_PM + +static void m3_suspend(m3_t *chip) +{ + snd_card_t *card = chip->card; + int i, index; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + snd_pcm_suspend_all(chip->pcm); + + mdelay(10); /* give the assp a chance to idle.. */ + + snd_m3_assp_halt(chip); + + /* save dsp image */ + index = 0; + for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) + chip->suspend_mem[index++] = + snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i); + for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + chip->suspend_mem[index++] = + snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); + + /* power down apci registers */ + snd_m3_outw(chip, 0xffff, 0x54); + snd_m3_outw(chip, 0xffff, 0x56); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void m3_resume(m3_t *chip) +{ + snd_card_t *card = chip->card; + int i, index; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + /* first lets just bring everything back. .*/ + snd_m3_outw(chip, 0, 0x54); + snd_m3_outw(chip, 0, 0x56); + + snd_m3_chip_init(chip); + snd_m3_assp_halt(chip); + snd_m3_ac97_reset(chip, 1); + + /* restore dsp image */ + index = 0; + for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, + chip->suspend_mem[index++]); + for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, + chip->suspend_mem[index++]); + + /* tell the dma engine to restart itself */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DMA_ACTIVE, 0); + + /* restore ac97 registers */ + snd_ac97_resume(chip->ac97); + + snd_m3_assp_continue(chip); + snd_m3_enable_ints(chip); + snd_m3_amp_enable(chip, 1); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_m3_suspend(struct pci_dev *pci, u32 state) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO); + m3_suspend(chip); + return 0; +} +static int snd_m3_resume(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO); + m3_resume(chip); + return 0; +} +#else +static void snd_m3_suspend(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + m3_suspend(chip); +} +static void snd_m3_resume(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + m3_resume(chip); +} +#endif + +/* callback */ +static int snd_m3_set_power_state(snd_card_t *card, unsigned int power_state) +{ + m3_t *chip = snd_magic_cast(m3_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + m3_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + m3_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + + +/* + */ + +static int snd_m3_dev_free(snd_device_t *device) +{ + m3_t *chip = snd_magic_cast(m3_t, device->device_data, return -ENXIO); + return snd_m3_free(chip); +} + +static int __devinit +snd_m3_create(snd_card_t *card, struct pci_dev *pci, + int enable_amp, + int amp_gpio, + m3_t **chip_ret) +{ + m3_t *chip; + int i, err; + struct m3_quirk *quirk; + u16 subsystem_vendor, subsystem_device; + static snd_device_ops_t ops = { + .dev_free = snd_m3_dev_free, + }; + + *chip_ret = NULL; + + if (pci_enable_device(pci)) + return -EIO; + + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = snd_magic_kcalloc(m3_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spin_lock_init(&chip->reg_lock); + switch (pci->device) { + case PCI_DEVICE_ID_ESS_ALLEGRO: + case PCI_DEVICE_ID_ESS_ALLEGRO_1: + case PCI_DEVICE_ID_ESS_CANYON3D_2LE: + case PCI_DEVICE_ID_ESS_CANYON3D_2: + chip->allegro_flag = 1; + break; + } + + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); + + for (quirk = m3_quirk_list; quirk->vendor; quirk++) { + if (subsystem_vendor == quirk->vendor && + subsystem_device == quirk->device) { + printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name); + chip->quirk = quirk; + break; + } + } + + chip->external_amp = enable_amp; + if (amp_gpio >= 0 && amp_gpio <= 0x0f) + chip->amp_gpio = amp_gpio; + else if (chip->quirk && chip->quirk->amp_gpio >= 0) + chip->amp_gpio = chip->quirk->amp_gpio; + else if (chip->allegro_flag) + chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; + else /* presumably this is for all 'maestro3's.. */ + chip->amp_gpio = GPO_EXT_AMP_M3; + + chip->num_substreams = NR_DSPS; + chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL); + if (chip->substreams == NULL) { + snd_magic_kfree(chip); + return -ENOMEM; + } + memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams); + + chip->iobase = pci_resource_start(pci, 0); + if ((chip->iobase_res = request_region(chip->iobase, 256, + card->driver)) == NULL) { + snd_m3_free(chip); + snd_printk("unable to grab i/o ports %ld\n", chip->iobase); + return -EBUSY; + } + + /* just to be sure */ + pci_set_master(pci); + + snd_m3_chip_init(chip); + snd_m3_assp_halt(chip); + + snd_m3_ac97_reset(chip, 0); + + if ((err = snd_m3_mixer(chip)) < 0) { + snd_m3_free(chip); + return err; + } + + snd_m3_assp_init(chip); + snd_m3_amp_enable(chip, 1); + + for (i = 0; i < chip->num_substreams; i++) { + m3_dma_t *s = &chip->substreams[i]; + s->chip = chip; + if ((err = snd_m3_assp_client_init(chip, s, i)) < 0) { + snd_m3_free(chip); + return err; + } + } + + if ((err = snd_m3_pcm(chip, 0)) < 0) { + snd_m3_free(chip); + return err; + } + + if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void *)chip)) { + snd_m3_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -ENOMEM; + } + chip->irq = pci->irq; + +#ifdef CONFIG_PM + chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH)); + if (chip->suspend_mem == NULL) + snd_printk("can't allocate apm buffer\n"); + card->set_power_state = snd_m3_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_m3_free(chip); + return err; + } + + snd_m3_enable_ints(chip); + snd_m3_assp_continue(chip); + + *chip_ret = chip; + + return 0; +} + +/* + */ +static int __devinit +snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + m3_t *chip; + int err; + + /* don't pick up modems */ + if (((pci->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return -ENODEV; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_ESS_ALLEGRO: + case PCI_DEVICE_ID_ESS_ALLEGRO_1: + strcpy(card->driver, "Allegro"); + break; + case PCI_DEVICE_ID_ESS_CANYON3D_2LE: + case PCI_DEVICE_ID_ESS_CANYON3D_2: + strcpy(card->driver, "Canyon3D-2"); + break; + default: + strcpy(card->driver, "Maestro3"); + break; + } + + if ((err = snd_m3_create(card, pci, + external_amp[dev], + amp_gpio[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "ESS %s PCI", card->driver); + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->iobase, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_m3_remove(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Maestro3", + .id_table = snd_m3_ids, + .probe = snd_m3_probe, + .remove = __devexit_p(snd_m3_remove), +#ifdef CONFIG_PM + .suspend = snd_m3_suspend, + .resume = snd_m3_resume, +#endif +}; + +static int __init alsa_card_m3_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Maestro3/Allegro soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_m3_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_m3_init) +module_exit(alsa_card_m3_exit) + +#ifndef MODULE + +/* format is: snd-maestro3=enable,index,id,external_amp,amp_gpio */ + +static int __init alsa_card_maestro3_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&external_amp[nr_dev]) == 2 && + get_option(&str,&_gpio[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-maestro3=", alsa_card_maestro3_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/nm256/Makefile linux/sound/pci/nm256/Makefile --- linux-2.4.21-rc1.orig/sound/pci/nm256/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/nm256/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _nm256.o + +list-multi := snd-nm256.o + +snd-nm256-objs := nm256.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_NM256) += snd-nm256.o + +include $(TOPDIR)/Rules.make + +snd-nm256.o: $(snd-nm256-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-nm256-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/nm256/nm256.c linux/sound/pci/nm256/nm256.c --- linux-2.4.21-rc1.orig/sound/pci/nm256/nm256.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/nm256/nm256.c 2003-02-14 04:25:19.000000000 -0700 @@ -0,0 +1,1700 @@ +/* + * Driver for NeoMagic 256AV and 256ZX chipsets. + * Copyright (c) 2000 by Takashi Iwai + * + * Based on nm256_audio.c OSS driver in linux kernel. + * The original author of OSS nm256 driver wishes to remain anonymous, + * so I just put my acknoledgment to him/her here. + * The original author's web page is found at + * http://www.uglx.org/sony.html + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define CARD_NAME "NeoMagic 256AV/ZX" +#define DRIVER_NAME "NM256" + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{NeoMagic,NM256AV}," + "{NeoMagic,NM256ZX}}"); + +/* + * some compile conditions. + */ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int playback_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16}; +static int capture_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16}; +static int force_ac97[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled as default */ +static int buffer_top[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* not specified */ +static int use_cache[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */ +static int vaio_hack[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(playback_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(playback_bufsize, SNDRV_ENABLED); +MODULE_PARM(capture_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(capture_bufsize, SNDRV_ENABLED); +MODULE_PARM(force_ac97, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(force_ac97, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(buffer_top, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(buffer_top, SNDRV_ENABLED); +MODULE_PARM(use_cache, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access."); +MODULE_PARM_SYNTAX(use_cache, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(vaio_hack, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks."); +MODULE_PARM_SYNTAX(vaio_hack, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +/* + * hw definitions + */ + +/* The BIOS signature. */ +#define NM_SIGNATURE 0x4e4d0000 +/* Signature mask. */ +#define NM_SIG_MASK 0xffff0000 + +/* Size of the second memory area. */ +#define NM_PORT2_SIZE 4096 + +/* The base offset of the mixer in the second memory area. */ +#define NM_MIXER_OFFSET 0x600 + +/* The maximum size of a coefficient entry. */ +#define NM_MAX_PLAYBACK_COEF_SIZE 0x5000 +#define NM_MAX_RECORD_COEF_SIZE 0x1260 + +/* The interrupt register. */ +#define NM_INT_REG 0xa04 +/* And its bits. */ +#define NM_PLAYBACK_INT 0x40 +#define NM_RECORD_INT 0x100 +#define NM_MISC_INT_1 0x4000 +#define NM_MISC_INT_2 0x1 +#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1) + +/* The AV's "mixer ready" status bit and location. */ +#define NM_MIXER_STATUS_OFFSET 0xa04 +#define NM_MIXER_READY_MASK 0x0800 +#define NM_MIXER_PRESENCE 0xa06 +#define NM_PRESENCE_MASK 0x0050 +#define NM_PRESENCE_VALUE 0x0040 + +/* + * For the ZX. It uses the same interrupt register, but it holds 32 + * bits instead of 16. + */ +#define NM2_PLAYBACK_INT 0x10000 +#define NM2_RECORD_INT 0x80000 +#define NM2_MISC_INT_1 0x8 +#define NM2_MISC_INT_2 0x2 +#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X)) + +/* The ZX's "mixer ready" status bit and location. */ +#define NM2_MIXER_STATUS_OFFSET 0xa06 +#define NM2_MIXER_READY_MASK 0x0800 + +/* The playback registers start from here. */ +#define NM_PLAYBACK_REG_OFFSET 0x0 +/* The record registers start from here. */ +#define NM_RECORD_REG_OFFSET 0x200 + +/* The rate register is located 2 bytes from the start of the register area. */ +#define NM_RATE_REG_OFFSET 2 + +/* Mono/stereo flag, number of bits on playback, and rate mask. */ +#define NM_RATE_STEREO 1 +#define NM_RATE_BITS_16 2 +#define NM_RATE_MASK 0xf0 + +/* Playback enable register. */ +#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) +#define NM_PLAYBACK_ENABLE_FLAG 1 +#define NM_PLAYBACK_ONESHOT 2 +#define NM_PLAYBACK_FREERUN 4 + +/* Mutes the audio output. */ +#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) +#define NM_AUDIO_MUTE_LEFT 0x8000 +#define NM_AUDIO_MUTE_RIGHT 0x0080 + +/* Recording enable register. */ +#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) +#define NM_RECORD_ENABLE_FLAG 1 +#define NM_RECORD_FREERUN 2 + +/* coefficient buffer pointer */ +#define NM_COEFF_START_OFFSET 0x1c +#define NM_COEFF_END_OFFSET 0x20 + +/* DMA buffer offsets */ +#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) +#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) +#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) +#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) + +#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) +#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) +#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) +#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) + +/* + * type definitions + */ + +typedef struct snd_nm256 nm256_t; +typedef struct snd_nm256_stream nm256_stream_t; +#define chip_t nm256_t + +struct snd_nm256_stream { + + nm256_t *chip; + snd_pcm_substream_t *substream; + int running; + + u32 buf; /* offset from chip->buffer */ + int bufsize; /* buffer size in bytes */ + unsigned long bufptr; /* mapped pointer */ + unsigned long bufptr_addr; /* physical address of the mapped pointer */ + + int dma_size; /* buffer size of the substream in bytes */ + int period_size; /* period size in bytes */ + int periods; /* # of periods */ + int shift; /* bit shifts */ + int cur_period; /* current period # */ + +}; + +struct snd_nm256 { + + snd_card_t *card; + + unsigned long cport; /* control port */ + struct resource *res_cport; /* its resource */ + unsigned long cport_addr; /* physical address */ + + unsigned long buffer; /* buffer */ + struct resource *res_buffer; /* its resource */ + unsigned long buffer_addr; /* buffer phyiscal address */ + + u32 buffer_start; /* start offset from pci resource 0 */ + u32 buffer_end; /* end offset */ + u32 buffer_size; /* total buffer size */ + + u32 all_coeff_buf; /* coefficient buffer */ + u32 coeff_buf[2]; /* coefficient buffer for each stream */ + + int coeffs_current; /* coeff. table is loaded? */ + int use_cache; /* use one big coef. table */ + + int mixer_base; /* register offset of ac97 mixer */ + int mixer_status_offset; /* offset of mixer status reg. */ + int mixer_status_mask; /* bit mask to test the mixer status */ + + int irq; + void (*interrupt)(int, void *, struct pt_regs *); + int badintrcount; /* counter to check bogus interrupts */ + + nm256_stream_t streams[2]; + + ac97_t *ac97; + + snd_pcm_t *pcm; + + struct pci_dev *pci; + + spinlock_t reg_lock; + +}; + + +/* + * include coefficient table + */ +#include "nm256_coef.c" + + +/* + * PCI ids + */ + +#ifndef PCI_VENDOR_ID_NEOMAGIC +#define PCI_VENDOR_ID_NEOMEGIC 0x10c8 +#endif +#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#endif +#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#endif + + +static struct pci_device_id snd_nm256_ids[] __devinitdata = { + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, snd_nm256_ids); + + +/* + * lowlvel stuffs + */ + +inline static u8 +snd_nm256_readb(nm256_t *chip, int offset) +{ + return readb(chip->cport + offset); +} + +inline static u16 +snd_nm256_readw(nm256_t *chip, int offset) +{ + return readw(chip->cport + offset); +} + +inline static u32 +snd_nm256_readl(nm256_t *chip, int offset) +{ + return readl(chip->cport + offset); +} + +inline static void +snd_nm256_writeb(nm256_t *chip, int offset, u8 val) +{ + writeb(val, chip->cport + offset); +} + +inline static void +snd_nm256_writew(nm256_t *chip, int offset, u16 val) +{ + writew(val, chip->cport + offset); +} + +inline static void +snd_nm256_writel(nm256_t *chip, int offset, u32 val) +{ + writel(val, chip->cport + offset); +} + +inline static void +snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size) +{ + offset -= chip->buffer_start; +#ifdef SNDRV_CONFIG_DEBUG + if (offset < 0 || offset >= chip->buffer_size) { + snd_printk("write_buffer invalid offset = %d size = %d\n", offset, size); + return; + } +#endif + memcpy_toio(chip->buffer + offset, src, size); +} + +/* + * coefficient handlers -- what a magic! + */ + +static u16 +snd_nm256_get_start_offset(int which) +{ + u16 offset = 0; + while (which-- > 0) + offset += coefficient_sizes[which]; + return offset; +} + +static void +snd_nm256_load_one_coefficient(nm256_t *chip, int stream, u32 port, int which) +{ + u32 coeff_buf = chip->coeff_buf[stream]; + u16 offset = snd_nm256_get_start_offset(which); + u16 size = coefficient_sizes[which]; + + snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size); + snd_nm256_writel(chip, port, coeff_buf); + /* ??? Record seems to behave differently than playback. */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + size--; + snd_nm256_writel(chip, port + 4, coeff_buf + size); +} + +static void +snd_nm256_load_coefficient(nm256_t *chip, int stream, int number) +{ + /* The enable register for the specified engine. */ + u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG); + u32 addr = NM_COEFF_START_OFFSET; + + addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET); + + if (snd_nm256_readb(chip, poffset) & 1) { + snd_printd("NM256: Engine was enabled while loading coefficients!\n"); + return; + } + + /* The recording engine uses coefficient values 8-15. */ + number &= 7; + if (stream == SNDRV_PCM_STREAM_CAPTURE) + number += 8; + + if (! chip->use_cache) { + snd_nm256_load_one_coefficient(chip, stream, addr, number); + return; + } + if (! chip->coeffs_current) { + snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf, + NM_TOTAL_COEFF_COUNT * 4); + chip->coeffs_current = 1; + } else { + u32 base = chip->all_coeff_buf; + u32 offset = snd_nm256_get_start_offset(number); + u32 end_offset = offset + coefficient_sizes[number]; + snd_nm256_writel(chip, addr, base + offset); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + end_offset--; + snd_nm256_writel(chip, addr + 4, base + end_offset); + } +} + + +/* The actual rates supported by the card. */ +static unsigned int samplerates[8] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, +}; +#define NUM_SAMPLERATES (sizeof(samplerates) / sizeof(samplerates[0])) +static snd_pcm_hw_constraint_list_t constraints_rates = { + .count = NUM_SAMPLERATES, + .list = samplerates, + .mask = 0, +}; + +/* + * return the index of the target rate + */ +static int +snd_nm256_fixed_rate(unsigned int rate) +{ + unsigned int i; + for (i = 0; i < NUM_SAMPLERATES; i++) { + if (rate == samplerates[i]) + return i; + } + snd_BUG(); + return 0; +} + +/* + * set sample rate and format + */ +static void +snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int rate_index = snd_nm256_fixed_rate(runtime->rate); + unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK; + + s->shift = 0; + if (snd_pcm_format_width(runtime->format) == 16) { + ratebits |= NM_RATE_BITS_16; + s->shift++; + } + if (runtime->channels > 1) { + ratebits |= NM_RATE_STEREO; + s->shift++; + } + + runtime->rate = samplerates[rate_index]; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */ + snd_nm256_writeb(chip, + NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */ + snd_nm256_writeb(chip, + NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + break; + } +} + +/* + * start / stop + */ + +/* update the watermark (current period) */ +static void snd_nm256_pcm_mark(nm256_t *chip, nm256_stream_t *s, int reg) +{ + s->cur_period++; + s->cur_period %= s->periods; + snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size); +} + +#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK) +#define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK) + +static void +snd_nm256_playback_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + /* program buffer pointers */ + snd_nm256_writel(chip, NM_PBUFFER_START, s->buf); + snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift)); + snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf); + snd_nm256_playback_mark(chip, s); + + /* Enable playback engine and interrupts. */ + snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, + NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); + /* Enable both channels. */ + snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0); +} + +static void +snd_nm256_capture_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + /* program buffer pointers */ + snd_nm256_writel(chip, NM_RBUFFER_START, s->buf); + snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size); + snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf); + snd_nm256_capture_mark(chip, s); + + /* Enable playback engine and interrupts. */ + snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, + NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); +} + +/* Stop the play engine. */ +static void +snd_nm256_playback_stop(nm256_t *chip) +{ + /* Shut off sound from both channels. */ + snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, + NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); + /* Disable play engine. */ + snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0); +} + +static void +snd_nm256_capture_stop(nm256_t *chip) +{ + /* Disable recording engine. */ + snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0); +} + +static int +snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long flags; + int err = 0; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (! s->running) { + snd_nm256_playback_start(chip, s, substream); + s->running = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->running) { + snd_nm256_playback_stop(chip); + s->running = 0; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + +static int +snd_nm256_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long flags; + int err = 0; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (! s->running) { + snd_nm256_capture_start(chip, s, substream); + s->running = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->running) { + snd_nm256_capture_stop(chip); + s->running = 0; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + + +/* + * prepare playback/capture channel + */ +static int snd_nm256_pcm_prepare(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + unsigned long flags; + + snd_assert(s, return -ENXIO); + s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); + s->period_size = frames_to_bytes(runtime, substream->runtime->period_size); + s->periods = substream->runtime->periods; + s->cur_period = 0; + + spin_lock_irqsave(&chip->reg_lock, flags); + s->running = 0; + snd_nm256_set_format(chip, s, substream); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + + +/* + * get the current pointer + */ +static snd_pcm_uframes_t +snd_nm256_playback_pointer(snd_pcm_substream_t * substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long curp; + + snd_assert(s, return 0); + curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf; + curp %= s->dma_size; + return bytes_to_frames(substream->runtime, curp); +} + +static snd_pcm_uframes_t +snd_nm256_capture_pointer(snd_pcm_substream_t * substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long curp; + + snd_assert(s != NULL, return 0); + curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf; + curp %= s->dma_size; + return bytes_to_frames(substream->runtime, curp); +} + +#ifndef __i386__ +/* FIXME: I/O space is not accessible via pointers on all architectures */ + +/* + * silence / copy for playback + */ +static int +snd_nm256_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + memset_io(s->bufptr + pos, 0, count); + return 0; +} + +static int +snd_nm256_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + if (copy_from_user_toio(s->bufptr + pos, src, count)) + return -EFAULT; + return 0; +} + +/* + * copy to user + */ +static int +snd_nm256_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + if (copy_to_user_fromio(dst, s->bufptr + pos, count)) + return -EFAULT; + return 0; +} + +#endif /* !__i386__ */ + + +/* + * update playback/capture watermarks + */ + +/* spinlock held! */ +static void +snd_nm256_playback_update(nm256_t *chip) +{ + nm256_stream_t *s; + + s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (s->running && s->substream) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&chip->reg_lock); + snd_nm256_playback_mark(chip, s); + } +} + +/* spinlock held! */ +static void +snd_nm256_capture_update(nm256_t *chip) +{ + nm256_stream_t *s; + + s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (s->running && s->substream) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&chip->reg_lock); + snd_nm256_capture_mark(chip, s); + } +} + +/* + * hardware info + */ +static snd_pcm_hardware_t snd_nm256_playback = +{ + .info = +#ifdef __i386__ + SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| +#endif + SNDRV_PCM_INFO_INTERLEAVED | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 128 * 1024, +}; + +static snd_pcm_hardware_t snd_nm256_capture = +{ + .info = +#ifdef __i386__ + SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| +#endif + SNDRV_PCM_INFO_INTERLEAVED | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 128 * 1024, +}; + + +/* set dma transfer size */ +static int snd_nm256_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) +{ + /* area and addr are already set and unchanged */ + substream->runtime->dma_bytes = params_buffer_bytes(hw_params); + return 0; +} + +/* + * open + */ +static void snd_nm256_setup_stream(nm256_t *chip, nm256_stream_t *s, + snd_pcm_substream_t *substream, + snd_pcm_hardware_t *hw_ptr) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + s->running = 0; + runtime->hw = *hw_ptr; + runtime->hw.buffer_bytes_max = s->bufsize; + runtime->hw.period_bytes_max = s->bufsize / 2; + runtime->dma_area = (void*) s->bufptr; + runtime->dma_addr = s->bufptr_addr; + runtime->dma_bytes = s->bufsize; + runtime->private_data = s; + s->substream = substream; + + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); +} + +static int +snd_nm256_playback_open(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], + substream, &snd_nm256_playback); + return 0; +} + +static int +snd_nm256_capture_open(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], + substream, &snd_nm256_capture); + return 0; +} + +/* + * close - we don't have to do special.. + */ +static int +snd_nm256_playback_close(snd_pcm_substream_t *substream) +{ + return 0; +} + + +static int +snd_nm256_capture_close(snd_pcm_substream_t *substream) +{ + return 0; +} + +/* + * create a pcm instance + */ +static snd_pcm_ops_t snd_nm256_playback_ops = { + .open = snd_nm256_playback_open, + .close = snd_nm256_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_nm256_pcm_hw_params, + .prepare = snd_nm256_pcm_prepare, + .trigger = snd_nm256_playback_trigger, + .pointer = snd_nm256_playback_pointer, +#ifndef __i386__ + .copy = snd_nm256_playback_copy, + .silence = snd_nm256_playback_silence, +#endif +}; + +static snd_pcm_ops_t snd_nm256_capture_ops = { + .open = snd_nm256_capture_open, + .close = snd_nm256_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_nm256_pcm_hw_params, + .prepare = snd_nm256_pcm_prepare, + .trigger = snd_nm256_capture_trigger, + .pointer = snd_nm256_capture_pointer, +#ifndef __i386__ + .copy = snd_nm256_capture_copy, +#endif +}; + +static int __devinit +snd_nm256_pcm(nm256_t *chip, int device) +{ + snd_pcm_t *pcm; + int i, err; + + for (i = 0; i < 2; i++) { + nm256_stream_t *s = &chip->streams[i]; + s->bufptr = chip->buffer + s->buf - chip->buffer_start; + s->bufptr_addr = chip->buffer_addr + s->buf - chip->buffer_start; + } + + err = snd_pcm_new(chip->card, chip->card->driver, device, + 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + chip->pcm = pcm; + + return 0; +} + + +/* + * Initialize the hardware. + */ +static void +snd_nm256_init_chip(nm256_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + /* Reset everything. */ + snd_nm256_writeb(chip, 0x0, 0x11); + snd_nm256_writew(chip, 0x214, 0); + /* stop sounds.. */ + //snd_nm256_playback_stop(chip); + //snd_nm256_capture_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + + +inline static void +snd_nm256_intr_check(nm256_t *chip) +{ + if (chip->badintrcount++ > 1000) { + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with IRQ 9s. + */ + if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) + snd_nm256_playback_stop(chip); + if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) + snd_nm256_capture_stop(chip); + chip->badintrcount = 0; + } +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * + * I don't like the cut-n-paste job here either between the two routines, + * but there are sufficient differences between the two interrupt handlers + * that parameterizing it isn't all that great either. (Could use a macro, + * I suppose...yucky bleah.) + */ + +static void +snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) +{ + nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return); + u16 status; + u8 cbyte; + + status = snd_nm256_readw(chip, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + snd_nm256_intr_check(chip); + return; + } + + chip->badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + spin_lock(&chip->reg_lock); + if (status & NM_PLAYBACK_INT) { + status &= ~NM_PLAYBACK_INT; + NM_ACK_INT(chip, NM_PLAYBACK_INT); + snd_nm256_playback_update(chip); + } + + if (status & NM_RECORD_INT) { + status &= ~NM_RECORD_INT; + NM_ACK_INT(chip, NM_RECORD_INT); + snd_nm256_capture_update(chip); + } + + if (status & NM_MISC_INT_1) { + status &= ~NM_MISC_INT_1; + NM_ACK_INT(chip, NM_MISC_INT_1); + snd_printd("NM256: Got misc interrupt #1\n"); + snd_nm256_writew(chip, NM_INT_REG, 0x8000); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte | 2); + } + + if (status & NM_MISC_INT_2) { + status &= ~NM_MISC_INT_2; + NM_ACK_INT(chip, NM_MISC_INT_2); + snd_printd("NM256: Got misc interrupt #2\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM_ACK_INT(chip, status); + } + + spin_unlock(&chip->reg_lock); +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * This handler is for the 256ZX, and is very similar to the non-ZX + * routine. + */ + +static void +snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) +{ + nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return); + u32 status; + u8 cbyte; + + status = snd_nm256_readl(chip, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + snd_nm256_intr_check(chip); + return; + } + + chip->badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + spin_lock(&chip->reg_lock); + if (status & NM2_PLAYBACK_INT) { + status &= ~NM2_PLAYBACK_INT; + NM2_ACK_INT(chip, NM2_PLAYBACK_INT); + snd_nm256_playback_update(chip); + } + + if (status & NM2_RECORD_INT) { + status &= ~NM2_RECORD_INT; + NM2_ACK_INT(chip, NM2_RECORD_INT); + snd_nm256_capture_update(chip); + } + + if (status & NM2_MISC_INT_1) { + status &= ~NM2_MISC_INT_1; + NM2_ACK_INT(chip, NM2_MISC_INT_1); + snd_printd("NM256: Got misc interrupt #1\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte | 2); + } + + if (status & NM2_MISC_INT_2) { + status &= ~NM2_MISC_INT_2; + NM2_ACK_INT(chip, NM2_MISC_INT_2); + snd_printd("NM256: Got misc interrupt #2\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM2_ACK_INT(chip, status); + } + + spin_unlock(&chip->reg_lock); +} + +/* + * AC97 interface + */ + +/* + * Waits for the mixer to become ready to be written; returns a zero value + * if it timed out. + */ +static int +snd_nm256_ac97_ready(nm256_t *chip) +{ + int timeout = 10; + u32 testaddr; + u16 testb; + + testaddr = chip->mixer_status_offset; + testb = chip->mixer_status_mask; + + /* + * Loop around waiting for the mixer to become ready. + */ + while (timeout-- > 0) { + if ((snd_nm256_readw(chip, testaddr) & testb) == 0) + return 1; + udelay(100); + } + return 0; +} + +/* + */ +static unsigned short +snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return -ENXIO); + int res; + + if (reg >= 128) + return 0; + + if (! snd_nm256_ac97_ready(chip)) + return 0; + res = snd_nm256_readw(chip, chip->mixer_base + reg); + /* Magic delay. Bleah yucky. */ + udelay(1000); + return res; +} + +/* + */ +static void +snd_nm256_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + int tries = 2; + u32 base; + + base = chip->mixer_base; + + snd_nm256_ac97_ready(chip); + + /* Wait for the write to take, too. */ + while (tries-- > 0) { + snd_nm256_writew(chip, base + reg, val); + udelay(1000); /* a little delay here seems better.. */ + if (snd_nm256_ac97_ready(chip)) + return; + } + snd_printd("nm256: ac97 codec not ready..\n"); +} + +/* initialize the ac97 into a known state */ +static void +snd_nm256_ac97_reset(ac97_t *ac97) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + /* Reset the mixer. 'Tis magic! */ + snd_nm256_writeb(chip, 0x6c0, 1); +#if 0 /* Dell latitude LS will lock up by this */ + snd_nm256_writeb(chip, 0x6cc, 0x87); +#endif + snd_nm256_writeb(chip, 0x6cc, 0x80); + snd_nm256_writeb(chip, 0x6cc, 0x0); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* create an ac97 mixer interface */ +static int __devinit +snd_nm256_mixer(nm256_t *chip) +{ + ac97_t ac97; + int i; + /* looks like nm256 hangs up when unexpected registers are touched... */ + static int mixer_regs[] = { + AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO, + AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, + AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, + AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, + AC97_EXTENDED_ID, + AC97_VENDOR_ID1, AC97_VENDOR_ID2, + -1 + }; + + memset(&ac97, 0, sizeof(ac97)); + ac97.reset = snd_nm256_ac97_reset; + ac97.write = snd_nm256_ac97_write; + ac97.read = snd_nm256_ac97_read; + ac97.limited_regs = 1; + for (i = 0; mixer_regs[i] >= 0; i++) + set_bit(mixer_regs[i], ac97.reg_accessed); + ac97.private_data = chip; + return snd_ac97_mixer(chip->card, &ac97, &chip->ac97); +} + +/* + * See if the signature left by the NM256 BIOS is intact; if so, we use + * the associated address as the end of our audio buffer in the video + * RAM. + */ + +static int __devinit +snd_nm256_peek_for_sig(nm256_t *chip) +{ + /* The signature is located 1K below the end of video RAM. */ + unsigned long temp; + /* Default buffer end is 5120 bytes below the top of RAM. */ + unsigned long pointer_found = chip->buffer_end - 0x1400; + u32 sig; + + temp = (unsigned long) ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16); + if (temp == 0) { + snd_printk("Unable to scan for card signature in video RAM\n"); + return -EBUSY; + } + + sig = readl(temp); + if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { + u32 pointer = readl(temp + 4); + + /* + * If it's obviously invalid, don't use it + */ + if (pointer == 0xffffffff || + pointer < chip->buffer_size || + pointer > chip->buffer_end) { + snd_printk("invalid signature found: 0x%x\n", pointer); + iounmap((void *)temp); + return -ENODEV; + } else { + pointer_found = pointer; + printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer); + } + } + + iounmap((void *)temp); + chip->buffer_end = pointer_found; + + return 0; +} + +#ifdef CONFIG_PM +/* + * APM event handler, so the card is properly reinitialized after a power + * event. + */ +static void nm256_suspend(nm256_t *chip) +{ + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + snd_pcm_suspend_all(chip->pcm); + chip->coeffs_current = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void nm256_resume(nm256_t *chip) +{ + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + /* Perform a full reset on the hardware */ + pci_enable_device(chip->pci); + snd_nm256_init_chip(chip); + + /* restore ac97 */ + snd_ac97_resume(chip->ac97); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_nm256_suspend(struct pci_dev *dev, u32 state) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO); + nm256_suspend(chip); + return 0; +} +static int snd_nm256_resume(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO); + nm256_resume(chip); + return 0; +} +#else +static void snd_nm256_suspend(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return); + nm256_suspend(chip); +} +static void snd_nm256_resume(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return); + nm256_resume(chip); +} +#endif + +/* callback */ +static int snd_nm256_set_power_state(snd_card_t *card, unsigned int power_state) +{ + nm256_t *chip = snd_magic_cast(nm256_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + nm256_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + nm256_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_nm256_free(nm256_t *chip) +{ + if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) + snd_nm256_playback_stop(chip); + if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) + snd_nm256_capture_stop(chip); + + if (chip->irq >= 0) + synchronize_irq(chip->irq); + + if (chip->cport) + iounmap((void *) chip->cport); + if (chip->buffer) + iounmap((void *) chip->buffer); + if (chip->res_cport) { + release_resource(chip->res_cport); + kfree_nocheck(chip->res_cport); + } + if (chip->res_buffer) { + release_resource(chip->res_buffer); + kfree_nocheck(chip->res_buffer); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_nm256_dev_free(snd_device_t *device) +{ + nm256_t *chip = snd_magic_cast(nm256_t, device->device_data, return -ENXIO); + return snd_nm256_free(chip); +} + +static int __devinit +snd_nm256_create(snd_card_t *card, struct pci_dev *pci, + int play_bufsize, int capt_bufsize, + int force_load, + u32 buffertop, + int usecache, + nm256_t **chip_ret) +{ + nm256_t *chip; + int err, pval; + static snd_device_ops_t ops = { + .dev_free = snd_nm256_dev_free, + }; + u32 addr; + + *chip_ret = NULL; + + chip = snd_magic_kcalloc(nm256_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + chip->pci = pci; + chip->use_cache = usecache; + spin_lock_init(&chip->reg_lock); + chip->irq = -1; + + chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize; + + /* + * The NM256 has two memory ports. The first port is nothing + * more than a chunk of video RAM, which is used as the I/O ring + * buffer. The second port has the actual juicy stuff (like the + * mixer and the playback engine control registers). + */ + + chip->buffer_addr = pci_resource_start(pci, 0); + chip->cport_addr = pci_resource_start(pci, 1); + + /* Init the memory port info. */ + /* remap control port (#2) */ + chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE, + card->driver); + if (chip->res_cport == NULL) { + snd_printk("memory region 0x%lx (size 0x%x) busy\n", + chip->cport_addr, NM_PORT2_SIZE); + err = -EBUSY; + goto __error; + } + chip->cport = (unsigned long) ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE); + if (chip->cport == 0) { + snd_printk("unable to map control port %lx\n", chip->cport_addr); + err = -ENOMEM; + goto __error; + } + + if (!strcmp(card->driver, "NM256AV")) { + /* Ok, try to see if this is a non-AC97 version of the hardware. */ + pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); + if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { + if (! force_load) { + printk(KERN_ERR "nm256: no ac97 is found!\n"); + printk(KERN_ERR " force the driver to load by passing in the module parameter\n"); + printk(KERN_ERR " force_ac97=1\n"); + printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); + err = -ENXIO; + goto __error; + } + } + chip->buffer_end = 2560 * 1024; + chip->interrupt = snd_nm256_interrupt; + chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; + chip->mixer_status_mask = NM_MIXER_READY_MASK; + } else { + /* Not sure if there is any relevant detect for the ZX or not. */ + if (snd_nm256_readb(chip, 0xa0b) != 0) + chip->buffer_end = 6144 * 1024; + else + chip->buffer_end = 4096 * 1024; + + chip->interrupt = snd_nm256_interrupt_zx; + chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; + chip->mixer_status_mask = NM2_MIXER_READY_MASK; + } + + chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; + if (chip->use_cache) + chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; + else + chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; + + if (buffertop >= chip->buffer_size && buffertop < chip->buffer_end) + chip->buffer_end = buffertop; + else { + /* get buffer end pointer from signature */ + if ((err = snd_nm256_peek_for_sig(chip)) < 0) + goto __error; + } + + chip->buffer_start = chip->buffer_end - chip->buffer_size; + chip->buffer_addr += chip->buffer_start; + + printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n", + chip->buffer_start, chip->buffer_end); + + chip->res_buffer = request_mem_region(chip->buffer_addr, + chip->buffer_size, + card->driver); + if (chip->res_buffer == NULL) { + snd_printk("nm256: buffer 0x%lx (size 0x%x) busy\n", + chip->buffer_addr, chip->buffer_size); + err = -EBUSY; + goto __error; + } + chip->buffer = (unsigned long) ioremap_nocache(chip->buffer_addr, chip->buffer_size); + if (chip->buffer == 0) { + err = -ENOMEM; + snd_printk("unable to map ring buffer at %lx\n", chip->buffer_addr); + goto __error; + } + + /* set offsets */ + addr = chip->buffer_start; + chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; + addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; + chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; + addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; + if (chip->use_cache) { + chip->all_coeff_buf = addr; + } else { + chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; + addr += NM_MAX_PLAYBACK_COEF_SIZE; + chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; + } + + /* acquire interrupt */ + if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void*)chip)) { + err = -EBUSY; + snd_printk("unable to grab IRQ %d\n", pci->irq); + goto __error; + } + chip->irq = pci->irq; + + /* Fixed setting. */ + chip->mixer_base = NM_MIXER_OFFSET; + + chip->coeffs_current = 0; + + snd_nm256_init_chip(chip); + + if ((err = snd_nm256_pcm(chip, 0)) < 0) + goto __error; + + if ((err = snd_nm256_mixer(chip)) < 0) + goto __error; + + // pci_set_master(pci); /* needed? */ + +#ifdef CONFIG_PM + card->set_power_state = snd_nm256_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; + + *chip_ret = chip; + return 0; + +__error: + snd_nm256_free(chip); + return err; +} + + +static int __devinit snd_nm256_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + nm256_t *chip; + int err; + unsigned int xbuffer_top; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: + strcpy(card->driver, "NM256AV"); + break; + case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: + strcpy(card->driver, "NM256ZX"); + break; + default: + snd_printk("invalid device id 0x%x\n", pci->device); + snd_card_free(card); + return -EINVAL; + } + + if (vaio_hack[dev]) + xbuffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ + else + xbuffer_top = buffer_top[dev]; + + if (playback_bufsize[dev] < 4) + playback_bufsize[dev] = 4; + if (playback_bufsize[dev] > 128) + playback_bufsize[dev] = 128; + if (capture_bufsize[dev] < 4) + capture_bufsize[dev] = 4; + if (capture_bufsize[dev] > 128) + capture_bufsize[dev] = 128; + if ((err = snd_nm256_create(card, pci, + playback_bufsize[dev] * 1024, /* in bytes */ + capture_bufsize[dev] * 1024, /* in bytes */ + force_ac97[dev], + xbuffer_top, + use_cache[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "NeoMagic %s", card->driver); + sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", + card->shortname, + chip->buffer_addr, chip->cport_addr, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_nm256_remove(struct pci_dev *pci) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + .name = "NeoMagic 256", + .id_table = snd_nm256_ids, + .probe = snd_nm256_probe, + .remove = __devexit_p(snd_nm256_remove), +#ifdef CONFIG_PM + .suspend = snd_nm256_suspend, + .resume = snd_nm256_resume, +#endif +}; + + +static int __init alsa_card_nm256_init(void) +{ + int err; + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "NeoMagic 256 audio soundchip not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_nm256_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_nm256_init) +module_exit(alsa_card_nm256_exit) + +#ifndef MODULE + +/* format is: snd-nm256=enable,index,id, + playback_bufsize,capture_bufsize, + force_ac97,buffer_top,use_cache */ + +static int __init alsa_card_nm256_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&playback_bufsize[nr_dev]) == 2 && + get_option(&str,&capture_bufsize[nr_dev]) == 2 && + get_option(&str,&force_ac97[nr_dev]) == 2 && + get_option(&str,&buffer_top[nr_dev]) == 2 && + get_option(&str,&use_cache[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-nm256=", alsa_card_nm256_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/nm256/nm256_coef.c linux/sound/pci/nm256/nm256_coef.c --- linux-2.4.21-rc1.orig/sound/pci/nm256/nm256_coef.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/nm256/nm256_coef.c 2001-12-18 09:07:11.000000000 -0700 @@ -0,0 +1,4607 @@ +#define NM_TOTAL_COEFF_COUNT 0x3158 + +static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { + 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, + 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, + 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, + 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, + 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF, + 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, + 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, + 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9, + 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C, + 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00, + 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, + 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, + 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00, + 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1, + 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, + 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, + 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD, + 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38, + 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, + 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, + 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06, + 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, + 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, + 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA, + 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9, + 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68, + 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32, + 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, + 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, + 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53, + 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04, + 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01, + 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20, + 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43, + 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1, + 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD, + 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8, + 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5, + 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04, + 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04, + 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D, + 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE, + 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD, + 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, + 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2, + 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD, + 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, + 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D, + 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01, + 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, + 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, + 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, + 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66, + 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF, + 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, + 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, + 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00, + 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF, + 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, + 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, + 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC, + 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98, + 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, + 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, + 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05, + 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A, + 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, + 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, + 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8, + 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40, + 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85, + 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36, + 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D, + 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39, + 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48, + 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01, + 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F, + 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F, + 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1, + 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE, + 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A, + 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A, + 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03, + 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, + 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, + 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01, + 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC, + 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, + 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2, + 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF, + 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48, + 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01, + 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11, + 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF, + 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, + 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, + 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF, + 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C, + 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, + 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, + 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC, + 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1, + 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, + 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, + 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04, + 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, + 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, + 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, + 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7, + 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3, + 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9, + 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A, + 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, + 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, + 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, + 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A, + 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48, + 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, + 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91, + 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8, + 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1, + 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF, + 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA, + 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD, + 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD, + 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06, + 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF, + 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94, + 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC, + 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, + 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4, + 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF, + 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A, + 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01, + 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, + 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, + 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, + 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B, + 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF, + 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, + 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, + 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF, + 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC, + 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, + 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, + 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC, + 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE, + 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, + 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, + 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03, + 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91, + 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, + 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, + 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5, + 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20, + 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3, + 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E, + 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00, + 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47, + 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96, + 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46, + 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01, + 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74, + 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B, + 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2, + 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF, + 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D, + 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A, + 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE, + 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, + 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, + 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF, + 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81, + 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC, + 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, + 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5, + 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2, + 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF, + 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED, + 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01, + 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, + 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25, + 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, + 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, + 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2, + 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, + 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, + 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, + 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA, + 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, + 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, + 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02, + 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB, + 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, + 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, + 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4, + 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85, + 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02, + 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41, + 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, + 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, + 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, + 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA, + 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44, + 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01, + 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5, + 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A, + 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3, + 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00, + 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A, + 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C, + 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00, + 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06, + 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D, + 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0, + 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC, + 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, + 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8, + 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7, + 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64, + 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE, + 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, + 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, + 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, + 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, + 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD, + 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, + 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, + 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE, + 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54, + 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, + 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, + 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, + 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC, + 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63, + 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, + 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, + 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00, + 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34, + 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, + 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, + 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3, + 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0, + 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36, + 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44, + 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, + 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA, + 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00, + 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4, + 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42, + 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01, + 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E, + 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31, + 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4, + 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01, + 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86, + 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D, + 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01, + 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, + 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, + 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88, + 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF, + 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA, + 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92, + 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA, + 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE, + 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF, + 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5, + 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, + 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, + 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01, + 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C, + 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, + 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, + 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC, + 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8, + 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, + 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, + 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, + 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF, + 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99, + 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, + 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, + 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2, + 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F, + 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D, + 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46, + 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, + 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, + 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, + 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, + 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F, + 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01, + 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A, + 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF, + 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5, + 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02, + 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9, + 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8, + 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03, + 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07, + 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0, + 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0, + 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC, + 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9, + 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD, + 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66, + 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36, + 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF, + 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, + 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, + 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF, + 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11, + 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, + 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, + 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01, + 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74, + 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF, + 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, + 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, + 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC, + 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A, + 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, + 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, + 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, + 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD, + 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08, + 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, + 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, + 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32, + 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1, + 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70, + 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5, + 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47, + 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF, + 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26, + 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00, + 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, + 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B, + 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01, + 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E, + 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0, + 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7, + 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD, + 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F, + 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66, + 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04, + 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, + 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, + 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, + 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48, + 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC, + 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93, + 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01, + 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34, + 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA, + 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF, + 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF, + 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10, + 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, + 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, + 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01, + 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30, + 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, + 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, + 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC, + 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A, + 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, + 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, + 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03, + 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04, + 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, + 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, + 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1, + 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE, + 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48, + 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71, + 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, + 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, + 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, + 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB, + 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37, + 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01, + 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2, + 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70, + 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8, + 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD, + 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9, + 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0, + 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05, + 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, + 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06, + 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40, + 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, + 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53, + 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC, + 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, + 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC, + 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D, + 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6, + 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00, + 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, + 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, + 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF, + 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06, + 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, + 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, + 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01, + 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57, + 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, + 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, + 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD, + 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B, + 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, + 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, + 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03, + 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24, + 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, + 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, + 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1, + 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8, + 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, + 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04, + 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C, + 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02, + 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00, + 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, + 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33, + 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00, + 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, + 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B, + 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA, + 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD, + 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58, + 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14, + 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06, + 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, + 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, + 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0, + 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC, + 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA, + 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4, + 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA, + 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00, + 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00, + 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06, + 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, + 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, + 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, + 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE, + 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, + 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, + 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD, + 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0, + 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, + 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, + 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04, + 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B, + 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, + 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, + 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, + 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2, + 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE, + 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E, + 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08, + 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4, + 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, + 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, + 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B, + 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E, + 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00, + 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E, + 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C, + 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC, + 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC, + 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6, + 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B, + 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06, + 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05, + 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC, + 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD, + 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8, + 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A, + 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2, + 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01, + 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, + 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, + 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00, + 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20, + 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, + 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, + 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, + 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7, + 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, + 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, + 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE, + 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F, + 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, + 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, + 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05, + 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D, + 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, + 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, + 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2, + 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, + 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C, + 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B, + 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0, + 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF, + 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66, + 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29, + 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF, + 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E, + 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4, + 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01, + 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC, + 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB, + 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12, + 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06, + 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, + 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, + 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12, + 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD, + 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7, + 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD, + 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0, + 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01, + 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00, + 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64, + 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, + 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, + 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73, + 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, + 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, + 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF, + 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC, + 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, + 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, + 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06, + 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF, + 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, + 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, + 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9, + 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4, + 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, + 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11, + 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63, + 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, + 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, + 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22, + 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25, + 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF, + 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20, + 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87, + 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE, + 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC, + 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3, + 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7, + 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07, + 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03, + 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97, + 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA, + 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02, + 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5, + 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A, + 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE, + 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01, + 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7, + 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, + 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00, + 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4, + 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, + 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, + 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, + 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60, + 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, + 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, + 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF, + 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D, + 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, + 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, + 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06, + 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15, + 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, + 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, + 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7, + 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6, + 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0, + 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15, + 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C, + 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF, + 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3, + 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF, + 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1, + 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F, + 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF, + 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8, + 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23, + 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA, + 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC, + 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB, + 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49, + 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07, + 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, + 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, + 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07, + 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01, + 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4, + 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E, + 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B, + 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01, + 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00, + 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB, + 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, + 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, + 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01, + 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB, + 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, + 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, + 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00, + 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48, + 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, + 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, + 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06, + 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81, + 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, + 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, + 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, + 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8, + 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4, + 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A, + 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9, + 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, + 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, + 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74, + 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A, + 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE, + 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B, + 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC, + 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8, + 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC, + 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F, + 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96, + 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06, + 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00, + 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C, + 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56, + 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00, + 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3, + 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8, + 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, + 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6, + 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, + 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, + 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1, + 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00, + 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, + 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, + 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01, + 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D, + 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, + 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, + 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01, + 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73, + 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, + 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, + 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07, + 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08, + 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, + 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD, + 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA, + 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F, + 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F, + 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB, + 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF, + 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89, + 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D, + 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15, + 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01, + 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6, + 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86, + 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6, + 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC, + 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0, + 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2, + 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06, + 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, + 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, + 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3, + 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF, + 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2, + 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35, + 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00, + 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD, + 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01, + 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, + 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41, + 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00, + 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, + 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01, + 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1, + 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, + 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, + 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02, + 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95, + 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, + 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, + 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07, + 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9, + 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00, + 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, + 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, + 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7, + 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE, + 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62, + 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25, + 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2, + 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, + 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, + 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F, + 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11, + 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01, + 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26, + 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53, + 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4, + 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC, + 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D, + 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D, + 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06, + 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD, + 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12, + 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29, + 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF, + 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1, + 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75, + 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, + 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0, + 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, + 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, + 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, + 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02, + 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00, + 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, + 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01, + 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C, + 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, + 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, + 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD, + 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64, + 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, + 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, + 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06, + 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65, + 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, + 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, + 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, + 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01, + 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F, + 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, + 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29, + 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80, + 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF, + 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E, + 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B, + 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C, + 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01, + 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB, + 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, + 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2, + 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC, + 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29, + 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D, + 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05, + 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, + 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, + 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3, + 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE, + 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1, + 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7, + 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF, + 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF, + 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, + 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E, + 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00, + 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, + 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, + 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01, + 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7, + 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, + 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, + 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD, + 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC, + 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, + 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, + 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06, + 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E, + 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, + 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, + 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC, + 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68, + 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55, + 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E, + 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63, + 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, + 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, + 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4, + 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08, + 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01, + 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C, + 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33, + 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2, + 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD, + 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06, + 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6, + 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04, + 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04, + 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10, + 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F, + 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD, + 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, + 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1, + 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA, + 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, + 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0, + 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01, + 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, + 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, + 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, + 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7, + 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00, + 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, + 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, + 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00, + 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5, + 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, + 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, + 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC, + 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78, + 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, + 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, + 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06, + 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, + 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, + 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA, + 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE, + 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D, + 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33, + 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E, + 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00, + 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96, + 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E, + 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04, + 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01, + 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD, + 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF, + 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49, + 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1, + 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD, + 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8, + 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E, + 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03, + 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, + 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, + 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97, + 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD, + 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, + 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2, + 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF, + 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5, + 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7, + 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF, + 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, + 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, + 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00, + 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C, + 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, + 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, + 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC, + 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3, + 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00, + 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, + 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, + 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, + 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05, + 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, + 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, + 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, + 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, + 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8, + 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54, + 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B, + 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37, + 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, + 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, + 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, + 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, + 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42, + 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48, + 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01, + 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0, + 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A, + 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1, + 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE, + 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43, + 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF, + 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03, + 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05, + 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B, + 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7, + 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC, + 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08, + 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3, + 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF, + 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86, + 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01, + 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A, + 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, + 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, + 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E, + 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF, + 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, + 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, + 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF, + 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0, + 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, + 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, + 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC, + 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04, + 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, + 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, + 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, + 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04, + 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63, + 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, + 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, + 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7, + 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7, + 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0, + 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B, + 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00, + 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC, + 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72, + 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47, + 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF, + 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56, + 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7, + 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1, + 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF, + 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD, + 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4, + 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD, + 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, + 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, + 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A, + 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC, + 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, + 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4, + 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF, + 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D, + 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01, + 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97, + 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF, + 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, + 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, + 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF, + 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45, + 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, + 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, + 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC, + 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08, + 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, + 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, + 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03, + 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D, + 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, + 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, + 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5, + 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33, + 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB, + 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F, + 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, + 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, + 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, + 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D, + 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46, + 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5, + 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, + 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D, + 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30, + 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2, + 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00, + 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC, + 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0, + 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF, + 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06, + 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07, + 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87, + 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC, + 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5, + 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6, + 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE, + 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, + 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4, + 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01, + 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, + 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, + 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, + 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60, + 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, + 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, + 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE, + 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE, + 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, + 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, + 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, + 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB, + 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, + 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, + 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, + 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01, + 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9, + 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, + 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, + 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4, + 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96, + 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B, + 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42, + 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00, + 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50, + 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF, + 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44, + 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, + 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1, + 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3, + 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3, + 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00, + 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35, + 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F, + 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00, + 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, + 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, + 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9, + 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC, + 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1, + 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8, + 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1, + 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F, + 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE, + 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF, + 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC, + 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF, + 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, + 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, + 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE, + 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1, + 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, + 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, + 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC, + 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B, + 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, + 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, + 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, + 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00, + 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45, + 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, + 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, + 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3, + 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF, + 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F, + 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44, + 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, + 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, + 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, + 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41, + 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01, + 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C, + 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E, + 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4, + 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01, + 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60, + 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D, + 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02, + 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07, + 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE, + 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4, + 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB, + 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB, + 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B, + 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, + 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73, + 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, + 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, + 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF, + 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B, + 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, + 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, + 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01, + 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70, + 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF, + 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, + 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, + 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC, + 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7, + 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, + 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, + 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE, + 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC, + 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, + 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, + 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2, + 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B, + 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76, + 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46, + 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF, + 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C, + 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00, + 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E, + 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF, + 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01, + 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8, + 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00, + 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5, + 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE, + 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0, + 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3, + 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03, + 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, + 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, + 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF, + 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5, + 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE, + 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E, + 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A, + 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF, + 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF, + 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30, + 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, + 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, + 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01, + 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9, + 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, + 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, + 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC, + 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20, + 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, + 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, + 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD, + 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C, + 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, + 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, + 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1, + 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A, + 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF, + 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48, + 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62, + 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, + 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, + 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, + 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A, + 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01, + 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B, + 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4, + 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7, + 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD, + 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89, + 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B, + 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04, + 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06, + 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61, + 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B, + 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC, + 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F, + 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01, + 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B, + 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6, + 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF, + 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, + 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, + 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, + 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF, + 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C, + 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, + 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, + 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01, + 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7, + 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF, + 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, + 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, + 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC, + 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28, + 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, + 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, + 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03, + 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC, + 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, + 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, + 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, + 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1, + 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB, + 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8, + 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48, + 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, + 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD, + 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00, + 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36, + 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00, + 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC, + 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96, + 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8, + 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD, + 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F, + 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF, + 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05, + 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, + 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, + 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB, + 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC, + 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB, + 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72, + 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7, + 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00, + 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF, + 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23, + 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, + 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, + 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01, + 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2, + 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF, + 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, + 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, + 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD, + 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01, + 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, + 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, + 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04, + 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD, + 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, + 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, + 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, + 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1, + 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD, + 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, + 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04, + 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03, + 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, + 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, + 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, + 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD, + 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32, + 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00, + 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10, + 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73, + 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA, + 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC, + 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98, + 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B, + 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06, + 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05, + 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, + 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C, + 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD, + 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA, + 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9, + 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD, + 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00, + 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, + 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, + 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, + 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00, + 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25, + 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, + 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, + 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, + 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, + 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, + 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, + 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, + 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, + 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26, + 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06, + 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, + 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, + 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84, + 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD, + 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, + 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, + 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, + 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12, + 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, + 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, + 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, + 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, + 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, + 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, + 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, + 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, + 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, + 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, + 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, + 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD, + 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70, + 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, + 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD, + 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7, + 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC, + 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC, + 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2, + 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC, + 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79, + 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47, + 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, + 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, + 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, + 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, + 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, + 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, + 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, + 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, + 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, + 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, + 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, + 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD, + 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07, + 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00, + 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96, + 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07, + 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, + 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, + 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85, + 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC, + 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C, + 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, + 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, + 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, + 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, + 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, + 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, + 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, + 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, + 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, + 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, + 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, + 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, + 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, + 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC, + 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83, + 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03, + 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45, + 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF, + 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C, + 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC, + 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86, + 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03, + 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14, + 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, + 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, + 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, + 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, + 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, + 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, + 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, + 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, + 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, + 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, + 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, + 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, + 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, + 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC, + 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC, + 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38, + 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06, + 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, + 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, + 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7, + 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC, + 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, + 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, + 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, + 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, + 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, + 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, + 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, + 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, + 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, + 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, + 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, + 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, + 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, + 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, + 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, + 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, + 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC, + 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C, + 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05, + 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A, + 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC, + 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, + 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9, + 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19, + 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, + 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, + 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, + 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, + 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, + 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, + 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, + 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, + 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, + 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, + 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, + 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, + 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC, + 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F, + 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15, + 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05, + 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, + 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, + 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00, + 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10, + 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, + 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, + 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, + 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, + 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, + 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, + 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, + 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, + 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, + 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, + 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, + 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, + 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, + 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, + 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, + 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, + 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, + 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1, + 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06, + 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C, + 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00, + 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7, + 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC, + 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, + 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6, + 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56, + 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, + 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, + 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, + 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, + 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, + 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, + 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, + 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, + 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, + 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, + 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, + 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC, + 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F, + 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D, + 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04, + 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, + 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, + 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0, + 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC, + 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, + 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, + 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, + 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47, + 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, + 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, + 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, + 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, + 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, + 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, + 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, + 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, + 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, + 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, + 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, + 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, + 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC, + 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA, + 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07, + 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18, + 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, + 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B, + 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD, + 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3, + 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4, + 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, + 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, + 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, + 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, + 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, + 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, + 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, + 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, + 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, + 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, + 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, + 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, + 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC, + 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21, + 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13, + 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02, + 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, + 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, + 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00, + 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94, + 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD, + 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, + 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, + 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, + 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, + 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, + 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, + 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, + 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, + 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, + 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, + 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, + 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, + 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, + 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, + 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC, + 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27, + 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07, + 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E, + 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00, + 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05, + 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD, + 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, + 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1, + 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C, + 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, + 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, + 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, + 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, + 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, + 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, + 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, + 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, + 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, + 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, + 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, + 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD, + 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF, + 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D, + 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF, + 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE, + 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, + 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, + 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47, + 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02, + 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, + 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, + 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, + 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, + 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, + 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, + 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, + 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, + 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, + 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, + 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, + 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, + 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, + 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, + 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, + 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD, + 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, + 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06, + 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94, + 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01, + 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, + 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1, + 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE, + 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, + 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, + 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, + 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, + 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, + 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, + 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, + 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, + 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, + 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, + 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, + 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, + 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE, + 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33, + 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A, + 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02, + 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, + 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6, + 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00, + 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, + 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, + 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, + 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, + 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, + 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, + 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, + 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, + 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, + 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, + 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, + 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, + 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, + 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, + 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF, + 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A, + 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04, + 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04, + 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48, + 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF, + 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2, + 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5, + 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, + 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, + 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, + 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, + 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, + 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, + 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, + 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, + 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, + 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, + 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, + 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, + 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, + 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00, + 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6, + 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC, + 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04, + 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08, + 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, + 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, + 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF, + 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6, + 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE, + 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF, + 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, + 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, + 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, + 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, + 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, + 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, + 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, + 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, + 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, + 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, + 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, + 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, + 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, + 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, + 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00, + 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D, + 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02, + 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD, + 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C, + 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE, + 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED, + 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5, + 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7, + 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD, + 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, + 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, + 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, + 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, + 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, + 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, + 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, + 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, + 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, + 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, + 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, + 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01, + 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46, + 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27, + 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06, + 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, + 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, + 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47, + 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD, + 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9, + 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, + 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, + 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA, + 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, + 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, + 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, + 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, + 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, + 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, + 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, + 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, + 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, + 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, + 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE, + 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13, + 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF, + 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D, + 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61, + 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD, + 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4, + 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA, + 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C, + 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B, + 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, + 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, + 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, + 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, + 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, + 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, + 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, + 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, + 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, + 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, + 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, + 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD, + 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA, + 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00, + 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA, + 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07, + 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, + 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, + 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, + 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6, + 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC, + 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE, + 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, + 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, + 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, + 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, + 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, + 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, + 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, + 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, + 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, + 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, + 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, + 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, + 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, + 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD, + 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D, + 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03, + 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD, + 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, + 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80, + 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC, + 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98, + 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00, + 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40, + 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, + 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, + 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, + 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, + 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, + 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, + 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, + 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, + 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, + 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, + 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, + 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC, + 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3, + 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00, + 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1, + 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07, + 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, + 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, + 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF, + 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97, + 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC, + 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, + 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, + 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, + 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, + 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, + 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, + 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, + 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, + 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, + 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, + 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, + 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, + 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, + 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, + 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, + 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, + 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC, + 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15, + 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04, + 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44, + 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14, + 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC, + 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, + 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA, + 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8, + 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, + 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, + 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, + 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, + 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, + 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, + 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, + 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, + 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, + 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, + 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, + 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC, + 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E, + 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C, + 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06, + 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, + 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, + 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00, + 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD, + 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC, + 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, + 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, + 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, + 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, + 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, + 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, + 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, + 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, + 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, + 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, + 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, + 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, + 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, + 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, + 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, + 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC, + 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38, + 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06, + 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9, + 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00, + 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55, + 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC, + 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, + 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7, + 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2, + 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, + 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, + 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, + 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, + 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, + 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, + 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, + 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, + 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, + 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, + 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, + 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, + 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, + 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00, + 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83, + 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05, + 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, + 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, + 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00, + 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20, + 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC, + 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, + 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, + 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, + 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, + 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, + 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, + 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, + 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, + 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, + 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, + 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, + 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, + 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, + 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, + 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, + 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC, + 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96, + 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06, + 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E, + 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E, + 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC, + 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, + 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4, + 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77, + 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45, + 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, + 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, + 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, + 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, + 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, + 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, + 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, + 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, + 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, + 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, + 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, + 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, + 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC, + 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7, + 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, + 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70, + 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03, + 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, + 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, + 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00, + 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C, + 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD, + 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, + 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, + 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B, + 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, + 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, + 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, + 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, + 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, + 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, + 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, + 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, + 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, + 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, + 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC, + 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26, + 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, + 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, + 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, + 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, + 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, + 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, + 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, + 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, + 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B, + 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11, + 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00, + 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C, + 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, + 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, + 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, + 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, + 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, + 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, + 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, + 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, + 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, + 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, + 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, + 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, + 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, + 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, + 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, + 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1, + 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07, + 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF, + 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A, + 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, + 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, + 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, + 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, + 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, + 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, + 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, + 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, + 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, + 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, + 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, + 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, + 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, + 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, + 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, + 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43, + 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48, + 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99, + 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE, + 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7, + 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, + 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, + 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, + 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, + 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, + 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, + 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, + 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, + 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, + 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, + 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D, + 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, + 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, + 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, + 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, + 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, + 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, + 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E, + 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46, + 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01, + 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D, + 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF, + 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, + 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, + 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, + 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, + 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, + 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06, + 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, + 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, + 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, + 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, + 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A, + 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, + 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, + 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00, + 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, + 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, + 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, + 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, + 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41, + 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01, + 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9, + 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF, + 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, + 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, + 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, + 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, + 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F, + 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, + 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, + 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00, + 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, + 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, + 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD, + 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, + 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00, + 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, + 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, + 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A, + 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, + 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85, + 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF, + 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, + 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, + 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, + 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, + 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, + 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A, + 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, + 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, + 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00, + 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, + 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, + 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7, + 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, + 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, + 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF, + 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, + 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, + 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, + 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, + 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC, + 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32, + 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01, + 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A, + 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, + 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, + 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, + 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, + 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, + 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00, + 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, + 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, + 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00, + 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, + 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, + 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC, + 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, + 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, + 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, + 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, + 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, + 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, + 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, + 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59, + 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28, + 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, + 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, + 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, + 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, + 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, + 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, + 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF, + 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, + 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, + 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, + 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, + 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, + 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, + 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF, + 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, + 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, + 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, + 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, + 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, + 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E, + 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01, + 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B, + 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, + 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, + 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, + 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, + 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, + 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, + 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, + 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, + 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, + 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, + 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, + 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, + 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, + 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, + 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, + 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, + 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7, + 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14, + 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00, + 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47, + 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, + 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, + 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, + 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, + 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, + 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, + 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, + 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, + 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, + 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, + 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, + 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, + 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, + 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, + 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12, + 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B, + 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF, + 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19, + 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, + 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, + 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, + 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, + 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, + 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, + 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, + 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, + 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, + 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, + 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, + 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, + 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, + 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, + 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, + 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18, + 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48, + 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87, + 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE, + 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61, + 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, + 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, + 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, + 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, + 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, + 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, + 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, + 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, + 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, + 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32, + 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, + 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, + 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, + 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, + 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, + 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, + 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, + 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C, + 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47, + 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3, + 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01, + 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73, + 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF, + 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, + 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, + 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, + 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, + 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, + 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04, + 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, + 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, + 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, + 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, + 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, + 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, + 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00, + 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, + 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, + 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, + 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, + 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5, + 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43, + 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE, + 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01, + 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1, + 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF, + 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, + 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, + 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, + 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, + 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, + 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, + 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, + 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00, + 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, + 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, + 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, + 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00, + 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, + 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, + 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, + 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, + 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, + 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D, + 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03, + 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, + 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02, + 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF, + 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, + 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, + 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, + 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, + 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, + 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06, + 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16, + 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF, + 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88, + 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02, + 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC, + 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A, + 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18, + 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF, + 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3, + 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00, + 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75, + 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10, + 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72, + 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF, + 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6, + 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF, + 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C, + 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10, + 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD, + 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00, + 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35, + 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1, + 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E, + 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9, + 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02, + 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F, + 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF, + 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71, + 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05, + 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06, + 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6, + 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF, + 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B, + 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02, + 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89, + 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E, + 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1, + 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, + 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD, + 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00, + 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F, + 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10, + 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E, + 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF, + 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA, + 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF, + 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6, + 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10, + 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2, + 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00, + 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3, + 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50, + 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E, + 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2, + 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02, + 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03, + 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF, + 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB, + 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06, + 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00, + 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05, + 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73, + 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, + 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88, + 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02, + 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, + 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E, + 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9, + 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF, + 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E, + 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00, + 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C, + 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10, + 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C, + 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF, + 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38, + 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF, + 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6, + 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10, + 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97, + 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00, + 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D, + 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF, + 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB, + 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A, + 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, + 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02, + 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99, + 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF, + 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45, + 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06, + 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04, + 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4, + 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF, + 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00, + 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03, + 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, + 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D, + 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7, + 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF, + 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05, + 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01, + 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, + 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10, + 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D, + 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF, + 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C, + 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF, + 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB, + 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10, + 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D, + 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00, + 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3, + 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, + 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C, + 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B, + 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01, + 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F, + 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF, + 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF, + 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07, + 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6, + 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09, + 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D, + 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73, + 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03, + 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8, + 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D, + 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA, + 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF, + 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9, + 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF, + 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70, + 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F, + 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72, + 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF, + 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6, + 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF, + 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96, + 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10, + 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84, + 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF, + 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52, + 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, + 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3, + 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B, + 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE, + 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01, + 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5, + 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, + 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C, + 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07, + 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC, + 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09, + 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2, + 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3, + 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04, + 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3, + 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C, + 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24, + 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF, + 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E, + 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF, + 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75, + 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F, + 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A, + 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF, + 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15, + 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF, + 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0, + 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F, + 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7, + 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF, + 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC, + 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF, + 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F, + 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C, + 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC, + 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04, + 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E, + 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, + 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B, + 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08, + 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2, + 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08, + 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E, + 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50, + 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04, + 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE, + 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C, + 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53, + 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF, + 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88, + 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF, + 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C, + 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F, + 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86, + 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF, + 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A, + 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF, + 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF, + 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F, + 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF, + 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E, + 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF, + 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2, + 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C, + 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9, + 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04, + 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6, + 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD, + 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09, + 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7, + 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07, + 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91, + 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, + 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82, + 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01, + 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8, + 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B, + 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A, + 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, + 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65, + 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF, + 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90, + 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10, + 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81, + 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF, + 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73, + 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF, + 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74, + 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F, + 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4, + 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF, + 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8, + 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF, + 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B, + 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D, + 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, + 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03, + 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32, + 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, + 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72, + 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09, + 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, + 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07, + 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA, + 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, + 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14, + 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01, + 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, + 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B, + 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6, + 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, + 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37, + 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00, + 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81, + 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10, + 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79, + 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF, + 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91, + 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF, + 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41, + 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10, + 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA, + 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01, + 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88, + 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, + 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B, + 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D, + 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, + 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03, + 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1, + 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF, + 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22, + 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04, + 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD, + 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06, + 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A, + 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, + 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F, + 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02, + 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, + 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A, + 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A, + 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF, + 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF, + 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00, + 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77, + 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10, + 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73, + 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF, + 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4, + 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF, + 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14, + 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10, + 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF, + 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00, + 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A, + 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1, + 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E, + 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA, + 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02, + 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53, + 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF, + 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D, + 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05, + 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06, + 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF, + 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, + 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23, + 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02, + 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87, + 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E, + 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D, + 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, + 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB, + 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00, + 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70, + 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10, + 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E, + 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF, + 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB, + 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF, + 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC, + 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10, + 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4, + 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00, + 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9, + 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F, + 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E, + 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3, + 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02, + 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7, + 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF, + 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7, + 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06, + 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05, + 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B, + 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, + 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1, + 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02, + 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91, + 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E, + 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5, + 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, + 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D, + 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00, + 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D, + 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10, + 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C, + 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, + 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27, + 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF, + 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB, + 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10, + 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99, + 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00, + 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34, + 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, + 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3, + 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A, + 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00, + 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02, + 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D, + 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61, + 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06, + 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05, + 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC, + 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, + 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A, + 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03, + 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, + 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D, + 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1, + 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, + 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16, + 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00, + 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C, + 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10, + 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D, + 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, + 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D, + 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF, + 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0, + 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10, + 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F, + 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00, + 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA, + 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, + 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43, + 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A, + 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01, + 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13, + 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, + 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB, + 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07, + 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4, + 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09, + 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E, + 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E, + 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03, + 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6, + 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D, + 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3, + 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF, + 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA, + 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF, + 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, + 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F, + 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71, + 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF, + 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47, + 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE, + 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30, + 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20, + 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42, + 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF, + 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC, + 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00, + 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F, + 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09, + 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB, + 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2, + 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73, + 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03, + 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE, + 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC, + 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49, + 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, + 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65, + 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB, + 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36, + 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E, + 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02, + 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00, + 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0, + 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00, + 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1, + 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21, + 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00, + 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62, + 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00, + 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52, + 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20, + 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D, + 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF, + 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2, + 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78, + 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A, + 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB, + 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4, + 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58, + 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04, + 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB, + 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F, + 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56, + 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB, + 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D, + 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D, + 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9, + 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00, + 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A, + 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00, + 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB, + 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21, + 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00, + 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F, + 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00, + 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71, + 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20, + 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58, + 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00, + 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9, + 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96, + 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B, + 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC, + 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB, + 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10, + 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF, + 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37, + 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB, + 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB, + 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D, + 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C, + 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB, + 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24, + 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C, + 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD, + 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00, + 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24, + 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00, + 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3, + 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21, + 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6, + 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00, + 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A, + 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00, + 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C, + 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21, + 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64, + 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00, + 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02, + 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00, + 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB, + 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C, + 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB, + 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB, + 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56, + 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF, + 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69, + 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB, + 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB, + 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44, + 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, + 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49, + 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB, + 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, + 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B, + 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E, + 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00, + 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD, + 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00, + 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8, + 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20, + 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2, + 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00, + 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF, + 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00, + 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4, + 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21, + 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70, + 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00, + 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A, + 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00, + 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4, + 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D, + 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA, + 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB, + 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8, + 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF, + 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4, + 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB, + 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB, + 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53, + 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B, + 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB, + 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16, + 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A, + 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D, + 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00, + 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26, + 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF, + 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB, + 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20, + 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD, + 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00, + 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5, + 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9, + 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21, + 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D, + 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00, + 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32, + 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00, + 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12, + 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E, + 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9, + 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB, + 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05, + 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF, + 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA, + 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC, + 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, + 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03, + 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2, + 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54, + 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB, + 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, + 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09, + 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A, + 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00, + 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90, + 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF, + 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC, + 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20, + 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7, + 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00, + 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4, + 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00, + 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB, + 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21, + 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A, + 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00, + 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47, + 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00, + 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43, + 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F, + 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8, + 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB, + 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E, + 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF, + 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B, + 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC, + 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8, + 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02, + 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65, + 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66, + 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB, + 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A, + 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08, + 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4, + 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00, + 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB, + 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF, + 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA, + 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F, + 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1, + 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00, + 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8, + 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00, + 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5, + 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17, + 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00, + 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59, + 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00, + 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77, + 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10, + 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7, + 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB, + 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2, + 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF, + 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98, + 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC, + 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7, + 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01, + 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29, + 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80, + 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB, + 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05, + 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07, + 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC, + 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00, + 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38, + 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF, + 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6, + 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F, + 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9, + 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00, + 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91, + 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00, + 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30, + 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18, + 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00, + 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66, + 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00, + 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE, + 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11, + 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7, + 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC, + 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62, + 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF, + 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF, + 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC, + 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7, + 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01, + 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF, + 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, + 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F, + 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD, + 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, + 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06, + 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72, + 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00, + 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78, + 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF, + 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1, + 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E, + 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0, + 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00, + 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F, + 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00, + 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B, + 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19, + 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00, + 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E, + 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00, + 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7, + 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13, + 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7, + 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC, + 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED, + 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF, + 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73, + 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD, + 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7, + 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00, + 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5, + 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, + 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87, + 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD, + 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05, + 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38, + 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, + 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC, + 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE, + 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA, + 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E, + 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6, + 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00, + 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21, + 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00, + 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5, + 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A, + 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05, + 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD, + 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63, + 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22, + 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14, + 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7, + 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC, + 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84, + 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF, + 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1, + 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD, + 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7, + 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF, + 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E, + 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, + 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B, + 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD, + 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, + 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04, + 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC, + 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00, + 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3, + 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE, + 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2, + 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D, + 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA, + 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00, + 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6, + 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00, + 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD, + 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B, + 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09, + 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE, + 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48, + 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E, + 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15, + 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8, + 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC, + 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26, + 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF, + 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C, + 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE, + 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8, + 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF, + 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49, + 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, + 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69, + 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC, + 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B, + 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16, + 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD, + 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, + 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0, + 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE, + 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9, + 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C, + 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB, + 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00, + 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C, + 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00, + 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13, + 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C, + 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F, + 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE, + 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36, + 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A, + 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16, + 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9, + 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC, + 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4, + 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF, + 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12, + 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE, + 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9, + 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE, + 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18, + 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, + 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54, + 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC, + 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E, + 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15, + 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC, + 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04, + 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE, + 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE, + 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B, + 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB, + 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00, + 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14, + 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00, + 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47, + 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C, + 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15, + 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE, + 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C, + 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00, + 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56, + 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04, + 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD, + 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C, + 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF, + 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6, + 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8, + 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE, + 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42, + 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5, + 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03, + 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC, + 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3, + 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF, + 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF, + 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3, + 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE, + 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68, + 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A, + 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1, + 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00, + 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8, + 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00, + 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB, + 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27, + 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3, + 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE, + 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E, + 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF, + 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4, + 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4, + 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01, + 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03, + 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F, + 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62, + 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05, + 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05, + 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB, + 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54, + 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03, + 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1, + 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3, + 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7, + 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF, + 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB, + 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE, + 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55, + 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28, + 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4, + 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00, + 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8, + 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00, + 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8, + 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29, + 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD, + 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE, + 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02, + 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A, + 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3, + 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01, + 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03, + 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28, + 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16, + 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05, + 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05, + 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00, + 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE, + 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04, + 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5, + 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4, + 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2, + 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF, + 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F, + 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE, + 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43, + 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26, + 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6, + 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00, + 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98, + 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00, + 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25, + 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B, + 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7, + 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE, + 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8, + 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F, + 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3, + 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03, + 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39, + 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC, + 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05, + 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05, + 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40, + 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71, + 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04, + 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9, + 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4, + 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E, + 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF, + 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70, + 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE, + 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31, + 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24, + 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7, + 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00, + 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68, + 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00, + 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51, + 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D, + 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1, + 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE, + 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0, + 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF, + 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4, + 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3, + 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02, + 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41, + 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00, + 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84, + 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06, + 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9, + 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8, + 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D, + 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04, + 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5, + 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D, + 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF, + 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42, + 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD, + 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20, + 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22, + 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07, + 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00, + 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24, + 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00, + 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C, + 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F, + 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB, + 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE, + 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC, + 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF, + 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79, + 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3, + 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02, + 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40, + 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C, + 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06, + 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E, + 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9, + 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2, + 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0, + 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04, + 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5, + 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F, + 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF, + 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16, + 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD, + 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10, + 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20, + 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17, + 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD, + 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00, + 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7, + 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31, + 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5, + 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF, + 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C, + 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF, + 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F, + 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2, + 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02, + 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35, + 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3, + 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06, + 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F, + 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA, + 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2, + 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A, + 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05, + 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, + 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6, + 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34, + 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF, + 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED, + 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD, + 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D, + 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26, + 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00, + 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F, + 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, + 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1, + 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32, + 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0, + 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF, + 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41, + 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF, + 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6, + 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2, + 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04, + 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01, + 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20, + 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00, + 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8, + 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06, + 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB, + 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5, + 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, + 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1, + 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC, + 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6, + 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D, + 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, + 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8, + 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD, + 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2, + 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B, + 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33, + 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00, + 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB, + 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01, + 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9, + 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34, + 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA, + 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF, + 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B, + 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF, + 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F, + 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2, + 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05, + 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01, + 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02, + 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A, + 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06, + 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC, + 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE, + 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, + 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2, + 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC, + 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7, + 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09, + 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB, + 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD, + 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4, + 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19, + 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F, + 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00, + 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C, + 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01, + 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20, + 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36, + 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5, + 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF, + 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB, + 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF, + 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A, + 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3, + 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06, + 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01, + 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9, + 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00, + 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09, + 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06, + 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC, + 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC, + 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00, + 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34, + 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC, + 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8, + 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8, + 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95, + 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD, + 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7, + 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17, + 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00, + 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87, + 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01, + 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45, + 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37, + 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0, + 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF, + 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61, + 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF, + 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8, + 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3, + 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06, + 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00, + 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7, + 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3, + 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06, + 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD, + 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F, + 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87, + 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC, + 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96, + 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08, + 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D, + 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89, + 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD, + 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB, + 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15, + 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF, + 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5, + 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01, + 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69, + 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39, + 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B, + 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF, + 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF, + 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF, + 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79, + 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3, + 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07, + 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00, + 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A, + 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58, + 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06, + 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10, + 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE, + 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27, + 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE, + 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC, + 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92, + 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06, + 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56, + 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88, + 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD, + 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13, + 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF, + 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6, + 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01, + 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A, + 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A, + 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97, + 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00, + 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93, + 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF, + 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D, + 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3, + 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08, + 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00, + 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23, + 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00, + 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7, + 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05, + 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10, + 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE, + 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45, + 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00, + 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69, + 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC, + 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F, + 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04, + 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D, + 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94, + 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC, + 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6, + 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11, + 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF, + 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB, + 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01, + 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9, + 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C, + 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93, + 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00, + 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F, + 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF, + 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4, + 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4, + 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09, + 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF, + 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1, + 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00, + 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90, + 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05, + 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F, + 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF, + 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67, + 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00, + 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8, + 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD, + 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C, + 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02, + 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41, + 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF, + 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A, + 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01, + 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD, + 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F, + 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63, + 0x01, 0x8D, 0xFF, 0x0F, 0x00 +}; + +static u16 +coefficient_sizes[8 * 2] = { + /* Playback */ + 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000, + /* capture */ + 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000, +}; + diff -urN linux-2.4.21-rc1.orig/sound/pci/rme32.c linux/sound/pci/rme32.c --- linux-2.4.21-rc1.orig/sound/pci/rme32.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme32.c 2003-01-31 08:20:31.000000000 -0700 @@ -0,0 +1,2030 @@ +/* + * ALSA driver for RME Digi32, Digi32/8 and Digi32 PRO audio interfaces + * + * Copyright (c) 2002, 2003 Martin Langer + * + * Thanks to : Anders Torger , + * Henk Hesselink + * for writing the digi96-driver + * and RME for all informations. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * **************************************************************************** + * + * Note #1 "Sek'd models" ................................... martin 2002-12-07 + * + * Identical soundcards by Sek'd were labeled: + * RME Digi 32 = Sek'd Prodif 32 + * RME Digi 32 Pro = Sek'd Prodif 96 + * RME Digi 32/8 = Sek'd Prodif Gold + * + * **************************************************************************** + * + * Note #2 "full duplex mode" ............................... martin 2002-12-07 + * + * Full duplex doesn't work. All cards (32, 32/8, 32Pro) are working identical + * in this mode. Rec data and play data are using the same buffer therefore. At + * first you have got the playing bits in the buffer and then (after playing + * them) they were overwitten by the captured sound of the CS8412/14. Both + * modes (play/record) are running harmonically hand in hand in the same buffer + * and you have only one start bit plus one interrupt bit to control this + * paired action. + * This is opposite to the latter rme96 where playing and capturing is totally + * separated and so their full duplex mode is supported by alsa (using two + * start bits and two interrupts for two different buffers). + * But due to the wrong sequence of playing and capturing ALSA shows no solved + * full duplex support for the rme32 at the moment. That's bad, but I'm not + * able to solve it. Are you motivated enough to solve this problem now? Your + * patch would be welcome! + * + * **************************************************************************** + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for RME Digi32 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for RME Digi32 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable RME Digi32 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_AUTHOR("Martin Langer "); +MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Digi32}," "{RME,Digi32/8}," "{RME,Digi32 PRO}}"); + +/* Defines for RME Digi32 series */ +#define RME32_SPDIF_NCHANNELS 2 + +/* Playback and capture buffer size */ +#define RME32_BUFFER_SIZE 0x20000 + +/* IO area size */ +#define RME32_IO_SIZE 0x30000 + +/* IO area offsets */ +#define RME32_IO_DATA_BUFFER 0x0 +#define RME32_IO_CONTROL_REGISTER 0x20000 +#define RME32_IO_GET_POS 0x20000 +#define RME32_IO_CONFIRM_ACTION_IRQ 0x20004 +#define RME32_IO_RESET_POS 0x20100 + +/* Write control register bits */ +#define RME32_WCR_START (1 << 0) /* startbit */ +#define RME32_WCR_MONO (1 << 1) /* 0=stereo, 1=mono + Setting the whole card to mono + doesn't seem to be very useful. + A software-solution can handle + full-duplex with one direction in + stereo and the other way in mono. + So, the hardware should work all + the time in stereo! */ +#define RME32_WCR_MODE24 (1 << 2) /* 0=16bit, 1=32bit */ +#define RME32_WCR_SEL (1 << 3) /* 0=input on output, 1=normal playback/capture */ +#define RME32_WCR_FREQ_0 (1 << 4) /* frequency (play) */ +#define RME32_WCR_FREQ_1 (1 << 5) +#define RME32_WCR_INP_0 (1 << 6) /* input switch */ +#define RME32_WCR_INP_1 (1 << 7) +#define RME32_WCR_RESET (1 << 8) /* Reset address */ +#define RME32_WCR_MUTE (1 << 9) /* digital mute for output */ +#define RME32_WCR_PRO (1 << 10) /* 1=professional, 0=consumer */ +#define RME32_WCR_DS_BM (1 << 11) /* 1=DoubleSpeed (only PRO-Version); 1=BlockMode (only Adat-Version) */ +#define RME32_WCR_ADAT (1 << 12) /* Adat Mode (only Adat-Version) */ +#define RME32_WCR_AUTOSYNC (1 << 13) /* AutoSync */ +#define RME32_WCR_PD (1 << 14) /* DAC Reset (only PRO-Version) */ +#define RME32_WCR_EMP (1 << 15) /* 1=Emphasis on (only PRO-Version) */ + +#define RME32_WCR_BITPOS_FREQ_0 4 +#define RME32_WCR_BITPOS_FREQ_1 5 +#define RME32_WCR_BITPOS_INP_0 6 +#define RME32_WCR_BITPOS_INP_1 7 + +/* Read control register bits */ +#define RME32_RCR_AUDIO_ADDR_MASK 0x10001 +#define RME32_RCR_LOCK (1 << 23) /* 1=locked, 0=not locked */ +#define RME32_RCR_ERF (1 << 26) /* 1=Error, 0=no Error */ +#define RME32_RCR_FREQ_0 (1 << 27) /* CS841x frequency (record) */ +#define RME32_RCR_FREQ_1 (1 << 28) +#define RME32_RCR_FREQ_2 (1 << 29) +#define RME32_RCR_KMODE (1 << 30) /* card mode: 1=PLL, 0=quartz */ +#define RME32_RCR_IRQ (1 << 31) /* interrupt */ + +#define RME32_RCR_BITPOS_F0 27 +#define RME32_RCR_BITPOS_F1 28 +#define RME32_RCR_BITPOS_F2 29 + +/* Input types */ +#define RME32_INPUT_OPTICAL 0 +#define RME32_INPUT_COAXIAL 1 +#define RME32_INPUT_INTERNAL 2 +#define RME32_INPUT_XLR 3 + +/* Clock modes */ +#define RME32_CLOCKMODE_SLAVE 0 +#define RME32_CLOCKMODE_MASTER_32 1 +#define RME32_CLOCKMODE_MASTER_44 2 +#define RME32_CLOCKMODE_MASTER_48 3 + +/* Block sizes in bytes */ +#define RME32_BLOCK_SIZE 8192 + +/* Hardware revisions */ +#define RME32_32_REVISION 192 +#define RME32_328_REVISION_OLD 100 +#define RME32_328_REVISION_NEW 101 +#define RME32_PRO_REVISION_WITH_8412 192 +#define RME32_PRO_REVISION_WITH_8414 150 + + +/* PCI vendor/device ID's */ +#ifndef PCI_VENDOR_ID_XILINX_RME +# define PCI_VENDOR_ID_XILINX_RME 0xea60 +#endif +#ifndef PCI_DEVICE_ID_DIGI32 +# define PCI_DEVICE_ID_DIGI32 0x9896 +#endif +#ifndef PCI_DEVICE_ID_DIGI32_PRO +# define PCI_DEVICE_ID_DIGI32_PRO 0x9897 +#endif +#ifndef PCI_DEVICE_ID_DIGI32_8 +# define PCI_DEVICE_ID_DIGI32_8 0x9898 +#endif + +typedef struct snd_rme32 { + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + u32 wcreg; /* cached write control register value */ + u32 wcreg_spdif; /* S/PDIF setup */ + u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */ + u32 rcreg; /* cached read control register value */ + + u8 rev; /* card revision number */ + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + int playback_frlog; /* log2 of framesize */ + int capture_frlog; + + size_t playback_periodsize; /* in bytes, zero if not used */ + size_t capture_periodsize; /* in bytes, zero if not used */ + + snd_pcm_uframes_t playback_last_appl_ptr; + size_t playback_ptr; + size_t capture_ptr; + + snd_card_t *card; + snd_pcm_t *spdif_pcm; + snd_pcm_t *adat_pcm; + struct pci_dev *pci; + snd_kcontrol_t *spdif_ctl; +} rme32_t; + +static struct pci_device_id snd_rme32_ids[] __devinitdata = { + {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, + {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, + {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32_PRO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, snd_rme32_ids); + +#define RME32_ISWORKING(rme32) ((rme32)->wcreg & RME32_WCR_START) +#define RME32_PRO_WITH_8414(rme32) ((rme32)->pci->device == PCI_DEVICE_ID_DIGI32_PRO && (rme32)->rev == RME32_PRO_REVISION_WITH_8414) + +static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream); + +static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream); + +static int +snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd); + +static int +snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd); + +static snd_pcm_uframes_t +snd_rme32_playback_pointer(snd_pcm_substream_t * substream); + +static snd_pcm_uframes_t +snd_rme32_capture_pointer(snd_pcm_substream_t * substream); + +static void snd_rme32_proc_init(rme32_t * rme32); + +static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32); + +static inline unsigned int snd_rme32_playback_ptr(rme32_t * rme32) +{ + + return (readl(rme32->iobase + RME32_IO_GET_POS) + & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->playback_frlog; +} + +static inline unsigned int snd_rme32_capture_ptr(rme32_t * rme32) +{ + return (readl(rme32->iobase + RME32_IO_GET_POS) + & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->capture_frlog; +} + +static int snd_rme32_ratecode(int rate) +{ + switch (rate) { + case 32000: return SNDRV_PCM_RATE_32000; + case 44100: return SNDRV_PCM_RATE_44100; + case 48000: return SNDRV_PCM_RATE_48000; + case 64000: return SNDRV_PCM_RATE_64000; + case 88200: return SNDRV_PCM_RATE_88200; + case 96000: return SNDRV_PCM_RATE_96000; + } + return 0; +} + +static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + count <<= rme32->playback_frlog; + pos <<= rme32->playback_frlog; + memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count); + return 0; +} + +static int snd_rme32_playback_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, snd_pcm_uframes_t count) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + count <<= rme32->playback_frlog; + pos <<= rme32->playback_frlog; + if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, + src, count)) + return -EFAULT; + return 0; +} + +static int snd_rme32_capture_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, snd_pcm_uframes_t count) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + count <<= rme32->capture_frlog; + pos <<= rme32->capture_frlog; + if (copy_to_user_fromio(dst, + rme32->iobase + RME32_IO_DATA_BUFFER + pos, + count)) + return -EFAULT; + return 0; +} + +/* + * Digital output capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme32_playback_spdif_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = RME32_BUFFER_SIZE, + .period_bytes_min = RME32_BLOCK_SIZE, + .period_bytes_max = RME32_BLOCK_SIZE, + .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * Digital input capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme32_capture_spdif_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = RME32_BUFFER_SIZE, + .period_bytes_min = RME32_BLOCK_SIZE, + .period_bytes_max = RME32_BLOCK_SIZE, + .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * Digital output capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme32_playback_adat_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats= SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = RME32_BUFFER_SIZE, + .period_bytes_min = RME32_BLOCK_SIZE, + .period_bytes_max = RME32_BLOCK_SIZE, + .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * Digital input capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme32_capture_adat_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = RME32_BUFFER_SIZE, + .period_bytes_min = RME32_BLOCK_SIZE, + .period_bytes_max = RME32_BLOCK_SIZE, + .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, +}; + +static void snd_rme32_reset_dac(rme32_t *rme32) +{ + writel(rme32->wcreg | RME32_WCR_PD, + rme32->iobase + RME32_IO_CONTROL_REGISTER); + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); +} + +static int snd_rme32_playback_getrate(rme32_t * rme32) +{ + int rate; + + rate = ((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_0) & 1) + + (((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_1) & 1) << 1); + switch (rate) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme32->wcreg & RME32_WCR_DS_BM) ? rate << 1 : rate; +} + +static int snd_rme32_capture_getrate(rme32_t * rme32, int *is_adat) +{ + int n; + + *is_adat = 0; + if (rme32->rcreg & RME32_RCR_LOCK) { + /* ADAT rate */ + *is_adat = 1; + } + if (rme32->rcreg & RME32_RCR_ERF) { + return -1; + } + + /* S/PDIF rate */ + n = ((rme32->rcreg >> RME32_RCR_BITPOS_F0) & 1) + + (((rme32->rcreg >> RME32_RCR_BITPOS_F1) & 1) << 1) + + (((rme32->rcreg >> RME32_RCR_BITPOS_F2) & 1) << 2); + + if (RME32_PRO_WITH_8414(rme32)) + switch (n) { /* supporting the CS8414 */ + case 0: + case 1: + case 2: + return -1; + case 3: + return 96000; + case 4: + return 88200; + case 5: + return 48000; + case 6: + return 44100; + case 7: + return 32000; + default: + return -1; + break; + } + else + switch (n) { /* supporting the CS8412 */ + case 0: + return -1; + case 1: + return 48000; + case 2: + return 44100; + case 3: + return 32000; + case 4: + return 48000; + case 5: + return 44100; + case 6: + return 44056; + case 7: + return 32000; + default: + break; + } + return -1; +} + +static int snd_rme32_playback_setrate(rme32_t * rme32, int rate) +{ + int ds; + + ds = rme32->wcreg & RME32_WCR_DS_BM; + switch (rate) { + case 32000: + rme32->wcreg &= ~RME32_WCR_DS_BM; + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & + ~RME32_WCR_FREQ_1; + break; + case 44100: + rme32->wcreg &= ~RME32_WCR_DS_BM; + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_1) & + ~RME32_WCR_FREQ_0; + break; + case 48000: + rme32->wcreg &= ~RME32_WCR_DS_BM; + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | + RME32_WCR_FREQ_1; + break; + case 64000: + if (rme32->pci->device != PCI_DEVICE_ID_DIGI32_PRO) + return -EINVAL; + rme32->wcreg |= RME32_WCR_DS_BM; + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & + ~RME32_WCR_FREQ_1; + break; + case 88200: + if (rme32->pci->device != PCI_DEVICE_ID_DIGI32_PRO) + return -EINVAL; + rme32->wcreg |= RME32_WCR_DS_BM; + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_1) & + ~RME32_WCR_FREQ_0; + break; + case 96000: + if (rme32->pci->device != PCI_DEVICE_ID_DIGI32_PRO) + return -EINVAL; + rme32->wcreg |= RME32_WCR_DS_BM; + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | + RME32_WCR_FREQ_1; + break; + default: + return -EINVAL; + } + if ((!ds && rme32->wcreg & RME32_WCR_DS_BM) || + (ds && !(rme32->wcreg & RME32_WCR_DS_BM))) + { + /* change to/from double-speed: reset the DAC (if available) */ + snd_rme32_reset_dac(rme32); + } else { + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + } + return 0; +} + +static int snd_rme32_setclockmode(rme32_t * rme32, int mode) +{ + switch (mode) { + case RME32_CLOCKMODE_SLAVE: + /* AutoSync */ + rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) & + ~RME32_WCR_FREQ_1; + break; + case RME32_CLOCKMODE_MASTER_32: + /* Internal 32.0kHz */ + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & + ~RME32_WCR_FREQ_1; + break; + case RME32_CLOCKMODE_MASTER_44: + /* Internal 44.1kHz */ + rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) | + RME32_WCR_FREQ_1; + break; + case RME32_CLOCKMODE_MASTER_48: + /* Internal 48.0kHz */ + rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | + RME32_WCR_FREQ_1; + break; + default: + return -EINVAL; + } + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + return 0; +} + +static int snd_rme32_getclockmode(rme32_t * rme32) +{ + return ((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_0) & 1) + + (((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_1) & 1) << 1); +} + +static int snd_rme32_setinputtype(rme32_t * rme32, int type) +{ + switch (type) { + case RME32_INPUT_OPTICAL: + rme32->wcreg = (rme32->wcreg & ~RME32_WCR_INP_0) & + ~RME32_WCR_INP_1; + break; + case RME32_INPUT_COAXIAL: + rme32->wcreg = (rme32->wcreg | RME32_WCR_INP_0) & + ~RME32_WCR_INP_1; + break; + case RME32_INPUT_INTERNAL: + rme32->wcreg = (rme32->wcreg & ~RME32_WCR_INP_0) | + RME32_WCR_INP_1; + break; + case RME32_INPUT_XLR: + rme32->wcreg = (rme32->wcreg | RME32_WCR_INP_0) | + RME32_WCR_INP_1; + break; + default: + return -EINVAL; + } + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + return 0; +} + +static int snd_rme32_getinputtype(rme32_t * rme32) +{ + return ((rme32->wcreg >> RME32_WCR_BITPOS_INP_0) & 1) + + (((rme32->wcreg >> RME32_WCR_BITPOS_INP_1) & 1) << 1); +} + +static void +snd_rme32_setframelog(rme32_t * rme32, int n_channels, int is_playback) +{ + int frlog; + + if (n_channels == 2) { + frlog = 1; + } else { + /* assume 8 channels */ + frlog = 3; + } + if (is_playback) { + frlog += (rme32->wcreg & RME32_WCR_MODE24) ? 2 : 1; + rme32->playback_frlog = frlog; + } else { + frlog += (rme32->wcreg & RME32_WCR_MODE24) ? 2 : 1; + rme32->capture_frlog = frlog; + } +} + +static int snd_rme32_setformat(rme32_t * rme32, int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme32->wcreg &= ~RME32_WCR_MODE24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme32->wcreg |= RME32_WCR_MODE24; + break; + default: + return -EINVAL; + } + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme32_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * params) +{ + int err, rate, dummy; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + + if ((err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(params))) < 0) + return err; + spin_lock_irq(&rme32->lock); + if ((rme32->rcreg & RME32_RCR_KMODE) && + (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { + /* AutoSync */ + if ((int)params_rate(params) != rate) { + spin_unlock_irq(&rme32->lock); + return -EIO; + } + } else if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) { + spin_unlock_irq(&rme32->lock); + return err; + } + if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) { + spin_unlock_irq(&rme32->lock); + return err; + } + + snd_rme32_setframelog(rme32, params_channels(params), 1); + if (rme32->capture_periodsize != 0) { + if (params_period_size(params) << rme32->playback_frlog != rme32->capture_periodsize) { + spin_unlock_irq(&rme32->lock); + return -EBUSY; + } + } + rme32->playback_periodsize = params_period_size(params) << rme32->playback_frlog; + /* S/PDIF setup */ + if ((rme32->wcreg & RME32_WCR_ADAT) == 0) { + rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP); + writel(rme32->wcreg |= + rme32->wcreg_spdif_stream, + rme32->iobase + RME32_IO_CONTROL_REGISTER); + } + spin_unlock_irq(&rme32->lock); + + return 0; +} + +static int snd_rme32_playback_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int +snd_rme32_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * params) +{ + unsigned long flags; + int err, isadat, rate; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme32->lock, flags); + /* enable AutoSync for record-preparing */ + rme32->wcreg |= RME32_WCR_AUTOSYNC; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + + if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme32->lock, flags); + return err; + } + if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) { + spin_unlock_irqrestore(&rme32->lock, flags); + return err; + } + if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { + if ((int)params_rate(params) != rate) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EIO; + } + if ((isadat && runtime->hw.channels_min == 2) || + (!isadat && runtime->hw.channels_min == 8)) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EIO; + } + } + /* AutoSync off for recording */ + rme32->wcreg &= ~RME32_WCR_AUTOSYNC; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + + snd_rme32_setframelog(rme32, params_channels(params), 0); + if (rme32->playback_periodsize != 0) { + if (params_period_size(params) << rme32->capture_frlog != + rme32->playback_periodsize) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } + } + rme32->capture_periodsize = + params_period_size(params) << rme32->capture_frlog; + spin_unlock_irqrestore(&rme32->lock, flags); + + return 0; +} + +static int snd_rme32_capture_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static void snd_rme32_playback_start(rme32_t * rme32, int from_pause) +{ + if (!from_pause) { + writel(0, rme32->iobase + RME32_IO_RESET_POS); + rme32->playback_last_appl_ptr = 0; + rme32->playback_ptr = 0; + } + + rme32->wcreg |= RME32_WCR_START; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); +} + +static void snd_rme32_capture_start(rme32_t * rme32, int from_pause) +{ + if (!from_pause) { + writel(0, rme32->iobase + RME32_IO_RESET_POS); + } + + rme32->wcreg |= RME32_WCR_START; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); +} + +static void snd_rme32_playback_stop(rme32_t * rme32) +{ + /* + * Check if there is an unconfirmed IRQ, if so confirm it, or else + * the hardware will not stop generating interrupts + */ + rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); + if (rme32->rcreg & RME32_RCR_IRQ) { + writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ); + } + rme32->wcreg &= ~RME32_WCR_START; + if (rme32->wcreg & RME32_WCR_SEL) + rme32->wcreg |= RME32_WCR_MUTE; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); +} + +static void snd_rme32_capture_stop(rme32_t * rme32) +{ + rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); + if (rme32->rcreg & RME32_RCR_IRQ) { + writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ); + } + rme32->wcreg &= ~RME32_WCR_START; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); +} + +static void +snd_rme32_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + rme32_t *rme32 = (rme32_t *) dev_id; + + rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); + if (!(rme32->rcreg & RME32_RCR_IRQ)) { + return; + } else { + if (rme32->capture_substream) { + snd_pcm_period_elapsed(rme32->capture_substream); + } + if (rme32->playback_substream) { + snd_pcm_period_elapsed(rme32->playback_substream); + } + writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ); + } +} + +static unsigned int period_bytes[] = { RME32_BLOCK_SIZE }; + + +#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { + .count = PERIOD_BYTES, + .list = period_bytes, + .mask = 0 +}; + +static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + int rate, dummy; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme32->lock, flags); + if (rme32->playback_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } + rme32->wcreg &= ~RME32_WCR_ADAT; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + rme32->playback_substream = substream; + rme32->playback_last_appl_ptr = 0; + rme32->playback_ptr = 0; + spin_unlock_irqrestore(&rme32->lock, flags); + + runtime->hw = snd_rme32_playback_spdif_info; + if (rme32->pci->device == PCI_DEVICE_ID_DIGI32_PRO) { + runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; + runtime->hw.rate_max = 96000; + } + if ((rme32->rcreg & RME32_RCR_KMODE) && + (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { + /* AutoSync */ + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); + + rme32->wcreg_spdif_stream = rme32->wcreg_spdif; + rme32->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme32->spdif_ctl->id); + return 0; +} + +static int snd_rme32_capture_spdif_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + int isadat, rate; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme32->lock, flags); + if (rme32->capture_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } + rme32->capture_substream = substream; + rme32->capture_ptr = 0; + spin_unlock_irqrestore(&rme32->lock, flags); + + runtime->hw = snd_rme32_capture_spdif_info; + if (RME32_PRO_WITH_8414(rme32)) { + runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; + runtime->hw.rate_max = 96000; + } + if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { + if (isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); + + return 0; +} + +static int +snd_rme32_playback_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int rate, dummy; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme32->lock, flags); + if (rme32->playback_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } + rme32->wcreg |= RME32_WCR_ADAT; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + rme32->playback_substream = substream; + rme32->playback_last_appl_ptr = 0; + rme32->playback_ptr = 0; + spin_unlock_irqrestore(&rme32->lock, flags); + + runtime->hw = snd_rme32_playback_adat_info; + if ((rme32->rcreg & RME32_RCR_KMODE) && + (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { + /* AutoSync */ + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme32_capture_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat, rate; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->hw = snd_rme32_capture_adat_info; + if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { + if (!isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme32->lock, flags); + if (rme32->capture_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } + rme32->capture_substream = substream; + rme32->capture_ptr = 0; + spin_unlock_irqrestore(&rme32->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); + return 0; +} + +static int snd_rme32_playback_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + int spdif = 0; + + spin_lock_irqsave(&rme32->lock, flags); + rme32->playback_substream = NULL; + rme32->playback_periodsize = 0; + spdif = (rme32->wcreg & RME32_WCR_ADAT) == 0; + spin_unlock_irqrestore(&rme32->lock, flags); + if (spdif) { + rme32->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &rme32->spdif_ctl->id); + } + return 0; +} + +static int snd_rme32_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&rme32->lock, flags); + rme32->capture_substream = NULL; + rme32->capture_periodsize = 0; + spin_unlock_irqrestore(&rme32->lock, flags); + return 0; +} + +static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme32->lock, flags); + if (RME32_ISWORKING(rme32)) { + snd_rme32_playback_stop(rme32); + } + writel(0, rme32->iobase + RME32_IO_RESET_POS); + if (rme32->wcreg & RME32_WCR_SEL) + rme32->wcreg &= ~RME32_WCR_MUTE; + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme32->lock, flags); + return 0; +} + +static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme32->lock, flags); + if (RME32_ISWORKING(rme32)) { + snd_rme32_capture_stop(rme32); + } + writel(0, rme32->iobase + RME32_IO_RESET_POS); + spin_unlock_irqrestore(&rme32->lock, flags); + return 0; +} + +static int +snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME32_ISWORKING(rme32)) { + if (substream != rme32->playback_substream) { + return -EBUSY; + } + snd_rme32_playback_start(rme32, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME32_ISWORKING(rme32)) { + if (substream != rme32->playback_substream) { + return -EBUSY; + } + snd_rme32_playback_stop(rme32); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME32_ISWORKING(rme32)) { + snd_rme32_playback_stop(rme32); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME32_ISWORKING(rme32)) { + snd_rme32_playback_start(rme32, 1); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME32_ISWORKING(rme32)) { + if (substream != rme32->capture_substream) { + return -EBUSY; + } + snd_rme32_capture_start(rme32, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME32_ISWORKING(rme32)) { + if (substream != rme32->capture_substream) { + return -EBUSY; + } + snd_rme32_capture_stop(rme32); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME32_ISWORKING(rme32)) { + snd_rme32_capture_stop(rme32); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME32_ISWORKING(rme32)) { + snd_rme32_capture_start(rme32, 1); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +snd_rme32_playback_pointer(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff; + size_t bytes; + + + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + diff = runtime->control->appl_ptr - + rme32->playback_last_appl_ptr; + rme32->playback_last_appl_ptr = runtime->control->appl_ptr; + if (diff != 0 && diff < -(snd_pcm_sframes_t) (runtime->boundary >> 1)) { + diff += runtime->boundary; + } + bytes = diff << rme32->playback_frlog; + if (bytes > RME32_BUFFER_SIZE - rme32->playback_ptr) { + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rme32->playback_ptr, + runtime->dma_area + rme32->playback_ptr, + RME32_BUFFER_SIZE - rme32->playback_ptr); + bytes -= RME32_BUFFER_SIZE - rme32->playback_ptr; + if (bytes > RME32_BUFFER_SIZE) { + bytes = RME32_BUFFER_SIZE; + } + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER, + runtime->dma_area, bytes); + rme32->playback_ptr = bytes; + } else if (bytes != 0) { + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rme32->playback_ptr, + runtime->dma_area + rme32->playback_ptr, bytes); + rme32->playback_ptr += bytes; + } + } + return snd_rme32_playback_ptr(rme32); +} + +static snd_pcm_uframes_t +snd_rme32_capture_pointer(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frameptr; + size_t ptr; + + frameptr = snd_rme32_capture_ptr(rme32); + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + ptr = frameptr << rme32->capture_frlog; + if (ptr > rme32->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme32->capture_ptr, + rme32->iobase + RME32_IO_DATA_BUFFER + + rme32->capture_ptr, + ptr - rme32->capture_ptr); + rme32->capture_ptr += ptr - rme32->capture_ptr; + } else if (ptr < rme32->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme32->capture_ptr, + rme32->iobase + RME32_IO_DATA_BUFFER + + rme32->capture_ptr, + RME32_BUFFER_SIZE - rme32->capture_ptr); + memcpy_fromio(runtime->dma_area, + rme32->iobase + RME32_IO_DATA_BUFFER, + ptr); + rme32->capture_ptr = ptr; + } + } + return frameptr; +} + +static snd_pcm_ops_t snd_rme32_playback_spdif_ops = { + .open = snd_rme32_playback_spdif_open, + .close = snd_rme32_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_playback_hw_params, + .hw_free = snd_rme32_playback_hw_free, + .prepare = snd_rme32_playback_prepare, + .trigger = snd_rme32_playback_trigger, + .pointer = snd_rme32_playback_pointer, + .copy = snd_rme32_playback_copy, + .silence = snd_rme32_playback_silence, +}; + +static snd_pcm_ops_t snd_rme32_capture_spdif_ops = { + .open = snd_rme32_capture_spdif_open, + .close = snd_rme32_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_capture_hw_params, + .hw_free = snd_rme32_capture_hw_free, + .prepare = snd_rme32_capture_prepare, + .trigger = snd_rme32_capture_trigger, + .pointer = snd_rme32_capture_pointer, + .copy = snd_rme32_capture_copy, +}; + +static snd_pcm_ops_t snd_rme32_playback_adat_ops = { + .open = snd_rme32_playback_adat_open, + .close = snd_rme32_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_playback_hw_params, + .hw_free = snd_rme32_playback_hw_free, + .prepare = snd_rme32_playback_prepare, + .trigger = snd_rme32_playback_trigger, + .pointer = snd_rme32_playback_pointer, + .copy = snd_rme32_playback_copy, + .silence = snd_rme32_playback_silence, +}; + +static snd_pcm_ops_t snd_rme32_capture_adat_ops = { + .open = snd_rme32_capture_adat_open, + .close = snd_rme32_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_capture_hw_params, + .hw_free = snd_rme32_capture_hw_free, + .prepare = snd_rme32_capture_prepare, + .trigger = snd_rme32_capture_trigger, + .pointer = snd_rme32_capture_pointer, + .copy = snd_rme32_capture_copy, +}; + +static void snd_rme32_free(void *private_data) +{ + rme32_t *rme32 = (rme32_t *) private_data; + + if (rme32 == NULL) { + return; + } + if (rme32->irq >= 0) { + snd_rme32_playback_stop(rme32); + snd_rme32_capture_stop(rme32); + free_irq(rme32->irq, (void *) rme32); + rme32->irq = -1; + } + if (rme32->iobase) { + iounmap((void *) rme32->iobase); + rme32->iobase = 0; + } + if (rme32->res_port != NULL) { + release_resource(rme32->res_port); + rme32->res_port = NULL; + } +} + +static void snd_rme32_free_spdif_pcm(snd_pcm_t * pcm) +{ + rme32_t *rme32 = (rme32_t *) pcm->private_data; + rme32->spdif_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void +snd_rme32_free_adat_pcm(snd_pcm_t *pcm) +{ + rme32_t *rme32 = (rme32_t *) pcm->private_data; + rme32->adat_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_rme32_create(rme32_t * rme32) +{ + struct pci_dev *pci = rme32->pci; + int err; + + rme32->irq = -1; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + rme32->port = pci_resource_start(rme32->pci, 0); + + if ((rme32->res_port = request_mem_region(rme32->port, RME32_IO_SIZE, "RME32")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", + rme32->port, rme32->port + RME32_IO_SIZE - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme32->irq = pci->irq; + + spin_lock_init(&rme32->lock); + if ((rme32->iobase = (unsigned long) ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { + snd_printk("unable to remap memory region 0x%lx-0x%lx\n", + rme32->port, rme32->port + RME32_IO_SIZE - 1); + return -ENOMEM; + } + + /* read the card's revision number */ + pci_read_config_byte(pci, 8, &rme32->rev); + + /* set up ALSA pcm device for S/PDIF */ + if ((err = snd_pcm_new(rme32->card, "Digi32 IEC958", 0, 1, 1, &rme32->spdif_pcm)) < 0) { + return err; + } + rme32->spdif_pcm->private_data = rme32; + rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm; + strcpy(rme32->spdif_pcm->name, "Digi32 IEC958"); + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_spdif_ops); + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_spdif_ops); + + rme32->spdif_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, + RME32_BUFFER_SIZE, + RME32_BUFFER_SIZE, + GFP_KERNEL); + + /* set up ALSA pcm device for ADAT */ + if ((pci->device == PCI_DEVICE_ID_DIGI32) || + (pci->device == PCI_DEVICE_ID_DIGI32_PRO)) { + /* ADAT is not available on DIGI32 and DIGI32 Pro */ + rme32->adat_pcm = NULL; + } + else { + if ((err = snd_pcm_new(rme32->card, "Digi32 ADAT", 1, + 1, 1, &rme32->adat_pcm)) < 0) + { + return err; + } + rme32->adat_pcm->private_data = rme32; + rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm; + strcpy(rme32->adat_pcm->name, "Digi32 ADAT"); + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_adat_ops); + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_adat_ops); + + rme32->adat_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, + RME32_BUFFER_SIZE, + RME32_BUFFER_SIZE, + GFP_KERNEL); + } + + + rme32->playback_periodsize = 0; + rme32->capture_periodsize = 0; + + /* make sure playback/capture is stopped, if by some reason active */ + snd_rme32_playback_stop(rme32); + snd_rme32_capture_stop(rme32); + + /* reset DAC */ + snd_rme32_reset_dac(rme32); + + /* reset buffer pointer */ + writel(0, rme32->iobase + RME32_IO_RESET_POS); + + /* set default values in registers */ + rme32->wcreg = RME32_WCR_SEL | /* normal playback */ + RME32_WCR_INP_0 | /* input select */ + RME32_WCR_MUTE; /* muting on */ + writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + + + /* init switch interface */ + if ((err = snd_rme32_create_switches(rme32->card, rme32)) < 0) { + return err; + } + + /* init proc interface */ + snd_rme32_proc_init(rme32); + + rme32->capture_substream = NULL; + rme32->playback_substream = NULL; + + return 0; +} + +/* + * proc interface + */ + +static void +snd_rme32_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) +{ + int n; + rme32_t *rme32 = (rme32_t *) entry->private_data; + + rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); + + snd_iprintf(buffer, rme32->card->longname); + snd_iprintf(buffer, " (index #%d)\n", rme32->card->number + 1); + + snd_iprintf(buffer, "\nGeneral settings\n"); + if (RME32_PRO_WITH_8414(rme32)) { + snd_iprintf(buffer, " receiver: CS8414\n"); + } else { + snd_iprintf(buffer, " receiver: CS8412\n"); + } + if (rme32->wcreg & RME32_WCR_MODE24) { + snd_iprintf(buffer, " format: 24 bit"); + } else { + snd_iprintf(buffer, " format: 16 bit"); + } + if (rme32->wcreg & RME32_WCR_MONO) { + snd_iprintf(buffer, ", Mono\n"); + } else { + snd_iprintf(buffer, ", Stereo\n"); + } + + snd_iprintf(buffer, "\nInput settings\n"); + switch (snd_rme32_getinputtype(rme32)) { + case RME32_INPUT_OPTICAL: + snd_iprintf(buffer, " input: optical"); + break; + case RME32_INPUT_COAXIAL: + snd_iprintf(buffer, " input: coaxial"); + break; + case RME32_INPUT_INTERNAL: + snd_iprintf(buffer, " input: internal"); + break; + case RME32_INPUT_XLR: + snd_iprintf(buffer, " input: XLR"); + break; + } + if (snd_rme32_capture_getrate(rme32, &n) < 0) { + snd_iprintf(buffer, "\n sample rate: no valid signal\n"); + } else { + if (n) { + snd_iprintf(buffer, " (8 channels)\n"); + } else { + snd_iprintf(buffer, " (2 channels)\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme32_capture_getrate(rme32, &n)); + } + + snd_iprintf(buffer, "\nOutput settings\n"); + if (rme32->wcreg & RME32_WCR_SEL) { + snd_iprintf(buffer, " output signal: normal playback"); + } else { + snd_iprintf(buffer, " output signal: same as input"); + } + if (rme32->wcreg & RME32_WCR_MUTE) { + snd_iprintf(buffer, " (muted)\n"); + } else { + snd_iprintf(buffer, "\n"); + } + + /* master output frequency */ + if (! + ((!(rme32->wcreg & RME32_WCR_FREQ_0)) + && (!(rme32->wcreg & RME32_WCR_FREQ_1)))) { + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme32_playback_getrate(rme32)); + } + if (rme32->rcreg & RME32_RCR_KMODE) { + snd_iprintf(buffer, " sample clock source: AutoSync\n"); + } else { + snd_iprintf(buffer, " sample clock source: Internal\n"); + } + if (rme32->wcreg & RME32_WCR_PRO) { + snd_iprintf(buffer, " format: AES/EBU (professional)\n"); + } else { + snd_iprintf(buffer, " format: IEC958 (consumer)\n"); + } + if (rme32->wcreg & RME32_WCR_EMP) { + snd_iprintf(buffer, " emphasis: on\n"); + } else { + snd_iprintf(buffer, " emphasis: off\n"); + } +} + +static void __devinit snd_rme32_proc_init(rme32_t * rme32) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(rme32->card, "rme32", &entry)) + snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read); +} + +/* + * control interface + */ + +static int +snd_rme32_info_loopback_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} +static int +snd_rme32_get_loopback_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme32->lock, flags); + ucontrol->value.integer.value[0] = + rme32->wcreg & RME32_WCR_SEL ? 0 : 1; + spin_unlock_irqrestore(&rme32->lock, flags); + return 0; +} +static int +snd_rme32_put_loopback_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.integer.value[0] ? 0 : RME32_WCR_SEL; + spin_lock_irqsave(&rme32->lock, flags); + val = (rme32->wcreg & ~RME32_WCR_SEL) | val; + change = val != rme32->wcreg; + if (ucontrol->value.integer.value[0]) + val &= ~RME32_WCR_MUTE; + else + val |= RME32_WCR_MUTE; + writel(rme32->wcreg = + val, rme32->iobase + RME32_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme32->lock, flags); + return change; +} + +static int +snd_rme32_info_inputtype_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + switch (rme32->pci->device) { + case PCI_DEVICE_ID_DIGI32: + case PCI_DEVICE_ID_DIGI32_8: + uinfo->value.enumerated.items = 3; + break; + case PCI_DEVICE_ID_DIGI32_PRO: + uinfo->value.enumerated.items = 4; + break; + default: + snd_BUG(); + break; + } + if (uinfo->value.enumerated.item > + uinfo->value.enumerated.items - 1) { + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + } + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme32_get_inputtype_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int items = 3; + + spin_lock_irqsave(&rme32->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme32_getinputtype(rme32); + + switch (rme32->pci->device) { + case PCI_DEVICE_ID_DIGI32: + case PCI_DEVICE_ID_DIGI32_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI32_PRO: + items = 4; + break; + default: + snd_BUG(); + break; + } + if (ucontrol->value.enumerated.item[0] >= items) { + ucontrol->value.enumerated.item[0] = items - 1; + } + + spin_unlock_irqrestore(&rme32->lock, flags); + return 0; +} +static int +snd_rme32_put_inputtype_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change, items = 3; + + switch (rme32->pci->device) { + case PCI_DEVICE_ID_DIGI32: + case PCI_DEVICE_ID_DIGI32_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI32_PRO: + items = 4; + break; + default: + snd_BUG(); + break; + } + val = ucontrol->value.enumerated.item[0] % items; + + spin_lock_irqsave(&rme32->lock, flags); + change = val != (unsigned int)snd_rme32_getinputtype(rme32); + snd_rme32_setinputtype(rme32, val); + spin_unlock_irqrestore(&rme32->lock, flags); + return change; +} + +static int +snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "AutoSync", + "Internal 32.0kHz", + "Internal 44.1kHz", + "Internal 48.0kHz" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme32_get_clockmode_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme32->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme32_getclockmode(rme32); + spin_unlock_irqrestore(&rme32->lock, flags); + return 0; +} +static int +snd_rme32_put_clockmode_control(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme32->lock, flags); + change = val != (unsigned int)snd_rme32_getclockmode(rme32); + snd_rme32_setclockmode(rme32, val); + spin_unlock_irqrestore(&rme32->lock, flags); + return change; +} + +static u32 snd_rme32_convert_from_aes(snd_aes_iec958_t * aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME32_WCR_PRO : 0; + if (val & RME32_WCR_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME32_WCR_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME32_WCR_EMP : 0; + return val; +} + +static void snd_rme32_convert_to_aes(snd_aes_iec958_t * aes, u32 val) +{ + aes->status[0] = ((val & RME32_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0); + if (val & RME32_WCR_PRO) + aes->status[0] |= (val & RME32_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME32_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme32_control_spdif_info(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme32_control_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + + snd_rme32_convert_to_aes(&ucontrol->value.iec958, + rme32->wcreg_spdif); + return 0; +} + +static int snd_rme32_control_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme32_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme32->lock, flags); + change = val != rme32->wcreg_spdif; + rme32->wcreg_spdif = val; + spin_unlock_irqrestore(&rme32->lock, flags); + return change; +} + +static int snd_rme32_control_spdif_stream_info(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme32_control_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * + ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + + snd_rme32_convert_to_aes(&ucontrol->value.iec958, + rme32->wcreg_spdif_stream); + return 0; +} + +static int snd_rme32_control_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * + ucontrol) +{ + rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme32_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme32->lock, flags); + change = val != rme32->wcreg_spdif_stream; + rme32->wcreg_spdif_stream = val; + rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP); + writel(rme32->wcreg |= val, rme32->iobase + RME32_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme32->lock, flags); + return change; +} + +static int snd_rme32_control_spdif_mask_info(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme32_control_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * + ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +static snd_kcontrol_new_t snd_rme32_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_rme32_control_spdif_info, + .get = snd_rme32_control_spdif_get, + .put = snd_rme32_control_spdif_put + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .info = snd_rme32_control_spdif_stream_info, + .get = snd_rme32_control_spdif_stream_get, + .put = snd_rme32_control_spdif_stream_put + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), + .info = snd_rme32_control_spdif_mask_info, + .get = snd_rme32_control_spdif_mask_get, + .private_value = IEC958_AES0_PROFESSIONAL | IEC958_AES0_CON_EMPHASIS + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), + .info = snd_rme32_control_spdif_mask_info, + .get = snd_rme32_control_spdif_mask_get, + .private_value = IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_EMPHASIS + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Input Connector", + .info = snd_rme32_info_inputtype_control, + .get = snd_rme32_get_inputtype_control, + .put = snd_rme32_put_inputtype_control + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Loopback Input", + .info = snd_rme32_info_loopback_control, + .get = snd_rme32_get_loopback_control, + .put = snd_rme32_put_loopback_control + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Sample Clock Source", + .info = snd_rme32_info_clockmode_control, + .get = snd_rme32_get_clockmode_control, + .put = snd_rme32_put_clockmode_control + } +}; + +static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32) +{ + int idx, err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < 7; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme32->spdif_ctl = kctl; + } + + return 0; +} + +/* + * Card initialisation + */ + +static void snd_rme32_card_free(snd_card_t * card) +{ + snd_rme32_free(card->private_data); +} + +static int __devinit +snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + static int dev = 0; + rme32_t *rme32; + snd_card_t *card; + int err; + + for (; dev < SNDRV_CARDS; dev++) { + if (!enable[dev]) { + dev++; + return -ENOENT; + } + break; + } + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(rme32_t))) == NULL) + return -ENOMEM; + card->private_free = snd_rme32_card_free; + rme32 = (rme32_t *) card->private_data; + rme32->card = card; + rme32->pci = pci; + if ((err = snd_rme32_create(rme32)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Digi32"); + switch (rme32->pci->device) { + case PCI_DEVICE_ID_DIGI32: + strcpy(card->shortname, "RME Digi32"); + break; + case PCI_DEVICE_ID_DIGI32_8: + strcpy(card->shortname, "RME Digi32/8"); + break; + case PCI_DEVICE_ID_DIGI32_PRO: + strcpy(card->shortname, "RME Digi32 PRO"); + break; + } + sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d", + card->shortname, rme32->rev, rme32->port, rme32->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme32_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "RME Digi32", + .id_table = snd_rme32_ids, + .probe = snd_rme32_probe, + .remove = __devexit_p(snd_rme32_remove), +}; + +static int __init alsa_card_rme32_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("No RME Digi32 cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_rme32_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_rme32_init) +module_exit(alsa_card_rme32_exit) + +#ifndef MODULE + +static int __init alsa_card_rme32_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void) (get_option(&str, &enable[nr_dev]) == 2 && + get_option(&str, &index[nr_dev]) == 2 && + get_id(&str, &id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme32=", alsa_card_rme32_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/rme96.c linux/sound/pci/rme96.c --- linux-2.4.21-rc1.orig/sound/pci/rme96.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme96.c 2003-01-31 08:20:32.000000000 -0700 @@ -0,0 +1,2594 @@ +/* + * ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio + * interfaces + * + * Copyright (c) 2000, 2001 Anders Torger + * + * Thanks to Henk Hesselink for the analog volume control + * code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +/* note, two last pcis should be equal, it is not a bug */ + +MODULE_AUTHOR("Anders Torger "); +MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, " + "Digi96/8 PAD"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Digi96}," + "{RME,Digi96/8}," + "{RME,Digi96/8 PRO}," + "{RME,Digi96/8 PST}," + "{RME,Digi96/8 PAD}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +/* + * Defines for RME Digi96 series, from internal RME reference documents + * dated 12.01.00 + */ + +#define RME96_SPDIF_NCHANNELS 2 + +/* Playback and capture buffer size */ +#define RME96_BUFFER_SIZE 0x10000 + +/* IO area size */ +#define RME96_IO_SIZE 0x60000 + +/* IO area offsets */ +#define RME96_IO_PLAY_BUFFER 0x0 +#define RME96_IO_REC_BUFFER 0x10000 +#define RME96_IO_CONTROL_REGISTER 0x20000 +#define RME96_IO_ADDITIONAL_REG 0x20004 +#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008 +#define RME96_IO_CONFIRM_REC_IRQ 0x2000C +#define RME96_IO_SET_PLAY_POS 0x40000 +#define RME96_IO_RESET_PLAY_POS 0x4FFFC +#define RME96_IO_SET_REC_POS 0x50000 +#define RME96_IO_RESET_REC_POS 0x5FFFC +#define RME96_IO_GET_PLAY_POS 0x20000 +#define RME96_IO_GET_REC_POS 0x30000 + +/* Write control register bits */ +#define RME96_WCR_START (1 << 0) +#define RME96_WCR_START_2 (1 << 1) +#define RME96_WCR_GAIN_0 (1 << 2) +#define RME96_WCR_GAIN_1 (1 << 3) +#define RME96_WCR_MODE24 (1 << 4) +#define RME96_WCR_MODE24_2 (1 << 5) +#define RME96_WCR_BM (1 << 6) +#define RME96_WCR_BM_2 (1 << 7) +#define RME96_WCR_ADAT (1 << 8) +#define RME96_WCR_FREQ_0 (1 << 9) +#define RME96_WCR_FREQ_1 (1 << 10) +#define RME96_WCR_DS (1 << 11) +#define RME96_WCR_PRO (1 << 12) +#define RME96_WCR_EMP (1 << 13) +#define RME96_WCR_SEL (1 << 14) +#define RME96_WCR_MASTER (1 << 15) +#define RME96_WCR_PD (1 << 16) +#define RME96_WCR_INP_0 (1 << 17) +#define RME96_WCR_INP_1 (1 << 18) +#define RME96_WCR_THRU_0 (1 << 19) +#define RME96_WCR_THRU_1 (1 << 20) +#define RME96_WCR_THRU_2 (1 << 21) +#define RME96_WCR_THRU_3 (1 << 22) +#define RME96_WCR_THRU_4 (1 << 23) +#define RME96_WCR_THRU_5 (1 << 24) +#define RME96_WCR_THRU_6 (1 << 25) +#define RME96_WCR_THRU_7 (1 << 26) +#define RME96_WCR_DOLBY (1 << 27) +#define RME96_WCR_MONITOR_0 (1 << 28) +#define RME96_WCR_MONITOR_1 (1 << 29) +#define RME96_WCR_ISEL (1 << 30) +#define RME96_WCR_IDIS (1 << 31) + +#define RME96_WCR_BITPOS_GAIN_0 2 +#define RME96_WCR_BITPOS_GAIN_1 3 +#define RME96_WCR_BITPOS_FREQ_0 9 +#define RME96_WCR_BITPOS_FREQ_1 10 +#define RME96_WCR_BITPOS_INP_0 17 +#define RME96_WCR_BITPOS_INP_1 18 +#define RME96_WCR_BITPOS_MONITOR_0 28 +#define RME96_WCR_BITPOS_MONITOR_1 29 + +/* Read control register bits */ +#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF +#define RME96_RCR_IRQ_2 (1 << 16) +#define RME96_RCR_T_OUT (1 << 17) +#define RME96_RCR_DEV_ID_0 (1 << 21) +#define RME96_RCR_DEV_ID_1 (1 << 22) +#define RME96_RCR_LOCK (1 << 23) +#define RME96_RCR_VERF (1 << 26) +#define RME96_RCR_F0 (1 << 27) +#define RME96_RCR_F1 (1 << 28) +#define RME96_RCR_F2 (1 << 29) +#define RME96_RCR_AUTOSYNC (1 << 30) +#define RME96_RCR_IRQ (1 << 31) + +#define RME96_RCR_BITPOS_F0 27 +#define RME96_RCR_BITPOS_F1 28 +#define RME96_RCR_BITPOS_F2 29 + +/* Additonal register bits */ +#define RME96_AR_WSEL (1 << 0) +#define RME96_AR_ANALOG (1 << 1) +#define RME96_AR_FREQPAD_0 (1 << 2) +#define RME96_AR_FREQPAD_1 (1 << 3) +#define RME96_AR_FREQPAD_2 (1 << 4) +#define RME96_AR_PD2 (1 << 5) +#define RME96_AR_DAC_EN (1 << 6) +#define RME96_AR_CLATCH (1 << 7) +#define RME96_AR_CCLK (1 << 8) +#define RME96_AR_CDATA (1 << 9) + +#define RME96_AR_BITPOS_F0 2 +#define RME96_AR_BITPOS_F1 3 +#define RME96_AR_BITPOS_F2 4 + +/* Monitor tracks */ +#define RME96_MONITOR_TRACKS_1_2 0 +#define RME96_MONITOR_TRACKS_3_4 1 +#define RME96_MONITOR_TRACKS_5_6 2 +#define RME96_MONITOR_TRACKS_7_8 3 + +/* Attenuation */ +#define RME96_ATTENUATION_0 0 +#define RME96_ATTENUATION_6 1 +#define RME96_ATTENUATION_12 2 +#define RME96_ATTENUATION_18 3 + +/* Input types */ +#define RME96_INPUT_OPTICAL 0 +#define RME96_INPUT_COAXIAL 1 +#define RME96_INPUT_INTERNAL 2 +#define RME96_INPUT_XLR 3 +#define RME96_INPUT_ANALOG 4 + +/* Clock modes */ +#define RME96_CLOCKMODE_SLAVE 0 +#define RME96_CLOCKMODE_MASTER 1 +#define RME96_CLOCKMODE_WORDCLOCK 2 + +/* Block sizes in bytes */ +#define RME96_SMALL_BLOCK_SIZE 2048 +#define RME96_LARGE_BLOCK_SIZE 8192 + +/* Volume control */ +#define RME96_AD1852_VOL_BITS 14 +#define RME96_AD1855_VOL_BITS 10 + +/* + * PCI vendor/device ids, could in the future be defined in , + * therefore #ifndef is used. + */ +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_DIGI96 +#define PCI_DEVICE_ID_DIGI96 0x3fc0 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8 +#define PCI_DEVICE_ID_DIGI96_8 0x3fc1 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8_PRO +#define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST +#define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3 +#endif + +typedef struct snd_rme96 { + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + u32 wcreg; /* cached write control register value */ + u32 wcreg_spdif; /* S/PDIF setup */ + u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */ + u32 rcreg; /* cached read control register value */ + u32 areg; /* cached additional register value */ + u16 vol[2]; /* cached volume of analog output */ + + u8 rev; /* card revision number */ + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + int playback_frlog; /* log2 of framesize */ + int capture_frlog; + + size_t playback_periodsize; /* in bytes, zero if not used */ + size_t capture_periodsize; /* in bytes, zero if not used */ + + snd_pcm_uframes_t playback_last_appl_ptr; + size_t playback_ptr; + size_t capture_ptr; + + snd_card_t *card; + snd_pcm_t *spdif_pcm; + snd_pcm_t *adat_pcm; + struct pci_dev *pci; + snd_kcontrol_t *spdif_ctl; +} rme96_t; + +static struct pci_device_id snd_rme96_ids[] __devinitdata = { + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_rme96_ids); + +#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START) +#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2) +#define RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) +#define RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \ + (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) +#define RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4) +#define RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \ + ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2)) +#define RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1) + +static int +snd_rme96_playback_prepare(snd_pcm_substream_t *substream); + +static int +snd_rme96_capture_prepare(snd_pcm_substream_t *substream); + +static int +snd_rme96_playback_trigger(snd_pcm_substream_t *substream, + int cmd); + +static int +snd_rme96_capture_trigger(snd_pcm_substream_t *substream, + int cmd); + +static snd_pcm_uframes_t +snd_rme96_playback_pointer(snd_pcm_substream_t *substream); + +static snd_pcm_uframes_t +snd_rme96_capture_pointer(snd_pcm_substream_t *substream); + +static void __devinit +snd_rme96_proc_init(rme96_t *rme96); + +static int +snd_rme96_create_switches(snd_card_t *card, + rme96_t *rme96); + +static int +snd_rme96_getinputtype(rme96_t *rme96); + +static inline unsigned int +snd_rme96_playback_ptr(rme96_t *rme96) +{ + return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS) + & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog; +} + +static inline unsigned int +snd_rme96_capture_ptr(rme96_t *rme96) +{ + return (readl(rme96->iobase + RME96_IO_GET_REC_POS) + & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog; +} + +static int +snd_rme96_ratecode(int rate) +{ + switch (rate) { + case 32000: return SNDRV_PCM_RATE_32000; + case 44100: return SNDRV_PCM_RATE_44100; + case 48000: return SNDRV_PCM_RATE_48000; + case 64000: return SNDRV_PCM_RATE_64000; + case 88200: return SNDRV_PCM_RATE_88200; + case 96000: return SNDRV_PCM_RATE_96000; + } + return 0; +} + +static int +snd_rme96_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->playback_frlog; + pos <<= rme96->playback_frlog; + memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + 0, count); + return 0; +} + +static int +snd_rme96_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->playback_frlog; + pos <<= rme96->playback_frlog; + copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, + count); + return 0; +} + +static int +snd_rme96_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->capture_frlog; + pos <<= rme96->capture_frlog; + copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, + count); + return 0; +} + +/* + * Digital output capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme96_playback_spdif_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = RME96_BUFFER_SIZE, + .period_bytes_min = RME96_SMALL_BLOCK_SIZE, + .period_bytes_max = RME96_LARGE_BLOCK_SIZE, + .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * Digital input capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme96_capture_spdif_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = RME96_BUFFER_SIZE, + .period_bytes_min = RME96_SMALL_BLOCK_SIZE, + .period_bytes_max = RME96_LARGE_BLOCK_SIZE, + .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * Digital output capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme96_playback_adat_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = RME96_BUFFER_SIZE, + .period_bytes_min = RME96_SMALL_BLOCK_SIZE, + .period_bytes_max = RME96_LARGE_BLOCK_SIZE, + .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * Digital input capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme96_capture_adat_info = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = RME96_BUFFER_SIZE, + .period_bytes_min = RME96_SMALL_BLOCK_SIZE, + .period_bytes_max = RME96_LARGE_BLOCK_SIZE, + .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface + * of the AD1852 or AD1852 D/A converter on the board. CDATA must be set up + * on the falling edge of CCLK and be stable on the rising edge. The rising + * edge of CLATCH after the last data bit clocks in the whole data word. + * A fast processor could probably drive the SPI interface faster than the + * DAC can handle (3MHz for the 1855, unknown for the 1852). The udelay(1) + * limits the data rate to 500KHz and only causes a delay of 33 microsecs. + * + * NOTE: increased delay from 1 to 10, since there where problems setting + * the volume. + */ +static void +snd_rme96_write_SPI(rme96_t *rme96, u16 val) +{ + int i; + + for (i = 0; i < 16; i++) { + if (val & 0x8000) { + rme96->areg |= RME96_AR_CDATA; + } else { + rme96->areg &= ~RME96_AR_CDATA; + } + rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + rme96->areg |= RME96_AR_CCLK; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + val <<= 1; + } + rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA); + rme96->areg |= RME96_AR_CLATCH; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + rme96->areg &= ~RME96_AR_CLATCH; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); +} + +static void +snd_rme96_apply_dac_volume(rme96_t *rme96) +{ + if (RME96_DAC_IS_1852(rme96)) { + snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0); + snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2); + } else if (RME96_DAC_IS_1855(rme96)) { + snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000); + snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400); + } +} + +static void +snd_rme96_reset_dac(rme96_t *rme96) +{ + writel(rme96->wcreg | RME96_WCR_PD, + rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static int +snd_rme96_getmontracks(rme96_t *rme96) +{ + return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1); +} + +static int +snd_rme96_setmontracks(rme96_t *rme96, + int montracks) +{ + if (montracks & 1) { + rme96->wcreg |= RME96_WCR_MONITOR_0; + } else { + rme96->wcreg &= ~RME96_WCR_MONITOR_0; + } + if (montracks & 2) { + rme96->wcreg |= RME96_WCR_MONITOR_1; + } else { + rme96->wcreg &= ~RME96_WCR_MONITOR_1; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_getattenuation(rme96_t *rme96) +{ + return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1); +} + +static int +snd_rme96_setattenuation(rme96_t *rme96, + int attenuation) +{ + switch (attenuation) { + case 0: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) & + ~RME96_WCR_GAIN_1; + break; + case 1: + rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) & + ~RME96_WCR_GAIN_1; + break; + case 2: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) | + RME96_WCR_GAIN_1; + break; + case 3: + rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) | + RME96_WCR_GAIN_1; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_capture_getrate(rme96_t *rme96, + int *is_adat) +{ + int n, rate; + + *is_adat = 0; + if (rme96->areg & RME96_AR_ANALOG) { + /* Analog input, overrides S/PDIF setting */ + n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) + + (((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1); + switch (n) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate; + } + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_LOCK) { + /* ADAT rate */ + *is_adat = 1; + if (rme96->rcreg & RME96_RCR_T_OUT) { + return 48000; + } + return 44100; + } + + if (rme96->rcreg & RME96_RCR_VERF) { + return -1; + } + + /* S/PDIF rate */ + n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) + + (((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) + + (((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2); + + switch (n) { + case 0: + if (rme96->rcreg & RME96_RCR_T_OUT) { + return 64000; + } + return -1; + case 3: return 96000; + case 4: return 88200; + case 5: return 48000; + case 6: return 44100; + case 7: return 32000; + default: + break; + } + return -1; +} + +static int +snd_rme96_playback_getrate(rme96_t *rme96) +{ + int rate, dummy; + + if (!(rme96->wcreg & RME96_WCR_MASTER) && + snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + return rate; + } + rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1); + switch (rate) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate; +} + +static int +snd_rme96_playback_setrate(rme96_t *rme96, + int rate) +{ + int ds; + + ds = rme96->wcreg & RME96_WCR_DS; + switch (rate) { + case 32000: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & + ~RME96_WCR_FREQ_1; + break; + case 44100: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & + ~RME96_WCR_FREQ_0; + break; + case 48000: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | + RME96_WCR_FREQ_1; + break; + case 64000: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & + ~RME96_WCR_FREQ_1; + break; + case 88200: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & + ~RME96_WCR_FREQ_0; + break; + case 96000: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | + RME96_WCR_FREQ_1; + break; + default: + return -EINVAL; + } + if ((!ds && rme96->wcreg & RME96_WCR_DS) || + (ds && !(rme96->wcreg & RME96_WCR_DS))) + { + /* change to/from double-speed: reset the DAC (if available) */ + snd_rme96_reset_dac(rme96); + } else { + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + } + return 0; +} + +static int +snd_rme96_capture_analog_setrate(rme96_t *rme96, + int rate) +{ + switch (rate) { + case 32000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & + ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 44100: + rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 48000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 64000: + if (rme96->rev < 4) { + return -EINVAL; + } + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & + ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + case 88200: + if (rme96->rev < 4) { + return -EINVAL; + } + rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + case 96000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + default: + return -EINVAL; + } + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + return 0; +} + +static int +snd_rme96_setclockmode(rme96_t *rme96, + int mode) +{ + switch (mode) { + case RME96_CLOCKMODE_SLAVE: + rme96->wcreg &= ~RME96_WCR_MASTER; + rme96->areg &= ~RME96_AR_WSEL; + break; + case RME96_CLOCKMODE_MASTER: + rme96->wcreg |= RME96_WCR_MASTER; + rme96->areg &= ~RME96_AR_WSEL; + break; + case RME96_CLOCKMODE_WORDCLOCK: + /* Word clock is a master mode */ + rme96->wcreg |= RME96_WCR_MASTER; + rme96->areg |= RME96_AR_WSEL; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + return 0; +} + +static int +snd_rme96_getclockmode(rme96_t *rme96) +{ + if (rme96->areg & RME96_AR_WSEL) { + return RME96_CLOCKMODE_WORDCLOCK; + } + return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER : + RME96_CLOCKMODE_SLAVE; +} + +static int +snd_rme96_setinputtype(rme96_t *rme96, + int type) +{ + int n; + + switch (type) { + case RME96_INPUT_OPTICAL: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) & + ~RME96_WCR_INP_1; + break; + case RME96_INPUT_COAXIAL: + rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) & + ~RME96_WCR_INP_1; + break; + case RME96_INPUT_INTERNAL: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) | + RME96_WCR_INP_1; + break; + case RME96_INPUT_XLR: + if ((rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && + rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO) || + (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && + rme96->rev > 4)) + { + /* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */ + return -EINVAL; + } + rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) | + RME96_WCR_INP_1; + break; + case RME96_INPUT_ANALOG: + if (!RME96_HAS_ANALOG_IN(rme96)) { + return -EINVAL; + } + rme96->areg |= RME96_AR_ANALOG; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + if (rme96->rev < 4) { + /* + * Revision less than 004 does not support 64 and + * 88.2 kHz + */ + if (snd_rme96_capture_getrate(rme96, &n) == 88200) { + snd_rme96_capture_analog_setrate(rme96, 44100); + } + if (snd_rme96_capture_getrate(rme96, &n) == 64000) { + snd_rme96_capture_analog_setrate(rme96, 32000); + } + } + return 0; + default: + return -EINVAL; + } + if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) { + rme96->areg &= ~RME96_AR_ANALOG; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_getinputtype(rme96_t *rme96) +{ + if (rme96->areg & RME96_AR_ANALOG) { + return RME96_INPUT_ANALOG; + } + return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1); +} + +static void +snd_rme96_setframelog(rme96_t *rme96, + int n_channels, + int is_playback) +{ + int frlog; + + if (n_channels == 2) { + frlog = 1; + } else { + /* assume 8 channels */ + frlog = 3; + } + if (is_playback) { + frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1; + rme96->playback_frlog = frlog; + } else { + frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1; + rme96->capture_frlog = frlog; + } +} + +static int +snd_rme96_playback_setformat(rme96_t *rme96, + int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme96->wcreg &= ~RME96_WCR_MODE24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme96->wcreg |= RME96_WCR_MODE24; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_capture_setformat(rme96_t *rme96, + int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme96->wcreg &= ~RME96_WCR_MODE24_2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme96->wcreg |= RME96_WCR_MODE24_2; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static void +snd_rme96_set_period_properties(rme96_t *rme96, + size_t period_bytes) +{ + switch (period_bytes) { + case RME96_LARGE_BLOCK_SIZE: + rme96->wcreg &= ~RME96_WCR_ISEL; + break; + case RME96_SMALL_BLOCK_SIZE: + rme96->wcreg |= RME96_WCR_ISEL; + break; + default: + snd_BUG(); + break; + } + rme96->wcreg &= ~RME96_WCR_IDIS; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static int +snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int err, rate, dummy; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme96->lock, flags); + if (!(rme96->wcreg & RME96_WCR_MASTER) && + snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + if ((int)params_rate(params) != rate) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + snd_rme96_setframelog(rme96, params_channels(params), 1); + if (rme96->capture_periodsize != 0) { + if (params_period_size(params) << rme96->playback_frlog != + rme96->capture_periodsize) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + } + rme96->playback_periodsize = + params_period_size(params) << rme96->playback_frlog; + snd_rme96_set_period_properties(rme96, rme96->playback_periodsize); + /* S/PDIF setup */ + if ((rme96->wcreg & RME96_WCR_ADAT) == 0) { + rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); + writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER); + } + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_playback_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int +snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err, isadat, rate; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme96->lock, flags); + if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { + if ((err = snd_rme96_capture_analog_setrate(rme96, + params_rate(params))) < 0) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if ((int)params_rate(params) != rate) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + if ((isadat && runtime->hw.channels_min == 2) || + (!isadat && runtime->hw.channels_min == 8)) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + } + snd_rme96_setframelog(rme96, params_channels(params), 0); + if (rme96->playback_periodsize != 0) { + if (params_period_size(params) << rme96->capture_frlog != + rme96->playback_periodsize) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + } + rme96->capture_periodsize = + params_period_size(params) << rme96->capture_frlog; + snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_capture_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static void +snd_rme96_playback_start(rme96_t *rme96, + int from_pause) +{ + if (!from_pause) { + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + } + + rme96->wcreg |= RME96_WCR_START; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_capture_start(rme96_t *rme96, + int from_pause) +{ + if (!from_pause) { + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + rme96->capture_ptr = 0; + } + + rme96->wcreg |= RME96_WCR_START_2; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_playback_stop(rme96_t *rme96) +{ + /* + * Check if there is an unconfirmed IRQ, if so confirm it, or else + * the hardware will not stop generating interrupts + */ + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ) { + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + rme96->wcreg &= ~RME96_WCR_START; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_capture_stop(rme96_t *rme96) +{ + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ_2) { + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } + rme96->wcreg &= ~RME96_WCR_START_2; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + rme96_t *rme96 = (rme96_t *)dev_id; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + /* fastpath out, to ease interrupt sharing */ + if (!((rme96->rcreg & RME96_RCR_IRQ) || + (rme96->rcreg & RME96_RCR_IRQ_2))) + { + return; + } + + if (rme96->rcreg & RME96_RCR_IRQ) { + /* playback */ + snd_pcm_period_elapsed(rme96->playback_substream); + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + if (rme96->rcreg & RME96_RCR_IRQ_2) { + /* capture */ + snd_pcm_period_elapsed(rme96->capture_substream); + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } +} + +static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; + +#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { + .count = PERIOD_BYTES, + .list = period_bytes, + .mask = 0 +}; + +static int +snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int rate, dummy; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->playback_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + rme96->wcreg &= ~RME96_WCR_ADAT; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + rme96->playback_substream = substream; + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_playback_spdif_info; + if (!(rme96->wcreg & RME96_WCR_MASTER) && + snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + + rme96->wcreg_spdif_stream = rme96->wcreg_spdif; + rme96->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); + return 0; +} + +static int +snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat, rate; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme96_capture_spdif_info; + if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && + (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) + { + if (isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->capture_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + rme96->capture_substream = substream; + rme96->capture_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + + return 0; +} + +static int +snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int rate, dummy; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->playback_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + rme96->wcreg |= RME96_WCR_ADAT; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + rme96->playback_substream = substream; + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_playback_adat_info; + if (!(rme96->wcreg & RME96_WCR_MASTER) && + snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat, rate; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme96_capture_adat_info; + if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG) { + /* makes no sense to use analog input. Note that analog + expension cards AEB4/8-I are RME96_INPUT_INTERNAL */ + return -EIO; + } + if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (!isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->capture_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + rme96->capture_substream = substream; + rme96->capture_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme96_playback_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int spdif = 0; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + rme96->playback_substream = NULL; + rme96->playback_periodsize = 0; + spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; + spin_unlock_irqrestore(&rme96->lock, flags); + if (spdif) { + rme96->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); + } + return 0; +} + +static int +snd_rme96_capture_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + rme96->capture_substream = NULL; + rme96->capture_periodsize = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_playback_prepare(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_capture_prepare(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME96_ISPLAYING(rme96)) { + if (substream != rme96->playback_substream) { + return -EBUSY; + } + snd_rme96_playback_start(rme96, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME96_ISPLAYING(rme96)) { + if (substream != rme96->playback_substream) { + return -EBUSY; + } + snd_rme96_playback_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME96_ISPLAYING(rme96)) { + snd_rme96_playback_start(rme96, 1); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +snd_rme96_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME96_ISRECORDING(rme96)) { + if (substream != rme96->capture_substream) { + return -EBUSY; + } + snd_rme96_capture_start(rme96, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME96_ISRECORDING(rme96)) { + if (substream != rme96->capture_substream) { + return -EBUSY; + } + snd_rme96_capture_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME96_ISRECORDING(rme96)) { + snd_rme96_capture_start(rme96, 1); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +snd_rme96_playback_pointer(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff; + size_t bytes; + + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + diff = runtime->control->appl_ptr - + rme96->playback_last_appl_ptr; + rme96->playback_last_appl_ptr = runtime->control->appl_ptr; + if (diff != 0 && + diff < -(snd_pcm_sframes_t)(runtime->boundary >> 1)) + { + diff += runtime->boundary; + } + bytes = diff << rme96->playback_frlog; + + if (bytes > RME96_BUFFER_SIZE - rme96->playback_ptr) { + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + + rme96->playback_ptr, + runtime->dma_area + rme96->playback_ptr, + RME96_BUFFER_SIZE - rme96->playback_ptr); + bytes -= RME96_BUFFER_SIZE - rme96->playback_ptr; + if (bytes > RME96_BUFFER_SIZE) { + bytes = RME96_BUFFER_SIZE; + } + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER, + runtime->dma_area, + bytes); + rme96->playback_ptr = bytes; + } else if (bytes != 0) { + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + + rme96->playback_ptr, + runtime->dma_area + rme96->playback_ptr, + bytes); + rme96->playback_ptr += bytes; + } + } + return snd_rme96_playback_ptr(rme96); +} + +static snd_pcm_uframes_t +snd_rme96_capture_pointer(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frameptr; + size_t ptr; + + frameptr = snd_rme96_capture_ptr(rme96); + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + ptr = frameptr << rme96->capture_frlog; + if (ptr > rme96->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme96->capture_ptr, + rme96->iobase + RME96_IO_REC_BUFFER + + rme96->capture_ptr, + ptr - rme96->capture_ptr); + rme96->capture_ptr += ptr - rme96->capture_ptr; + } else if (ptr < rme96->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme96->capture_ptr, + rme96->iobase + RME96_IO_REC_BUFFER + + rme96->capture_ptr, + RME96_BUFFER_SIZE - rme96->capture_ptr); + memcpy_fromio(runtime->dma_area, + rme96->iobase + RME96_IO_REC_BUFFER, + ptr); + rme96->capture_ptr = ptr; + } + } + return frameptr; +} + +static snd_pcm_ops_t snd_rme96_playback_spdif_ops = { + .open = snd_rme96_playback_spdif_open, + .close = snd_rme96_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme96_playback_hw_params, + .hw_free = snd_rme96_playback_hw_free, + .prepare = snd_rme96_playback_prepare, + .trigger = snd_rme96_playback_trigger, + .pointer = snd_rme96_playback_pointer, + .copy = snd_rme96_playback_copy, + .silence = snd_rme96_playback_silence, +}; + +static snd_pcm_ops_t snd_rme96_capture_spdif_ops = { + .open = snd_rme96_capture_spdif_open, + .close = snd_rme96_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme96_capture_hw_params, + .hw_free = snd_rme96_capture_hw_free, + .prepare = snd_rme96_capture_prepare, + .trigger = snd_rme96_capture_trigger, + .pointer = snd_rme96_capture_pointer, + .copy = snd_rme96_capture_copy, +}; + +static snd_pcm_ops_t snd_rme96_playback_adat_ops = { + .open = snd_rme96_playback_adat_open, + .close = snd_rme96_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme96_playback_hw_params, + .hw_free = snd_rme96_playback_hw_free, + .prepare = snd_rme96_playback_prepare, + .trigger = snd_rme96_playback_trigger, + .pointer = snd_rme96_playback_pointer, + .copy = snd_rme96_playback_copy, + .silence = snd_rme96_playback_silence, +}; + +static snd_pcm_ops_t snd_rme96_capture_adat_ops = { + .open = snd_rme96_capture_adat_open, + .close = snd_rme96_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme96_capture_hw_params, + .hw_free = snd_rme96_capture_hw_free, + .prepare = snd_rme96_capture_prepare, + .trigger = snd_rme96_capture_trigger, + .pointer = snd_rme96_capture_pointer, + .copy = snd_rme96_capture_copy, +}; + +static void +snd_rme96_free(void *private_data) +{ + rme96_t *rme96 = (rme96_t *)private_data; + + if (rme96 == NULL) { + return; + } + if (rme96->irq >= 0) { + snd_rme96_playback_stop(rme96); + snd_rme96_capture_stop(rme96); + rme96->areg &= ~RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + free_irq(rme96->irq, (void *)rme96); + rme96->irq = -1; + } + if (rme96->iobase) { + iounmap((void *)rme96->iobase); + rme96->iobase = 0; + } + if (rme96->res_port != NULL) { + release_resource(rme96->res_port); + kfree_nocheck(rme96->res_port); + rme96->res_port = NULL; + } +} + +static void +snd_rme96_free_spdif_pcm(snd_pcm_t *pcm) +{ + rme96_t *rme96 = (rme96_t *) pcm->private_data; + rme96->spdif_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void +snd_rme96_free_adat_pcm(snd_pcm_t *pcm) +{ + rme96_t *rme96 = (rme96_t *) pcm->private_data; + rme96->adat_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit +snd_rme96_create(rme96_t *rme96) +{ + struct pci_dev *pci = rme96->pci; + int err; + + rme96->irq = -1; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + rme96->port = pci_resource_start(rme96->pci, 0); + + if ((rme96->res_port = request_mem_region(rme96->port, RME96_IO_SIZE, "RME96")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme96->irq = pci->irq; + + spin_lock_init(&rme96->lock); + if ((rme96->iobase = (unsigned long) ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { + snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -ENOMEM; + } + + /* read the card's revision number */ + pci_read_config_byte(pci, 8, &rme96->rev); + + /* set up ALSA pcm device for S/PDIF */ + if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0, + 1, 1, &rme96->spdif_pcm)) < 0) + { + return err; + } + rme96->spdif_pcm->private_data = rme96; + rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; + strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); + snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); + snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); + + rme96->spdif_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + + /* set up ALSA pcm device for ADAT */ + if (pci->device == PCI_DEVICE_ID_DIGI96) { + /* ADAT is not available on the base model */ + rme96->adat_pcm = NULL; + } else { + if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1, + 1, 1, &rme96->adat_pcm)) < 0) + { + return err; + } + rme96->adat_pcm->private_data = rme96; + rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; + strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); + snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); + snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); + + rme96->adat_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + } + + rme96->playback_periodsize = 0; + rme96->capture_periodsize = 0; + + /* make sure playback/capture is stopped, if by some reason active */ + snd_rme96_playback_stop(rme96); + snd_rme96_capture_stop(rme96); + + /* set default values in registers */ + rme96->wcreg = + RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */ + RME96_WCR_SEL | /* normal playback */ + RME96_WCR_MASTER | /* set to master clock mode */ + RME96_WCR_INP_0; /* set coaxial input */ + + rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */ + + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset the ADC */ + writel(rme96->areg | RME96_AR_PD2, + rme96->iobase + RME96_IO_ADDITIONAL_REG); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset and enable the DAC (order is important). */ + snd_rme96_reset_dac(rme96); + rme96->areg |= RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset playback and record buffer pointers */ + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + + /* reset volume */ + rme96->vol[0] = rme96->vol[1] = 0; + if (RME96_HAS_ANALOG_OUT(rme96)) { + snd_rme96_apply_dac_volume(rme96); + } + + /* init switch interface */ + if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) { + return err; + } + + /* init proc interface */ + snd_rme96_proc_init(rme96); + + return 0; +} + +/* + * proc interface + */ + +static void +snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + int n; + rme96_t *rme96 = (rme96_t *)entry->private_data; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + + snd_iprintf(buffer, rme96->card->longname); + snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1); + + snd_iprintf(buffer, "\nGeneral settings\n"); + if (rme96->wcreg & RME96_WCR_IDIS) { + snd_iprintf(buffer, " period size: N/A (interrupts " + "disabled)\n"); + } else if (rme96->wcreg & RME96_WCR_ISEL) { + snd_iprintf(buffer, " period size: 2048 bytes\n"); + } else { + snd_iprintf(buffer, " period size: 8192 bytes\n"); + } + snd_iprintf(buffer, "\nInput settings\n"); + switch (snd_rme96_getinputtype(rme96)) { + case RME96_INPUT_OPTICAL: + snd_iprintf(buffer, " input: optical"); + break; + case RME96_INPUT_COAXIAL: + snd_iprintf(buffer, " input: coaxial"); + break; + case RME96_INPUT_INTERNAL: + snd_iprintf(buffer, " input: internal"); + break; + case RME96_INPUT_XLR: + snd_iprintf(buffer, " input: XLR"); + break; + case RME96_INPUT_ANALOG: + snd_iprintf(buffer, " input: analog"); + break; + } + if (snd_rme96_capture_getrate(rme96, &n) < 0) { + snd_iprintf(buffer, "\n sample rate: no valid signal\n"); + } else { + if (n) { + snd_iprintf(buffer, " (8 channels)\n"); + } else { + snd_iprintf(buffer, " (2 channels)\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme96_capture_getrate(rme96, &n)); + } + if (rme96->wcreg & RME96_WCR_MODE24_2) { + snd_iprintf(buffer, " sample format: 24 bit\n"); + } else { + snd_iprintf(buffer, " sample format: 16 bit\n"); + } + + snd_iprintf(buffer, "\nOutput settings\n"); + if (rme96->wcreg & RME96_WCR_SEL) { + snd_iprintf(buffer, " output signal: normal playback\n"); + } else { + snd_iprintf(buffer, " output signal: same as input\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme96_playback_getrate(rme96)); + if (rme96->wcreg & RME96_WCR_MODE24) { + snd_iprintf(buffer, " sample format: 24 bit\n"); + } else { + snd_iprintf(buffer, " sample format: 16 bit\n"); + } + if (rme96->areg & RME96_AR_WSEL) { + snd_iprintf(buffer, " clock mode: word clock\n"); + } else if (rme96->wcreg & RME96_WCR_MASTER) { + snd_iprintf(buffer, " clock mode: master\n"); + } else if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { + snd_iprintf(buffer, " clock mode: slave (master anyway due to analog input setting)\n"); + } else if (snd_rme96_capture_getrate(rme96, &n) < 0) { + snd_iprintf(buffer, " clock mode: slave (master anyway due to no valid signal)\n"); + } else { + snd_iprintf(buffer, " clock mode: slave\n"); + } + if (rme96->wcreg & RME96_WCR_PRO) { + snd_iprintf(buffer, " format: AES/EBU (professional)\n"); + } else { + snd_iprintf(buffer, " format: IEC958 (consumer)\n"); + } + if (rme96->wcreg & RME96_WCR_EMP) { + snd_iprintf(buffer, " emphasis: on\n"); + } else { + snd_iprintf(buffer, " emphasis: off\n"); + } + if (rme96->wcreg & RME96_WCR_DOLBY) { + snd_iprintf(buffer, " non-audio (dolby): on\n"); + } else { + snd_iprintf(buffer, " non-audio (dolby): off\n"); + } + if (RME96_HAS_ANALOG_IN(rme96)) { + snd_iprintf(buffer, "\nAnalog output settings\n"); + switch (snd_rme96_getmontracks(rme96)) { + case RME96_MONITOR_TRACKS_1_2: + snd_iprintf(buffer, " monitored ADAT tracks: 1+2\n"); + break; + case RME96_MONITOR_TRACKS_3_4: + snd_iprintf(buffer, " monitored ADAT tracks: 3+4\n"); + break; + case RME96_MONITOR_TRACKS_5_6: + snd_iprintf(buffer, " monitored ADAT tracks: 5+6\n"); + break; + case RME96_MONITOR_TRACKS_7_8: + snd_iprintf(buffer, " monitored ADAT tracks: 7+8\n"); + break; + } + switch (snd_rme96_getattenuation(rme96)) { + case RME96_ATTENUATION_0: + snd_iprintf(buffer, " attenuation: 0 dB\n"); + break; + case RME96_ATTENUATION_6: + snd_iprintf(buffer, " attenuation: -6 dB\n"); + break; + case RME96_ATTENUATION_12: + snd_iprintf(buffer, " attenuation: -12 dB\n"); + break; + case RME96_ATTENUATION_18: + snd_iprintf(buffer, " attenuation: -18 dB\n"); + break; + } + snd_iprintf(buffer, " volume left: %u\n", rme96->vol[0]); + snd_iprintf(buffer, " volume right: %u\n", rme96->vol[1]); + } +} + +static void __devinit +snd_rme96_proc_init(rme96_t *rme96) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(rme96->card, "rme96", &entry)) + snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read); +} + +/* + * control interface + */ + +static int +snd_rme96_info_loopback_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} +static int +snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1; + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL; + spin_lock_irqsave(&rme96->lock, flags); + val = (rme96->wcreg & ~RME96_WCR_SEL) | val; + change = val != rme96->wcreg; + writel(rme96->wcreg = val, rme96->iobase + RME96_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + uinfo->value.enumerated.items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + uinfo->value.enumerated.items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + /* PST */ + uinfo->value.enumerated.items = 4; + texts[3] = _texts[4]; /* Analog instead of XLR */ + } else { + /* PAD */ + uinfo->value.enumerated.items = 5; + } + break; + default: + snd_BUG(); + break; + } + if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) { + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int items = 3; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96); + + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + /* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */ + if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) { + ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR; + } + items = 4; + } else { + items = 5; + } + break; + default: + snd_BUG(); + break; + } + if (ucontrol->value.enumerated.item[0] >= items) { + ucontrol->value.enumerated.item[0] = items - 1; + } + + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change, items = 3; + + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + items = 4; + } else { + items = 5; + } + break; + default: + snd_BUG(); + break; + } + val = ucontrol->value.enumerated.item[0] % items; + + /* special case for PST */ + if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) { + if (val == RME96_INPUT_XLR) { + val = RME96_INPUT_ANALOG; + } + } + + spin_lock_irqsave(&rme96->lock, flags); + change = (int)val != snd_rme96_getinputtype(rme96); + snd_rme96_setinputtype(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { "Slave", "Master", "Wordclock" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) { + uinfo->value.enumerated.item = 2; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme96->lock, flags); + change = (int)val != snd_rme96_getclockmode(rme96); + snd_rme96_setclockmode(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 4; + spin_lock_irqsave(&rme96->lock, flags); + + change = (int)val != snd_rme96_getattenuation(rme96); + snd_rme96_setattenuation(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 4; + spin_lock_irqsave(&rme96->lock, flags); + change = (int)val != snd_rme96_getmontracks(rme96); + snd_rme96_setmontracks(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0; + if (val & RME96_WCR_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0; + return val; +} + +static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0); + if (val & RME96_WCR_PRO) + aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif); + return 0; +} + +static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme96->lock, flags); + change = val != rme96->wcreg_spdif; + rme96->wcreg_spdif = val; + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream); + return 0; +} + +static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme96->lock, flags); + change = val != rme96->wcreg_spdif_stream; + rme96->wcreg_spdif_stream = val; + rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); + writel(rme96->wcreg |= val, rme96->iobase + RME96_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +static int +snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96); + return 0; +} + +static int +snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + u->value.integer.value[0] = rme96->vol[0]; + u->value.integer.value[1] = rme96->vol[1]; + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + + if (!RME96_HAS_ANALOG_OUT(rme96)) { + return -EINVAL; + } + spin_lock_irqsave(&rme96->lock, flags); + if (u->value.integer.value[0] != rme96->vol[0]) { + rme96->vol[0] = u->value.integer.value[0]; + change = 1; + } + if (u->value.integer.value[1] != rme96->vol[1]) { + rme96->vol[1] = u->value.integer.value[1]; + change = 1; + } + if (change) { + snd_rme96_apply_dac_volume(rme96); + } + spin_unlock_irqrestore(&rme96->lock, flags); + + return change; +} + +static snd_kcontrol_new_t snd_rme96_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_rme96_control_spdif_info, + .get = snd_rme96_control_spdif_get, + .put = snd_rme96_control_spdif_put +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_rme96_control_spdif_stream_info, + .get = snd_rme96_control_spdif_stream_get, + .put = snd_rme96_control_spdif_stream_put +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_rme96_control_spdif_mask_info, + .get = snd_rme96_control_spdif_mask_get, + .private_value = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_rme96_control_spdif_mask_info, + .get = snd_rme96_control_spdif_mask_get, + .private_value = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Input Connector", + .info = snd_rme96_info_inputtype_control, + .get = snd_rme96_get_inputtype_control, + .put = snd_rme96_put_inputtype_control +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Loopback Input", + .info = snd_rme96_info_loopback_control, + .get = snd_rme96_get_loopback_control, + .put = snd_rme96_put_loopback_control +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Clock Mode", + .info = snd_rme96_info_clockmode_control, + .get = snd_rme96_get_clockmode_control, + .put = snd_rme96_put_clockmode_control +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Monitor Tracks", + .info = snd_rme96_info_montracks_control, + .get = snd_rme96_get_montracks_control, + .put = snd_rme96_put_montracks_control +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Attenuation", + .info = snd_rme96_info_attenuation_control, + .get = snd_rme96_get_attenuation_control, + .put = snd_rme96_put_attenuation_control +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Playback Volume", + .info = snd_rme96_dac_volume_info, + .get = snd_rme96_dac_volume_get, + .put = snd_rme96_dac_volume_put +} +}; + +static int +snd_rme96_create_switches(snd_card_t *card, + rme96_t *rme96) +{ + int idx, err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < 7; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme96->spdif_ctl = kctl; + } + + if (RME96_HAS_ANALOG_OUT(rme96)) { + for (idx = 7; idx < 10; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) + return err; + } + + return 0; +} + +/* + * Card initialisation + */ + +static void snd_rme96_card_free(snd_card_t *card) +{ + snd_rme96_free(card->private_data); +} + +static int __devinit +snd_rme96_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + rme96_t *rme96; + snd_card_t *card; + int err; + u8 val; + + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if (!enable[dev]) { + dev++; + return -ENOENT; + } + if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(rme96_t))) == NULL) + return -ENOMEM; + card->private_free = snd_rme96_card_free; + rme96 = (rme96_t *)card->private_data; + rme96->card = card; + rme96->pci = pci; + if ((err = snd_rme96_create(rme96)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Digi96"); + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + strcpy(card->shortname, "RME Digi96"); + break; + case PCI_DEVICE_ID_DIGI96_8: + strcpy(card->shortname, "RME Digi96/8"); + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + strcpy(card->shortname, "RME Digi96/8 PRO"); + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + pci_read_config_byte(rme96->pci, 8, &val); + if (val < 5) { + strcpy(card->shortname, "RME Digi96/8 PAD"); + } else { + strcpy(card->shortname, "RME Digi96/8 PST"); + } + break; + } + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, + rme96->port, rme96->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme96_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "RME Digi96", + .id_table = snd_rme96_ids, + .probe = snd_rme96_probe, + .remove = __devexit_p(snd_rme96_remove), +}; + +static int __init alsa_card_rme96_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "No RME Digi96 cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_rme96_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_rme96_init) +module_exit(alsa_card_rme96_exit) + +#ifndef MODULE + +/* format is: snd-rme96=enable,index,id */ + +static int __init alsa_card_rme96_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme96=", alsa_card_rme96_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/rme9652/Makefile linux/sound/pci/rme9652/Makefile --- linux-2.4.21-rc1.orig/sound/pci/rme9652/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme9652/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _rme9652.o + +list-multi := snd-rme9652-mem.o snd-rme9652.o + +export-objs := hammerfall_mem.o + +snd-hammerfall-mem-objs := hammerfall_mem.o +snd-rme9652-objs := rme9652.o +snd-hdsp-objs := hdsp.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_RME9652) += snd-rme9652.o snd-hammerfall-mem.o +obj-$(CONFIG_SND_HDSP) += snd-hdsp.o snd-hammerfall-mem.o + +include $(TOPDIR)/Rules.make + +snd-hammerfall-mem.o: $(snd-hammerfall-mem-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-hammerfall-mem-objs) + +snd-rme9652.o: $(snd-rme9652-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-objs) + +snd-hdsp.o: $(snd-hdsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-hdsp-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/rme9652/digiface_firmware.dat linux/sound/pci/rme9652/digiface_firmware.dat --- linux-2.4.21-rc1.orig/sound/pci/rme9652/digiface_firmware.dat 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme9652/digiface_firmware.dat 2002-07-14 15:30:57.000000000 -0600 @@ -0,0 +1,4072 @@ +/* stored in little-endian */ +static u32 digiface_firmware[24413] __devinitdata = { +0xffffffff, 0x66aa9955, 0x8001000c, 0xe0000000, 0x8006800c, 0xb0000000, +0x8004800c, 0xb4fc0100, 0x8003000c, 0x00000000, 0x8001000c, 0x90000000, +0x8004000c, 0x00000000, 0x8001000c, 0x80000000, 0x0002000c, 0x581a000a, +0x00044800, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800000, +0x00000120, 0x00000000, 0x00044800, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x40800000, 0x00000120, 0x00000000, 0x00024001, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000100, 0x00000000, +0x00004000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000100, 0x00000000, 0x00004008, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000900, 0x00000000, 0x0000c008, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000320, 0x00000000, +0x00004801, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000120, 0x00000000, 0x438048f6, 0x78011e00, 0x8005e001, 0x015c001f, +0x11e000f8, 0x9e004f80, 0xe005f801, 0x002f8019, 0x057801be, 0x778005e0, +0xd800fe00, 0xbc0f6002, 0x00000123, 0x00000000, 0x040fb003, 0x03cd803f, +0x3cc40fb2, 0xfc20ff00, 0xc00ff083, 0x20ff083f, 0x0f3003ec, 0xff0837ca, +0xf003fc00, 0x113e820f, 0x0bcc40fb, 0x0fc0c311, 0x00000e00, 0x00000000, +0x00870001, 0x0844a03f, 0x044001f1, 0x74000e08, 0x4201f000, 0x001d0007, +0x01500074, 0x1d100740, 0xd0007400, 0x01854143, 0x0bc40051, 0x0440c1b0, +0x00000c20, 0x00000000, 0x0043a011, 0x0a24000d, 0x304808d0, 0x1420cd20, +0x400cd003, 0x00c10013, 0x0c158314, 0xc5001340, 0xd0033440, 0x04915004, +0x1b060049, 0x44404019, 0x00000e80, 0x00000000, 0x09058803, 0x81643155, +0x04410550, 0x76201d00, 0x4001d900, 0x001d9827, 0x01590034, 0x1c002700, +0xd8007600, 0x00275009, 0x030608c1, 0x0c402414, 0x00000620, 0x00000000, +0x0037a802, 0x0166011d, 0x54d00d90, 0x7c001f02, 0xc201f080, 0x33df0027, +0x0110006c, 0x5f0277c2, 0xf0807428, 0x0026c209, 0x034c52db, 0x02d01910, +0x00000e20, 0x00000000, 0x003d8007, 0x02d4202f, 0x6f8803f2, 0xfc00f720, +0xc00f7003, 0x083f101f, 0x0fb003f8, 0xbf100fc0, 0xf003fc01, 0x0818c006, +0x83fd80ff, 0x1ec093f0, 0x00000602, 0x00000000, 0x00150802, 0x904e8397, +0x17c089b0, 0x7c001f01, 0xc0417020, 0x00db0107, 0x01f0007c, 0x5f0117c8, +0x30007c18, 0x0804c001, 0x034c00d3, 0x08c06bb0, 0x00000420, 0x00000000, +0x0c14a013, 0x0b4c21dd, 0x2740a514, 0x6001d601, 0x803d1003, 0x09152077, +0xbdd10774, 0x9d006748, 0x402b7c42, 0x04fe400f, 0x03c400d1, 0x4c913910, +0x00000200, 0x00000000, 0x00428007, 0x07048085, 0x63403510, 0x0405cd00, +0x421c901b, 0x220120b3, 0x0cd04f34, 0x8920e340, 0x90073443, 0x0972580c, +0x032400c1, 0x1c403410, 0x00000a00, 0x00000000, 0x00488004, 0x24a409ad, +0x5b408a10, 0x94312508, 0x411290a4, 0x91e10249, 0x92d0a4b4, 0x6d0c5b40, +0xd0140425, 0x000a0010, 0x07a400c1, 0x1a409e14, 0x00000200, 0x00000000, +0x00300012, 0x62460097, 0x23c00034, 0x7d00cf00, 0x400cf403, 0x04034417, +0x0cf0433c, 0x8f2043c1, 0xb0673400, 0x0314c004, 0x036c48c3, 0x48c08130, +0x00000040, 0x00000000, 0x003da802, 0x21dc20ff, 0x17c0cf70, 0xec883f00, +0x80037200, 0x00f7a82f, 0x03f2007c, 0x7f203fc2, 0x70807c84, 0x016dc88b, +0x0bdc0cbf, 0x09c08771, 0x00000660, 0x00000000, 0x0027aa15, 0x014c049f, +0x05401530, 0x5c001300, 0xc0113004, 0x00170024, 0x01300044, 0x1f0024d0, +0xf0005c00, 0x0027c0e9, 0x236c00df, 0x54c01f10, 0x00000e00, 0x00000000, +0x10298812, 0x02ec08fd, 0x38600a10, 0x3c00eb28, 0xc40eb303, 0x00eb0012, +0x0fb00384, 0xf90018c0, 0xd103bc00, 0x005b4146, 0x038440ad, 0x48400c12, +0x00000620, 0x00000000, 0x00790003, 0x048400ad, 0x40401283, 0xb4012508, +0x40109004, 0x01210048, 0x12900404, 0x2d004a40, 0xd0048441, 0x024b4052, +0x173401ed, 0x0d411e13, 0x00000402, 0x00000000, 0x04b32012, 0x030449cd, +0x31400c90, 0x3448cd10, 0x452c984b, 0x00c98232, 0x2c900f04, 0xc9203061, +0xd8cb0512, 0x8033601d, 0x031403cd, 0x49403c10, 0x00000c20, 0x00000000, +0x01dda017, 0x09c4827f, 0x14d005b4, 0x5c035702, 0x4865b019, 0x40510054, +0x65903545, 0x5d1016c2, 0xf0195402, 0x0113e055, 0x017c117f, 0x5dd03730, +0x00000620, 0x00000000, 0x20030012, 0x407c301f, 0x8ac80373, 0xdc063b10, +0xc023f208, 0x023e008f, 0x03f008bc, 0x3b000fc0, 0xf000fe02, 0x000fea23, +0x0024001f, 0x4a0061f4, 0x00000c00, 0x00000000, 0x00270010, 0x020c0883, +0x67c04930, 0x7c419300, 0xc0599016, 0x05830161, 0x4830066c, 0x8720a0c0, +0x308a2e41, 0x3824cc59, 0x026d5097, 0x43c01930, 0x00000c20, 0x00000000, +0x38662801, 0x16540091, 0x27644950, 0x5c009500, 0x40499012, 0x839500e7, +0x7952165c, 0x9510e540, 0x520e5403, 0x00244009, 0x02640091, 0x07407950, +0x00000800, 0x00000000, 0x0224a018, 0x12440091, 0x2f400912, 0xe488b902, +0x400bd082, 0x00bd002f, 0x0b1822c4, 0xb1102c40, 0x1002c408, 0x902e002b, +0x0245008d, 0x63444910, 0x00000200, 0x00000000, 0x48202010, 0x0a140281, +0xab402a50, 0x8402ad00, 0x442a910a, 0xc2a528ab, 0x2a528a94, 0xa538b944, +0x520b9402, 0x022a528a, 0x12040089, 0x43408850, 0x00000080, 0x00000000, +0x0506b01d, 0x504e0013, 0x17400030, 0x7c205b20, 0xc001f000, 0x004b2017, +0x0130004e, 0x570004c0, 0x30006c00, 0x0d8ec161, 0x2c6c1e0f, 0x77c16130, +0x00000ac0, 0x00000000, 0x002f9019, 0x02fc42ff, 0xa7c029f1, 0x70029720, +0xc029f00a, 0x029f10b7, 0x29f00a5c, 0x9f00a7c2, 0xf00a7c82, 0x0125c049, +0x267c81b7, 0x67c24bb4, 0x00000e60, 0x00000000, 0x012fa818, 0x0a4c14bf, +0x24c129b0, 0x4c089e05, 0x89693222, 0x149325a4, 0x29321246, 0x93022480, +0xb04a6802, 0x002fc94b, 0x026c009d, 0x67c10bf0, 0x00000e00, 0x00000000, +0x8087081c, 0x006c461d, 0x86c02110, 0x6c421d30, 0xc461b008, 0x42111186, +0x01113844, 0x1b938440, 0x1100ec80, 0x01074060, 0x28440e17, 0x734021e0, +0x00000c22, 0x00000000, 0x00a3a010, 0x1284108d, 0x2b604a50, 0x8606ad84, +0x600ad81a, 0x10a58028, 0x6a584296, 0xad802942, 0x98129446, 0x04a24908, +0x5224108d, 0x434148d0, 0x00000e80, 0x00000000, 0x01a5a018, 0x0224009d, +0x2f440b10, 0xa400bd00, 0x420bd002, 0x60b5182a, 0x0a5103c4, 0xb91c2d40, +0x1002f420, 0x00276409, 0x82640095, 0x63414950, 0x00000620, 0x00000000, +0x00a7a005, 0x064c029d, 0x25c009b4, 0x4c009f00, 0xc009f402, 0x00974024, +0x09540255, 0x9d00a5d0, 0xb0025420, 0x0126c909, 0x026c049f, 0x17c039d0, +0x00000e20, 0x00000000, 0x00258014, 0x0e7c029f, 0x26c099f0, 0x7c008f00, +0xc4093082, 0x009b3827, 0x09b3026c, 0x9b0026c0, 0xf0026c00, 0x0427e809, +0x025c0587, 0x53c019f0, 0x00000600, 0x00000000, 0x00050014, 0x00cc0217, +0x0cc30238, 0xcc103340, 0xc4023000, 0x8037048f, 0x03f280cc, 0x2b0088c0, +0xf000f800, 0x2084c001, 0x80480213, 0x53c021d2, 0x00000420, 0x00000000, +0x0014a814, 0x0145015d, 0x95431558, 0x54025100, 0x44355021, 0x1b570017, +0x85d18970, 0x5d201540, 0xd001f40a, 0x009c4007, 0x01440055, 0x530067d0, +0x00000200, 0x00000000, 0x0032a014, 0x038401dd, 0xba601e12, 0xb628e904, +0x43ae90a3, 0x23fd20bb, 0x0e904fe4, 0xed0c7860, 0xd087d48a, 0x08b3420c, +0x830400c9, 0x53413cd0, 0x00000a00, 0x00000000, 0x10388005, 0x038402ed, +0xfb400c50, 0x3400e900, 0x400ed203, 0x00e5003a, 0x2ed00bb4, 0xed843940, +0xd00ba440, 0x0043421e, 0x138404ed, 0x17400ed0, 0x00000200, 0x00000000, +0x00681015, 0x070c81ff, 0x7ec01e18, 0xbc01cb18, 0xc01eb007, 0x21c7007b, +0x1cf187ac, 0xeb0078c0, 0xf0079c01, 0x007bc01e, 0x178e01fb, 0x57c01af0, +0x00000040, 0x00000000, 0x0035b010, 0x007c00df, 0x05c001f0, 0x5c001710, +0xc0016000, 0x001f0017, 0x01f0005c, 0x1f1007c8, 0xf0007c00, 0x0804c00d, +0x1b7d0ad7, 0x43c003f0, 0x00000660, 0x00000000, 0x007ba000, 0x07c804f3, +0x7c801f30, 0xc801f300, 0xc01fb007, 0x09da0274, 0x1fe007dc, 0xf2027f80, +0xf087f809, 0x005cc09d, 0x4f5403df, 0x0bc097f0, 0x00000e00, 0x00000000, +0x00398815, 0x138410ab, 0x3ac04e10, 0x8444f100, 0x440ef113, 0x27ef0078, +0x0ef023b4, 0xeb10b9c0, 0xd0033408, 0x0408441e, 0x079421fd, 0x57c00ad0, +0x00000620, 0x00000000, 0x00290000, 0x83040081, 0x38400e12, 0xa400e102, +0x400e9843, 0x08ed0138, 0x0ed003b4, 0xe9003b40, 0xd047b400, 0x001a408c, +0x038700ed, 0x236006d0, 0x00000400, 0x00000000, 0x30332804, 0x8c240099, +0xc260a010, 0x340a0120, 0x40315a08, 0x420d1210, 0xe0402834, 0x01428540, +0xd004344e, 0x0040489c, 0x030413dd, 0x194020d0, 0x00000c20, 0x00000000, +0x0035a815, 0x634c01d1, 0xf4c83d10, 0x6d03d349, 0x507d900b, 0x92d90064, +0x3dd00f5e, 0xd900f7c0, 0xd0077c03, 0x00c4c00d, 0x03dd12ff, 0x57400dd0, +0x00000620, 0x00000000, 0x28260001, 0x015c00df, 0xb7c02df0, 0x4c00df00, +0xc02df10b, 0xc2d700a7, 0x2df0037c, 0xdf08b7c4, 0xf2037c42, 0x0497d00c, +0x037400df, 0x27007ff2, 0x00000c00, 0x00000000, 0x006b0880, 0x02a400f3, +0x7ec10c30, 0xfc11ff04, 0xc19ff057, 0x15f7046c, 0x9ff0c3c4, 0xf3043e00, +0x7063cc42, 0x000cc103, 0x03cd00f3, 0x04c01af1, 0x00000c20, 0x00000000, +0x00262081, 0x034400d1, 0x85404110, 0x74041d10, 0x4031d10c, 0x401e2404, +0x51d00874, 0x1110c740, 0x10045401, 0x43514031, 0x034080d5, 0x254059d0, +0x00000802, 0x00000000, 0x02342001, 0x1b5400d1, 0x04404110, 0x74081d06, +0x41015840, 0x201c2204, 0x01d0a044, 0x01020640, 0x500224b0, 0x0024538d, +0x834000d1, 0x044085d0, 0x00000200, 0x00000000, 0x00202010, 0x01140081, +0x01400010, 0x34000c00, 0x5000d800, 0x24090100, 0x00d10036, 0x01400340, +0x10023400, 0x0010410c, 0x030404c1, 0x404004d2, 0x00000080, 0x00000000, +0x00269000, 0x025c0093, 0x00c00110, 0x7c801f08, 0xc8007080, 0x011f0844, +0x01f2004e, 0x120202c2, 0x70826c80, 0x0024c011, 0x17cc01f3, 0x04c001f0, +0x00000ac0, 0x00000000, 0x802fb005, 0x03cc00bf, 0x3fc40fd0, 0xfc00ff10, +0xc00ff103, 0x08f7022f, 0x0ff003fc, 0xff097fc0, 0xf045dc00, 0x001fc403, +0xa3fc08ff, 0x17c003f0, 0x00000e40, 0x00000000, 0x000fa003, 0x23fc00ff, +0x3cc00f31, 0xcc00fd30, 0xc00ff003, 0x40ff002d, 0x0f7803cc, 0xfa003fc4, +0xf081cc80, 0x083fc00f, 0x03cc04f3, 0x0cc06f74, 0x00000e00, 0x00000000, +0x04870801, 0x0274121d, 0x054001b8, 0x6c801f00, 0x4001d200, 0xc0170807, +0x01520044, 0x05280740, 0xd0026c80, 0x00b7400d, 0x03948ae1, 0x04404e10, +0x00000c20, 0x00000000, 0x0303a011, 0x01160ccd, 0x02460090, 0x04800d00, +0x44005800, 0x000d0001, 0x00900005, 0x05200140, 0x50022420, 0x60b14008, +0x033400c9, 0x44406c10, 0x00000e80, 0x00000000, 0x0005a803, 0x0336289d, +0x07428110, 0x6400152c, 0x4241d080, 0x20151207, 0x81504054, 0x15000646, +0xd1826420, 0x0037420d, 0x033400d9, 0x0c400d12, 0x00000620, 0x00000000, +0x0637a802, 0x077c00df, 0x06c82190, 0x4c0b1d08, 0xc131f01c, 0x041f0145, +0x21b3084c, 0x1704c7c4, 0xf0046c49, 0x0237c00d, 0x037c00db, 0x08c00d10, +0x00000e20, 0x00000000, 0x003d8007, 0x0efca07e, 0x3dc80ff2, 0xfc21ff0a, +0xc01ff013, 0x04ff1027, 0x0df023ec, 0xf500bb80, 0xf027fc00, 0x0077400f, +0x03dc60f7, 0x1fc00f80, 0x00000600, 0x00000000, 0x10350802, 0x017c08df, +0x37c02cf0, 0x7c02c300, 0xc00df083, 0x20d70027, 0x2df00b3c, 0xc34236c0, +0xb1034c80, 0x0437c04d, 0x035c00c3, 0x08c00db4, 0x00000420, 0x00000000, +0x0ef4a213, 0x03460bd9, 0x37400dd0, 0x6403d5a0, 0xc10dd007, 0x18d50c24, +0x5de0c364, 0xd0087542, 0x500f4401, 0x00b7043d, 0x03c407f1, 0x4c400f50, +0x00000200, 0x00000000, 0x0022a007, 0x2014230d, 0x034000d0, 0x342501a0, +0x6330d06c, 0x011104d2, 0x00904824, 0x05048141, 0x116c0413, 0x40b7402c, +0x030513c9, 0x1c400d10, 0x00000a00, 0x00000000, 0x00688004, 0x07844129, +0x73401ed1, 0xb499e520, 0x645ed007, 0x01e50178, 0xded007a4, 0xe5027940, +0x53170411, 0x147b415e, 0x27a481e9, 0x10401e50, 0x00000200, 0x00000000, +0x00301012, 0x0b140a1f, 0x33c28cf1, 0x3c02c100, 0xc00cf023, 0x00c70033, +0x6cf0333c, 0xc72035c0, 0x30030d0c, 0x00238008, 0x031c80cb, 0x48c00c32, +0x00000040, 0x00000000, 0x003db802, 0x23dc00bb, 0x3fc20ff0, 0xec20ff00, +0xc24ff0a3, 0x00de0136, 0x4f70a3ec, 0xfb003dc0, 0x73177c28, 0x002fc24f, +0x83dc40f7, 0x0bc00f71, 0x00000660, 0x00000000, 0x0027a015, 0x007c001f, +0x05c201f0, 0x4d001f00, 0xc2013100, 0x001b0054, 0x11f2007c, 0x1b0804c8, +0x30047c00, 0x0834c01d, 0x434485d1, 0x54c13d34, 0x00000e00, 0x00000000, +0x00298812, 0x03b4006c, 0x39c00ed0, 0x8400e320, 0x400e9803, 0x80e12038, +0x0ec803b4, 0xeb083040, 0xb003b400, 0x2038440f, 0x138414fb, 0x49404e50, +0x00000620, 0x00000000, 0x00f90003, 0x07b4012d, 0x79401e50, 0x8401ed00, +0x401e1007, 0x01e10078, 0x1ed007b4, 0xe408f848, 0x50473401, 0x2061401e, +0x27850ce1, 0x0c549c50, 0x00000400, 0x00000000, 0x01b30812, 0x03b600cd, +0xbd407ed0, 0x8407f500, 0x401e904f, 0x8ae100b8, 0x1ed02fb4, 0xed08f844, +0xd003b440, 0x0021501c, 0x030400c9, 0x49400c58, 0x00000c20, 0x00000000, +0x005da817, 0x0174117f, 0xd5c11470, 0x4c115f04, 0xd1953049, 0x015b4094, +0x65f00d7c, 0x570014d0, 0x7001f405, 0x0015c115, 0x014c0053, 0x5cc00550, +0x00000620, 0x00000000, 0x10830012, 0x08fe001f, 0x0dc403f2, 0xfc423304, +0xc022b188, 0x063f210f, 0x63f148f4, 0x3ba08fc4, 0x9008fc02, 0x1006c001, +0x007c821b, 0x4ac001a2, 0x00000c00, 0x00000000, 0x00270810, 0x061c0293, +0x26c00970, 0x4c019300, 0xc019b0a6, 0x059f0024, 0x5930163c, 0x9304a7c0, +0xf0227c99, 0x0223c009, 0x020c0583, 0x43c009b4, 0x00000c20, 0x00000000, +0x00260001, 0x1a4c0091, 0x25c08950, 0x44129520, 0x01b9140e, 0x279d0025, +0x49529e74, 0x9101a748, 0xd00a7403, 0x00e74009, 0x02440495, 0x0740091c, +0x00000800, 0x00000000, 0x8024a018, 0x22540089, 0x2c400f10, 0x8404b120, +0x000b1242, 0x00bd022c, 0x0b1002f4, 0xf0002b46, 0xd082f490, 0x04270089, +0x02444091, 0x63400914, 0x00000200, 0x00000000, 0x42302010, 0x22a40281, +0xa9480a50, 0x8502a500, 0x422a100a, 0x02ad00a9, 0x2a500ab4, 0xa140aa40, +0xd10bb402, 0x05a34028, 0x12040685, 0x43404812, 0x00000080, 0x00000000, +0x0586b01d, 0x085e8013, 0x04c00170, 0x4c000300, 0xc8003000, 0x801f0810, +0x0030807c, 0x120007c0, 0xf000fc00, 0x0107c005, 0x2c0c0113, 0x77c0b134, +0x00000ac0, 0x00000000, 0x012fb819, 0x1a4c02bf, 0xa5c029f0, 0x7c429f00, +0xc429f10a, 0x029f00a7, 0x29f10a7c, 0x9f00a7c0, 0xf20a7c02, 0x002fc00b, +0x267c289d, 0x67c099f0, 0x00000e60, 0x00000000, 0x01afa818, 0x1a6c06b7, +0xa4c04b70, 0x4c869e01, 0x4009b032, 0x109f0027, 0x6930124c, 0x9f2327c0, +0xb0027000, 0x09254489, 0x1a7c8093, 0x63c00932, 0x00000e00, 0x00000000, +0x0183081c, 0x184c8617, 0x84c021d0, 0x5c021b21, 0xc0417008, 0x44170014, +0x05a20144, 0x1f001740, 0x10a8742a, 0x03874000, 0x90740a31, 0x7341e110, +0x00000c20, 0x00000000, 0x00a30010, 0x1a260285, 0xa2502850, 0x8602ad10, +0x412a911a, 0x10ad04ab, 0x2a825a85, 0xad01ab48, 0x1442b494, 0x442b444a, +0x1a3414a1, 0x43400854, 0x00000e80, 0x00000000, 0x01258018, 0x43440295, +0x254109d0, 0xd4009590, 0x630b5022, 0xa0a50a2c, 0x8b102284, 0xb52a2f48, +0x1142f4d0, 0x283b600b, 0x027400b1, 0x63400950, 0x00000620, 0x00000000, +0x0027a005, 0x0e6c0097, 0x64c03970, 0x4c189d01, 0xc039b046, 0x079f2027, +0x0994024c, 0x9d00e7c0, 0xb2027420, 0x1065c209, 0x027c4091, 0x17420970, +0x00000e20, 0x00000000, 0x00258014, 0x135c009f, 0x24c088f0, 0x5c019b32, +0xc08970c2, 0x84970026, 0x09f2426c, 0x9c8027c1, 0xf0267c19, 0x0926c029, +0x027c009b, 0x53c00920, 0x00000600, 0x00000000, 0x00050014, 0x007c000b, +0x87c001f0, 0xcc003b80, 0xc0033000, 0x5033000c, 0x03b000cc, 0x3c000cc0, +0xf108cc02, 0x020ed003, 0x003c003b, 0x50c00130, 0x00000420, 0x00000000, +0x001ca814, 0x6d440071, 0x174005d0, 0x54005f20, 0x4015b019, 0x035b00d5, +0x05f0096c, 0x5d0017c0, 0xd2215413, 0x20144225, 0x01740175, 0x50400500, +0x00000200, 0x00000000, 0x0072a014, 0x031410c1, 0x33400cd0, 0xa400f100, +0x61ae500b, 0x2be103fa, 0x0e102bb4, 0xe9333840, 0xd007e402, 0x8cf842fe, +0x033481e1, 0x50500c00, 0x00000a00, 0x00000000, 0x20f88005, 0x038400c1, +0x72400ed8, 0x3400cd08, 0x6c1cd107, 0x00e9007b, 0x3ed203a4, 0xed203a40, +0xd0439401, 0x0030481e, 0x13b442e5, 0x14405e10, 0x00000200, 0x00000000, +0x40781015, 0x279c01a1, 0xfb401ad0, 0xa401e104, 0xc01e7007, 0x61c30072, +0x1c3407d8, 0xfa1078cc, 0xf1078c81, 0x08788a1e, 0x073c01e3, 0x54c05c36, +0x00000040, 0x00000000, 0x0015b010, 0x134c0097, 0xb7c009d0, 0x5c001901, +0xcc01b000, 0x001f0005, 0x0530006c, 0x1d0807c6, 0xf1007c20, 0x0805c001, +0x2b7c0017, 0x43c0edf1, 0x00000660, 0x00000000, 0x906fa200, 0x07fc0573, +0x7ec01f70, 0xcc01f700, 0x001f3407, 0x41f7427f, 0x1bfa377c, 0xff027cd0, +0x7427fc81, 0x027fc09f, 0x0fcc41f3, 0x03c03f32, 0x00000e00, 0x00000000, +0x003c8815, 0x02bc006b, 0x3de00eb0, 0xd410f12a, 0xc00e7083, 0x20e1033d, +0x6bd817b4, 0xed02b941, 0x100bb400, 0x0b3b44ce, 0x23d440ef, 0x57404eb0, +0x00000620, 0x00000000, 0x00290000, 0x01340621, 0x3a408a90, 0x8400e500, +0x410e1063, 0x08e1003b, 0x0a5203a4, 0xed0a3840, 0x1203b400, 0x803b480e, +0x038401e5, 0x03600e90, 0x00000400, 0x00000000, 0x08132804, 0x08140001, +0x71481811, 0x14010100, 0x4001104c, 0x60010241, 0x00d00034, 0x0d090040, +0x1028340b, 0x00034200, 0x0354600d, 0x13400c80, 0x00000c20, 0x00000000, +0x3015a815, 0x65744091, 0xbec1b914, 0x4c43d522, 0xc2bd1107, 0x53d720b7, +0x2d784b74, 0xdd04f4c0, 0x708f7c8a, 0x0037804d, 0x03cc00d3, 0x57c00f9c, +0x00000620, 0x00000000, 0x08270201, 0x097c015f, 0x37c209f0, 0x3c905f22, +0xc80d7083, 0xd0df21b7, 0x0df0137c, 0xdd00b7c1, 0xf10b7c02, 0x00b7c02d, +0x037c00da, 0x07c00df0, 0x00000c00, 0x00000000, 0x009b0880, 0x0cfc00b3, +0x3fc80bf0, 0xec00bb10, 0xc21fb20f, 0x03ef017c, 0x0ff023cc, 0xee013f48, +0xf047cc00, 0x0074c08f, 0x03f801df, 0x03c00ff8, 0x00000c22, 0x00000000, +0x84c62081, 0x00740051, 0x374019d2, 0x4c40d518, 0x40117094, 0x021d00c5, +0x25d04854, 0x1d204740, 0xd0005426, 0x00454310, 0x03742017, 0x07400d50, +0x00000802, 0x00000000, 0x0804a001, 0x01748191, 0x33484990, 0x7486d900, +0x40615200, 0x501d0885, 0x01d08054, 0x14000640, 0xd0204604, 0x09064001, +0x0374649d, 0x07400dd8, 0x00000200, 0x00000000, 0x00202010, 0x00240141, +0x32400898, 0x04004100, 0x4000d100, 0x40082000, 0x00824014, 0x0d000248, +0xd0001400, 0x00014001, 0x03340085, 0x43400c18, 0x00000080, 0x00000000, +0x4006b000, 0x007c0091, 0x3fc009f0, 0x7c209b00, 0xd0017200, 0x801e0005, +0x01d0945c, 0x1e9007c0, 0xe2000c00, 0x00048401, 0x03fc009f, 0x03c00ff0, +0x00000ac0, 0x00000000, 0x000f9005, 0x00fc407f, 0x3fc40be1, 0xdc20ff00, +0xc40f7083, 0x80fe083f, 0x0bf003fc, 0xff003fc8, 0xf003fc00, 0x003fc20f, +0x03fc0077, 0x17c00ff0, 0x00000e60, 0x00000000, 0x010fa803, 0x03ec04ff, +0x3fc00ff0, 0xfc00ff00, 0xc20ff003, 0x00df0037, 0x0fb003ec, 0xf70037c0, +0xf003fc60, 0x006fc015, 0x236c079f, 0x0cd08fb0, 0x00000e00, 0x00000000, +0x05a70801, 0x0044321d, 0x05c001d2, 0x74000700, 0x4201d000, 0x001d0007, +0x01140044, 0x1d200740, 0xd1007420, 0x0292c085, 0x9b84825d, 0x04486f10, +0x00000c20, 0x00000000, 0x0213a011, 0x82248c0d, 0x134008d1, 0x3400cd00, +0x400cd003, 0x20cd2012, 0x0c100114, 0xcd083344, 0xd0013400, 0x20034004, +0xa304868d, 0x44446c10, 0x00000e80, 0x00000000, 0x3225a003, 0x417400cd, +0x274005d8, 0x74001d00, 0x4001d800, 0x000d0027, 0x01030254, 0x1d040740, +0xd0027422, 0x00354005, 0x0364005d, 0x0c400c14, 0x00000620, 0x00000000, +0x2147a002, 0x036c00df, 0x37c805f0, 0x7412df00, 0xc53df04b, 0x005f2237, +0x0130075d, 0x970277c0, 0xf0077c82, 0x000f400f, 0x036d00bf, 0x00c00db2, +0x00000e20, 0x00000000, 0x006d8007, 0x00c8803d, 0x0d800bd0, 0xfc183702, +0xc083f0a0, 0x08bf044f, 0x0ff010ec, 0x7f204fc0, 0xf220fc00, 0x003ec00f, +0x83dc006e, 0x1fc80ff0, 0x00000602, 0x00000000, 0x40150002, 0x465c2413, +0x17c10170, 0x6c02df08, 0xc12d700b, 0x205f2214, 0x01b0310c, 0x9d0037c0, +0xf3014c10, 0x4406c8cd, 0x034c0093, 0x09c00d70, 0x00000420, 0x00000000, +0x2024a013, 0x014400d1, 0x67425d18, 0x44011d09, 0x4821d088, 0x0b9d00a5, +0x5dd08244, 0x59008746, 0xd0064c01, 0x0034402d, 0x03c40051, 0x4c40af10, +0x00000200, 0x00000000, 0x0202a807, 0x811422c1, 0xa3400c50, 0x04210d04, +0x40a0d030, 0x238c0822, 0x2c900e06, 0x4d048341, 0xd0622400, 0x08404014, +0x03440080, 0x1c409c10, 0x00000a00, 0x00000000, 0x11508004, 0x26840121, +0x5a401290, 0x8611ed01, 0x401ed007, 0x956d015a, 0x92d00586, 0xa9047b40, +0x920584e1, 0x01784c56, 0x07840541, 0x18401c10, 0x00000200, 0x00000000, +0x00101012, 0x201c0013, 0x43c04878, 0x0d000f21, 0x50417200, 0x019f2042, +0x8cb2b00d, 0x5f2343c0, 0xf2002914, 0x8480d055, 0x070d0583, 0x49c28c70, +0x00000040, 0x00000000, 0x011db802, 0x23be00ff, 0x3fc00770, 0xdc00ff00, +0xc00ff223, 0x407f003d, 0x81f0a37c, 0xbb0037c0, 0xf027fcc0, 0x013dc007, +0x23fc097f, 0x0bc00ff4, 0x00000660, 0x00000000, 0x0067a815, 0x814c81d3, +0x65c00570, 0x7c001300, 0xc001f800, 0x001f0027, 0x11f0027c, 0x1f0004c2, +0xf0827c20, 0x2007c00d, 0x0b4b0193, 0x57c0adf0, 0x00000e00, 0x00000000, +0x10398812, 0x02ac0001, 0x18400a10, 0x9c00e100, 0xc20ed803, 0x00ed001b, +0x0ed001b4, 0xe7003840, 0xd001b400, 0x1033480e, 0xcbac006b, 0x4b410ed0, +0x00000620, 0x00000000, 0x00790003, 0x04840121, 0x49401250, 0xb4a12900, +0x4812d884, 0x012d204b, 0x32d084b4, 0x2d20484a, 0xd284b4a1, 0x004b601e, +0x87840081, 0x0f405ed1, 0x00000402, 0x00000000, 0x00332012, 0x032440c1, +0x30402c10, 0x5400c900, 0x400cd003, 0x00cd0037, 0x3cd20336, 0xc5083041, +0xd0033421, 0x2433486c, 0x03040049, 0x4b400cd8, 0x00000c20, 0x00000000, +0x401d8017, 0x01440071, 0x15c02450, 0x7c005b40, 0xc005f001, 0x405f0017, +0x05f0017c, 0x5f0014d0, 0xf081fc04, 0x0017c025, 0x01440073, 0x5fc005f0, +0x00000620, 0x00000000, 0x00c70012, 0x00fc001f, 0x8ec103f8, 0xfc003700, +0xc403f000, 0x063f000d, 0x23f000fe, 0x3f108fc4, 0xf000bc00, 0x000fc420, +0x007c003f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x02670010, 0x021c019f, +0x66c059b1, 0x7c009f00, 0xc0093102, 0xc2930027, 0x19300a7e, 0x9f00e7c0, +0x31027c04, 0x0060c019, 0x020c0083, 0x40c00830, 0x00000c20, 0x00000000, +0x10262001, 0x0274419d, 0xe4401931, 0x74189d20, 0x4009120e, 0x03912027, +0x39110e74, 0x9d002742, 0x142a7403, 0x00e4c039, 0x02442095, 0x04500950, +0x00000800, 0x00000000, 0x0424a818, 0x0674049d, 0x2e400b10, 0xf400bd01, +0x406b501a, 0x00b111af, 0x8b9002b4, 0xbd902f40, 0x108af490, 0x062c40a9, +0x024400b1, 0x60408910, 0x00000200, 0x00000000, 0x02202010, 0x06b4038d, +0xa8400a90, 0xb602ad00, 0x402a500a, 0x02a110ab, 0x2a900bb4, 0xad80bb40, +0x100ab402, 0x00a84028, 0x920400a5, 0x4048d850, 0x00000080, 0x00000000, +0x8d82b01d, 0x007c941f, 0x12c001b4, 0x7c801f00, 0xc8017400, 0x80524007, +0x04b40070, 0x1f0007c2, 0x3000fc00, 0x0004d001, 0x2c4e2033, 0x74c03130, +0x00000ac0, 0x00000000, 0x012fb819, 0x0a3c00bf, 0xb7c82878, 0x7c229f08, +0xc229b20a, 0x029f00a7, 0x29720a7c, 0x9f10a7c0, 0xf08a7c42, 0x00a5c429, +0x267c169f, 0x67c0c9f0, 0x00000e60, 0x00000000, 0x002fa018, 0x02cc20bf, +0x27c24b30, 0x4c089f05, 0xc149f0b2, 0x089f2b24, 0xc9f0c25e, 0x932824c1, +0x00227004, 0x0167c059, 0x124c0591, 0x67c14b30, 0x00000e00, 0x00000000, +0x2487081c, 0x206c901d, 0x07400114, 0x14881f01, 0x4841d000, 0x82172300, +0x21d2585c, 0x01078140, 0x30187484, 0x248dc921, 0x58440211, 0x736141b0, +0x00000c22, 0x00000000, 0x0b23a010, 0x0224008c, 0x2b408810, 0x8480ad05, +0x494ad232, 0x0cad1129, 0x0ad102b4, 0xa1112944, 0x961aa436, 0x00ab426a, +0x32241689, 0x43400810, 0x00000e80, 0x00000000, 0x80258818, 0x0264409d, +0x27600910, 0xd400bd00, 0x400ed002, 0x00b5002d, 0x0bd242f4, 0xb1002d40, +0x1042f400, 0x202d400b, 0x02640099, 0x63600990, 0x00000620, 0x00000000, +0x0a67a805, 0x064c339f, 0x27c00930, 0x4c009f08, 0xd009fa02, 0x009f04a5, +0x09f00a7c, 0x93c025c8, 0xb8026c00, 0x4127c809, 0x026c009a, 0x17400930, +0x00000e20, 0x00000000, 0x14258014, 0x223c058f, 0x27c039f0, 0x7c009700, +0xc009b002, 0x009f0026, 0x09f0025c, 0x9f0827c0, 0xf0023c40, 0x0025c809, +0x025c0097, 0x53c009f0, 0x00000600, 0x00000000, 0x00050814, 0x004c0217, +0x8fc80134, 0xfc803340, 0xc8033400, 0x6c33448f, 0x03f000dc, 0x33240fc2, +0x7088cd04, 0x000dc2c3, 0x003c0003, 0x53c101f0, 0x00000420, 0x00000000, +0x049ca014, 0x85c43177, 0x17408711, 0x74055320, 0x49251201, 0x20541c97, +0x25d03d5c, 0x51105743, 0x100d4401, 0x001f4025, 0x01740051, 0x534037d0, +0x00000200, 0x00000000, 0x00f2a014, 0x030400c5, 0x3b420c12, 0xb407f100, +0x407e9837, 0x03e1003b, 0x7fd08f96, 0xe100ff00, 0x11138441, 0x407d600e, +0x033400c1, 0x53408cd0, 0x00000a00, 0x00000000, 0x08288005, 0x0b040065, +0x7b400a10, 0xb400e120, 0x400e9007, 0x00e9007b, 0x0ed003a4, 0xe9143a44, +0x10438403, 0x043b420e, 0x23b405e5, 0x17480ad0, 0x00000200, 0x00000000, +0x00781015, 0x878c0107, 0x7f4c1e32, 0xbc01e310, 0xc01e3007, 0x81e3007b, +0x1ef1079c, 0xe3407bc8, 0x7407cc81, 0x0871c41e, 0x07bc02e3, 0x57c016f0, +0x00000040, 0x00000000, 0x00359810, 0x017d001f, 0x07c20dd1, 0x7c001700, +0xc4017000, 0x00150007, 0x01f0015c, 0x170807c0, 0xf1007c00, 0x0007c001, +0xcb7c01db, 0x43c003f0, 0x00000660, 0x00000000, 0x005f8800, 0x07cc01b3, +0x7ec09732, 0x4881f300, 0xc09f9007, 0x01f30174, 0x1f3006dc, 0xfb127cc0, +0xf0074c05, 0x093ec09f, 0x07cd01f3, 0x0bc01f30, 0x00000e00, 0x00000000, +0x00899815, 0x03844a21, 0x30400ab0, 0x840aeb02, 0x408eb007, 0x02e90078, +0x0e115694, 0xe30a7840, 0xf0578404, 0x003ac0ae, 0x038400fb, 0x57400a10, +0x00000620, 0x00000000, 0x02290000, 0x23040021, 0x30610410, 0x0410c100, +0x450c1803, 0x18cd0633, 0x0c103284, 0xc1893041, 0xd023a45c, 0x0070440e, +0x039400e9, 0x23400610, 0x00000400, 0x00000000, 0x24e72004, 0x15041801, +0xc0401c18, 0x04200109, 0x4120901c, 0x25098083, 0x60110854, 0x0112c040, +0x51402400, 0x00c04000, 0x031002c1, 0x1b000110, 0x00000c20, 0x00000000, +0x44b5a015, 0x154c01d0, 0xb4c01d11, 0x4c00d104, 0xc02d304b, 0x81d30a77, +0x2d310b4c, 0xd302f4c0, 0xd0036c02, 0x04f4540d, 0x03dd0ff9, 0x77400d34, +0x00000620, 0x00000000, 0x80270001, 0x037d025f, 0x15c021f0, 0x7c04df00, +0xc00d7203, 0x20d210b4, 0x2df2436c, 0xdf0037c8, 0xf00b5c02, 0x0137c00c, +0x036c00cf, 0x87c007f8, 0x00000c00, 0x00000000, 0x80170080, 0x058c2183, +0x28c70a30, 0x8e01eb00, 0xc20e3023, 0x20e30038, 0x2e32035c, 0xeb0078c8, +0x3217cc19, 0x00b8c05f, 0x034c00fb, 0x04c01bf0, 0x00000c20, 0x00000000, +0x00962081, 0x0d440097, 0xf442b110, 0x44031100, 0x4411100c, 0x42150444, +0x11f04968, 0x1108c6c0, 0x10005423, 0x0046c021, 0x034480db, 0x04c021d2, +0x00000802, 0x00000000, 0x0014a001, 0x194508d9, 0xb4402510, 0x44041101, +0x40411040, 0x08112204, 0x01100044, 0x11018441, 0x10401410, 0x10244001, +0x834420d1, 0x0440add0, 0x00000200, 0x00000000, 0x00002010, 0x0304804d, +0x10600010, 0x04400102, 0x40001040, 0x00011100, 0x00140004, 0x01210244, +0x10001440, 0x80224000, 0x230408c9, 0x424008d0, 0x00000080, 0x00000000, +0x0006b000, 0x014c00db, 0x24400132, 0x44001900, 0xc0013014, 0x08130b44, +0x01101454, 0x1320c4c0, 0x34144480, 0x4024c280, 0x03cd00d3, 0x04e005f2, +0x00000ac0, 0x00000000, 0x800fb805, 0x00fc00f7, 0x7fc003f0, 0xec00f701, +0xc00fd203, 0x85ff037f, 0x0ff00a7c, 0xff05f5c0, 0xf00bec00, 0x001fc05f, +0x17fc45ff, 0x15c003f1, 0x00000e60, 0x00000000, 0x040f9003, 0x10fc0433, +0x0fc00ff0, 0xc400f300, 0xc00f3003, 0x00f30036, 0x0b3003cc, 0xfb003cc0, +0xb003cc00, 0x003fc00f, 0x23cc0873, 0x0cc14371, 0x00000e00, 0x00000000, +0x00870001, 0x08740811, 0xb0484fd8, 0x4d009f02, 0x4001b400, 0x00110004, +0x01140044, 0x11400440, 0x10004500, 0x24074001, 0x83c44491, 0x04492110, +0x00000c20, 0x00000000, 0x0523a011, 0x20340001, 0x15410cd0, 0x26004120, +0x40009000, 0x00014000, 0x00d00006, 0x01800258, 0x10002400, 0x01034000, +0x13240089, 0x45404c10, 0x00000e80, 0x00000000, 0x0025a803, 0x01740051, +0x34400dd2, 0x2410c502, 0x40811060, 0x08110000, 0x01d0a806, 0x09000141, +0x10086408, 0x22074001, 0x03640099, 0x0c400d14, 0x00000620, 0x00000000, +0x0047a802, 0x007c4113, 0x81c00dd0, 0x4c83d100, 0xd0319108, 0x13130206, +0x31f1084d, 0x1b0086c0, 0xb4286c04, 0x4107c0f1, 0x036d0019, 0x09c3b130, +0x00000e20, 0x00000000, 0x08cd8007, 0x0efc04ff, 0x1f880ef2, 0xdc09bf00, +0x800ed207, 0x61ee007f, 0x8b3007f8, 0xf7023ec0, 0xf0039c01, 0x007bc01f, +0x035c84e7, 0x1fc012b2, 0x00000600, 0x00000000, 0x00310802, 0x077c8097, +0x97208d32, 0x6c025700, 0xc00d302b, 0x60d302b7, 0x69301b7c, 0xd900b5c0, +0x70035c08, 0x2234c0ad, 0x037c00de, 0x08c02f30, 0x00000420, 0x00000000, +0x02b4a013, 0x03740add, 0x1f601f14, 0x6d02d100, 0x405d1487, 0x05d12077, +0x0915177e, 0xd1017440, 0x14174501, 0x0076d01d, 0x03f480dd, 0x4c41ad14, +0x00000200, 0x00000000, 0x0012a007, 0x00240009, 0xd3410c10, 0x04470111, +0x4070581c, 0x070101c3, 0x35820c34, 0x0181c044, 0x101c0407, 0x01c24070, +0x0374000d, 0x1c403090, 0x00000a00, 0x00000000, 0x20788004, 0x05b4012d, +0x7b405e10, 0xb411e144, 0x401e5007, 0x01e1007b, 0x1c900794, 0xe1287041, +0x10078401, 0x007a401e, 0x37b401ed, 0x10401290, 0x00000200, 0x00000000, +0x00100012, 0x206c0047, 0x13400c39, 0x4c0cd710, 0xc0ac7023, 0x00c30377, +0x0cb12370, 0xd34231d0, 0x70335c08, 0x0232c0cd, 0x133c00cf, 0x48d001b0, +0x00000040, 0x00000000, 0x003da802, 0x01fc007f, 0x3fe04ff0, 0xcc08ff00, +0xc00fb023, 0x00dfc037, 0x8f7003fc, 0xf7003fc0, 0xf023fc00, 0x023dc08f, +0x13fc01fd, 0x0bc00370, 0x00000660, 0x00000000, 0x0007a015, 0x917c045f, +0x30c1adf0, 0x7c801342, 0xc001e000, 0x401f0007, 0x0572007c, 0x170007c2, +0xf0007c00, 0x0007c001, 0x1b4c001f, 0x57c00f30, 0x00000e00, 0x00000000, +0x00298812, 0xa3b408ed, 0x78680ed0, 0xb400e100, 0x480e7203, 0x00ed103b, +0x0ed003bc, 0xed203b40, 0xd203b400, 0x203bc00e, 0x4bac00ed, 0x4b400e10, +0x00000620, 0x00000000, 0x00d90003, 0x07b681ed, 0x7c401ed3, 0xb401e900, +0x411ed007, 0x21ed207b, 0x1ed007b4, 0xe5047b40, 0xd007b401, 0x007b403e, +0x170411ed, 0x0f411e94, 0x00000400, 0x00000000, 0x00772812, 0x1b3400dd, +0xb0440cd0, 0xb400e900, 0x400e500b, 0x12fd013b, 0x0ed10794, 0xed01bb44, +0xd00fb400, 0x08b9404e, 0x032421fd, 0x4b422c90, 0x00000c20, 0x00000000, +0x201da817, 0x117c265d, 0x9cd805d0, 0x7c005b00, 0xc015f041, 0x045f1417, +0x25700170, 0x570857c0, 0xf0617418, 0x01d76625, 0x014cc17f, 0x5fc417b2, +0x00000620, 0x00000000, 0x00070012, 0x807c121f, 0x87c020f2, 0xbc003700, +0xc843f210, 0x003f000f, 0x43f030fc, 0x3f200fc6, 0xf000fc00, 0x098fe003, +0x007c003f, 0x4bc12172, 0x00000c00, 0x00000000, 0x00270810, 0x036c05d3, +0x60f049f0, 0x4c808b44, 0xc0093002, 0x00930060, 0x08b0020c, 0x830020c4, +0xf3024c00, 0x1026c808, 0x825c009f, 0x43c00931, 0x00000c20, 0x00000000, +0x42a62001, 0x02450791, 0x246829d0, 0x44009106, 0x44091002, 0x40911024, +0x0910025d, 0x91102440, 0xd0024400, 0x10a54009, 0x0254009d, 0x07412910, +0x00000800, 0x00000000, 0x0434a018, 0x02440091, 0x340709d0, 0xc4009980, +0x500a1802, 0x00b0023c, 0x0b1002e4, 0xb1803c40, 0xd002c400, 0x102c400b, +0x021400bd, 0x63420918, 0x00000200, 0x00000000, 0x12202010, 0x23040881, +0xa04088d0, 0x8402a101, 0x402a140a, 0x02a150a8, 0x2a140aa4, 0xa100a858, +0xd00a8502, 0x08a8502a, 0x120400ad, 0x43408818, 0x00000080, 0x00000000, +0xa586b01d, 0x584c1613, 0x444020f0, 0x4d001b00, 0xc8013100, 0x40132004, +0x0132006e, 0x534004c0, 0xf0004c80, 0x0016c401, 0x2c5c8a2f, 0x77c16132, +0x00000ac0, 0x00000000, 0x012fb819, 0x127c049f, 0x2fc069f0, 0x7c029f22, +0xc029f00a, 0x029f00a7, 0x2d700a5e, 0x9f80a7c0, 0xf08a7c02, 0x00a7e429, +0x267c039f, 0x67c04bf4, 0x00000e60, 0x00000000, 0x012fa018, 0x32fc06bf, +0xa4c429c0, 0x7c049f04, 0xc0093022, 0x009b0027, 0x29f00a6c, 0x9f0227c0, +0x30027c02, 0x05a4c029, 0x5a7c009f, 0x63c00bb5, 0x00000e00, 0x00000000, +0x0503081c, 0x08344657, 0x84aa61d0, 0x74001d00, 0x404112a8, 0x041d2107, +0x00d0804e, 0x17128740, 0x10107400, 0x11ccc001, 0x8864141d, 0x73404110, +0x00000c20, 0x00000000, 0x0323a010, 0x1a34028d, 0x284888d0, 0xb016ad20, +0x452a9412, 0x12ad04ab, 0x2ad00ab4, 0xad012b40, 0x544ab402, 0x002a402a, +0x1234028d, 0x43413890, 0x00000e80, 0x00000000, 0x0025a818, 0x42740895, +0x264809d1, 0xe486bd08, 0x40af910a, 0x30bd0c2f, 0x0bd002f4, 0xb50c2e00, +0x5002b400, 0x002e522a, 0x0274069d, 0x63400910, 0x00000620, 0x00000000, +0x04a78805, 0x0a7c029f, 0x245009f2, 0x7c419f28, 0xc2099026, 0x039b00e7, +0x19d04676, 0x9f00e784, 0x706e7c0e, 0x26e6c099, 0x027c009f, 0x174219b0, +0x00000e20, 0x00000000, 0x01658014, 0x927c0097, 0x25cc0cf0, 0x3c019f01, +0xc0197206, 0x089e3227, 0x89f01248, 0x9f2227c8, 0xb0067800, 0x2065c019, +0x026c009f, 0x53c159f0, 0x00000600, 0x00000000, 0x01850814, 0x0c7c1013, +0x0fc20134, 0xcc023f20, 0xc423f108, 0x4037000d, 0x033000ec, 0x37000dc0, +0x3008cc00, 0x008cc023, 0x000c0013, 0x52c00131, 0x00000420, 0x00000000, +0x00dca014, 0x91f40371, 0x17e01510, 0x44805d00, 0x4005d001, 0x00510014, +0x0510095c, 0x5d101550, 0x10015400, 0x08154405, 0x01440055, 0x50501714, +0x00000200, 0x00000000, 0x80f2a014, 0x073403c1, 0x33440c90, 0x0620cd00, +0x480c9803, 0x24c90030, 0x0c101b04, 0xcd003062, 0x10030400, 0x0030600c, +0x030400c0, 0x50401c10, 0x00000a00, 0x00000000, 0x80d88005, 0x06b41061, +0x09410e10, 0x86012d00, 0x4012d000, 0x01210048, 0x121284a4, 0x2d100860, +0x10009401, 0x01480002, 0x23040de1, 0x14411e10, 0x00000200, 0x00000000, +0x40681015, 0x85bc81e3, 0x4b401e30, 0x8d01cf00, 0xd01cb187, 0x01c74061, +0x1e34070d, 0x470078c0, 0x34070d01, 0x40d0d01c, 0x878d0dc3, 0x54c01c32, +0x00000040, 0x00000000, 0x1005b810, 0x007c00df, 0x37c501e1, 0x7c201f00, +0xc401d800, 0x001f1017, 0x01f0005c, 0x9f0007c0, 0xb0006c00, 0x0067c001, +0x0b7c04df, 0x41c007f0, 0x00000660, 0x00000000, 0x007fa000, 0xa7cc29ff, +0x7fc49ff8, 0xfc81f362, 0xc21ff087, 0x697f827e, 0x1ff005dc, 0xf3007ec0, +0xf007fc01, 0x013fc01f, 0x0fcc01f3, 0x00c01330, 0x00000e00, 0x00000000, +0x00398815, 0x32ac006d, 0x0f428e70, 0xb4002b00, 0x4803d080, 0x0cad1308, +0x02d02286, 0x21048f40, 0xf000b400, 0x040fc102, 0x039400f1, 0x54400610, +0x00000620, 0x00000000, 0x00190000, 0x01a4800d, 0x0b400ed0, 0xb400e108, +0x400ed063, 0x606d2429, 0x0ed00184, 0x61003b41, 0xd003b418, 0x005b400e, +0x238400e1, 0x00400a10, 0x00000400, 0x00000000, 0x00932804, 0xb804040d, +0xb7420050, 0x300b094a, 0x52a0d00c, 0x088d01d0, 0x30d08a04, 0x8140c244, +0x506c3401, 0x00e54030, 0x031426c1, 0x10400411, 0x00000c20, 0x00000000, +0x0035a815, 0x0f65049e, 0x77c205f0, 0x70401320, 0xc231d004, 0x0b9f2096, +0x21f00244, 0x93008780, 0xd2047c83, 0x40ef40a1, 0x03cc06f3, 0x54d00d34, +0x00000620, 0x00000000, 0x0c170001, 0x033c001f, 0x07c20570, 0x7c42d708, +0xc00df04b, 0x405f00a7, 0x6df0e169, 0x5f0137c0, 0xf0037010, 0x0417c02d, +0x033c00df, 0x07c00bf3, 0x00000c00, 0x00000000, 0x006b0880, 0x13dc05f3, +0x0fc00731, 0xec303304, 0xc323f208, 0x02bf000f, 0x23f006fc, 0x3f004fc0, +0xf088fc02, 0x004fc023, 0x035c00f3, 0x03c00f70, 0x00000c22, 0x00000000, +0x10062081, 0x134401d1, 0x3744b151, 0x7400d740, 0x401dd007, 0x01590037, +0x3dd02574, 0xdd02f6e0, 0x920f7401, 0x027f421d, 0x037c00d5, 0x07420150, +0x00000802, 0x00000000, 0x02248001, 0x00548081, 0x37482190, 0x74181900, +0x4001d040, 0x001d0417, 0x21d00064, 0x9d000740, 0xd0887400, 0x28274101, +0x034400c9, 0x07421150, 0x00000200, 0x00000000, 0x40202010, 0x00040001, +0x03440011, 0x2600c100, 0x440c9103, 0x00c92823, 0x0cd20334, 0x4d183144, +0x90033400, 0x0013400c, 0x032000cd, 0x43400010, 0x00000080, 0x00000000, +0x0006b000, 0x005c2013, 0x07c00134, 0x74001b40, 0xc001f000, 0x000f0287, +0x01f1007c, 0x1f000740, 0xf0007800, 0x0007c001, 0x03dc00fb, 0x03c00870, +0x00000ac0, 0x00000000, 0x000fb805, 0x00fc403f, 0x3fc003f0, 0xfc00ff00, +0xc00ff203, 0x00fb007f, 0x0ff003fc, 0xff003ec0, 0xf203fc00, 0x003fc00f, +0x03fc00f7, 0x17c003f0, 0x00000e60, 0x00000000, 0x203fa003, 0x03fc00f1, +0x3ec80fb2, 0xdc00dd00, 0x880ff203, 0x80dd0035, 0x0ff203f8, 0xff203f40, +0x9001fc80, 0x001cc04b, 0x00cc003f, 0x0cc007b4, 0x00000e00, 0x00000000, +0x00050801, 0x00740017, 0x07400170, 0x76801d00, 0x00019000, 0x001c0007, +0x01d10074, 0x0d200740, 0x10017454, 0x801442e9, 0x137d00dd, 0x04510510, +0x00000c20, 0x00000000, 0x00332011, 0x0714008d, 0x73481c50, 0x2601cd20, +0x481c9007, 0x41cd1071, 0x1cd00134, 0xcd207148, 0x50003601, 0x00115908, +0x4014800d, 0x445684d0, 0x00000e80, 0x00000000, 0x0805a807, 0x8074005d, +0x07420152, 0x70001d00, 0x4001d000, 0x201d0807, 0x01d00674, 0x1d200740, +0x10817400, 0x00154019, 0x835440dd, 0x0c4005d0, 0x00000620, 0x00000000, +0x00a7a802, 0x027c005b, 0x26c009f0, 0x7e009f00, 0xc009d002, 0x009f0025, +0x09f0037c, 0x9f0025c2, 0x308d7c20, 0x183dc119, 0x00d4003d, 0x08c425f0, +0x00000e20, 0x00000000, 0x201d8003, 0x01fc80b3, 0x1fc007f0, 0xfc007f04, +0xc807b001, 0x007f841f, 0x07f000fc, 0x7f001fc0, 0xf4156000, 0x003ec008, +0x03bc00ef, 0x1fc00630, 0x00000600, 0x00000000, 0x80250802, 0x425c101f, +0x27c28970, 0x7c049f00, 0xc009b002, 0x009f0025, 0x09f0017c, 0x9b8427c0, +0xf0014d00, 0x0134c04d, 0x005c001f, 0x08c10374, 0x00000420, 0x00000000, +0x00948013, 0x197412dd, 0x1640a570, 0x74045d01, 0x4005f105, 0x015d1097, +0xa5d02e74, 0x5d04d740, 0xb003442a, 0x00f4553d, 0x836c40dd, 0x4c413530, +0x00000200, 0x00000000, 0x0412a007, 0x093403c9, 0x13402450, 0x34004d80, +0x4044d035, 0x0d4d0013, 0x24988224, 0x4d009340, 0x10016400, 0x1291480c, +0x0014200d, 0x1c500450, 0x00000a00, 0x00000000, 0x02688004, 0x06b4012d, +0x6a401a50, 0xb401ad00, 0x411ad006, 0x01ad006b, 0x9ad065b4, 0xad106b01, +0x9005a401, 0x025a601e, 0x072409cd, 0x10409411, 0x00000200, 0x00000000, +0x04101012, 0x011c008d, 0x13c00470, 0x3c014f10, 0xc004b011, 0x094d0751, +0x04b0143c, 0x49001340, 0x30192c20, 0x0210c484, 0x001c080f, 0x48c02470, +0x00000040, 0x00000000, 0x282cb802, 0x02fc007f, 0x2e400bf8, 0x7c08bf00, +0xc00b7002, 0x089f0027, 0x0be103fc, 0xbf1027c2, 0xf001dc09, 0x8a1dc08d, +0x037c08df, 0x0bc00770, 0x00000660, 0x00000000, 0x4007a815, 0x007c8053, +0x44d00124, 0x4d001340, 0x9001f200, 0x801f0004, 0x0124066c, 0x1f0004d0, +0xb0017c00, 0x0037c001, 0x044d0011, 0x54c00570, 0x00000e00, 0x00000000, +0x003d9812, 0x03de00a3, 0x3c400f38, 0x8400fb00, 0xc00fd003, 0x80ed0038, +0x0f1001f6, 0xed203840, 0x1003b400, 0x503b4802, 0x038400c1, 0x484806d0, +0x00000620, 0x00000000, 0x00490003, 0x04b4412d, 0x4b4012d0, 0xb4012900, +0x44121004, 0x012d0049, 0x129004b4, 0x25804848, 0x9805f481, 0x807b401e, +0x00840121, 0x0d401850, 0x00000400, 0x00000000, 0x0a730012, 0x031402c1, +0x30600c50, 0x1400c100, 0x400cd003, 0x00cd0031, 0x0c900334, 0xcd203040, +0x10233400, 0x89b3400c, 0x034400c1, 0x495014d0, 0x00000c20, 0x00000000, +0x0055a017, 0x01760253, 0x14c00570, 0x5c205900, 0xc0057001, 0x205f2815, +0x05b00164, 0x7f0014c0, 0xb009bc20, 0x0097ec17, 0x014c0053, 0x5dc07770, +0x00000620, 0x00000000, 0x008b0012, 0x00dc1037, 0x8fc02332, 0xe0003f00, +0xc002f008, 0x022e200c, 0x037200fc, 0x3f900fc8, 0xf0047c00, 0x0003e081, +0x00fc003f, 0x4ac101f2, 0x00000c00, 0x00000000, 0x00270010, 0x022c0083, +0x64c00830, 0x0c018f00, 0xc0197082, 0x009f0026, 0x08f02648, 0x9b0024c0, +0x70827c85, 0x2025c928, 0x024c0493, 0x40c018f0, 0x00000c20, 0x00000000, +0x50a60005, 0x025d0091, 0x66d52914, 0x45009d02, 0x5009144a, 0x029d0024, +0x29d00245, 0x9c10a450, 0x10827401, 0x01678109, 0x02540081, 0x04101914, +0x00000800, 0x00000000, 0x0034a01c, 0x02c400b1, 0x2d400b10, 0xc408bd00, +0x408b9002, 0x10bd002c, 0x0bda02c4, 0xbd00ac42, 0x50027420, 0x02264409, +0x02c410b0, 0x60408910, 0x00000200, 0x00000000, 0x00b80010, 0x0aa402a1, +0xab402a18, 0x8482ad28, 0x422a500a, 0x02ad20a8, 0x2ad80a94, 0xad90a840, +0x125a7408, 0x112341c8, 0x329404a1, 0x40509810, 0x00000080, 0x00000000, +0x00063019, 0x004c2013, 0x15c00130, 0x4c001f00, 0xc0017000, 0x401f0006, +0x05f00042, 0x3b0004c0, 0x70107c02, 0x4045c061, 0x0ccc0113, 0x74d16170, +0x00000ac0, 0x00000000, 0x00a7b819, 0x0a5c029f, 0xa4c029f0, 0x7c029f00, +0xc029300a, 0x029f00a7, 0x29f08a6c, 0x9f00a7c0, 0xf202bc06, 0x2221c04f, +0x327c089f, 0x67c04b70, 0x00000e60, 0x00000000, 0x04a7a818, 0x034c049b, +0xb5c14df0, 0x5404d324, 0x408df213, 0x20d32434, 0x0d30426c, 0xf32437c0, +0x301a4412, 0x016cc00b, 0x260c01b9, 0x63c06b30, 0x00000e00, 0x00000000, +0x41871818, 0x384c0615, 0x076061d0, 0x74021140, 0x50a11008, 0x0a114084, +0xa1b41845, 0x15008f44, 0x50284526, 0x02844861, 0x086c4211, 0x71ca6010, +0x00000c20, 0x00000000, 0x802ba010, 0x428512a5, 0x29490ad0, 0x8412a110, +0x644a144a, 0x54a12d28, 0x4a924294, 0x8d052b61, 0x10120404, 0x08214928, +0x1a040289, 0x4340c858, 0x00000e80, 0x00000000, 0x0025a018, 0x02c600fd, +0x2f600bd2, 0xf680f100, 0x600f1a02, 0x00b1802c, 0x0b1003d4, 0xdd002f48, +0x54024400, 0x00254019, 0x02640091, 0x61402950, 0x00000620, 0x00000000, +0x00278005, 0x024e0893, 0x25c009f0, 0x5c409100, 0x40095002, 0x00931024, +0x09901a4c, 0x9f1027c0, 0x34424c00, 0x00255038, 0x024c009b, 0x17c8197c, +0x00000e20, 0x00000000, 0x00218010, 0x027c8093, 0x23e209f0, 0x7c409f00, +0xe4087002, 0x008f0823, 0x09f0020c, 0x836027ca, 0xf4227c00, 0x0026c009, +0x027c009f, 0x53c059bc, 0x00000600, 0x00000000, 0x100d0014, 0x00dc0837, +0x0fcc03f0, 0xfe003f08, 0x8003e000, 0x8037080f, 0x03f200cc, 0x132a0fc8, +0x30007c00, 0x0241c021, 0x001c101f, 0x50c18130, 0x00000420, 0x00000000, +0x0014a014, 0x017c005d, 0x15c115d1, 0x74005d00, 0x4005d101, 0x80511017, +0x05d00144, 0x71405dc0, 0xb0817420, 0x009c4027, 0x01d4836d, 0x50503715, +0x00000200, 0x00000000, 0x80328014, 0x4b3401dd, 0x32400cd0, 0x7480cd88, +0x400c9103, 0x00c9d4b1, 0x0cd80311, 0xc5003720, 0x90037400, 0x087060fc, +0x031508cd, 0x50401cd0, 0x00000a00, 0x00000000, 0x08088005, 0x08b4002d, +0x0b400290, 0xb4002d00, 0x40029200, 0x8029200a, 0x02d00094, 0xec200100, +0x9033b400, 0x0838104c, 0x139400cd, 0x14481e98, 0x00000200, 0x00000000, +0x00581015, 0x879c81ef, 0x4bc416f0, 0xb401af00, 0xc01af085, 0x0179007f, +0x16f3068c, 0xe7005bc0, 0xb017fc21, 0x0079c43a, 0x0f9c012f, 0x44c612b0, +0x00000040, 0x00000000, 0x9024b810, 0x005c401f, 0x35c009f0, 0x7c005f20, +0xc005f102, 0x80930807, 0x09f20164, 0xd3002780, 0x74837c00, 0x0035c01d, +0x036c001f, 0x43c00373, 0x00000660, 0x00000000, 0x013fa000, 0x24ce49bf, +0x7cc01b30, 0xfc89f300, 0x409f2027, 0x01fb225f, 0x1d3027f8, 0xff206dc0, +0x3007c801, 0x0a7cca9f, 0x0fcc817f, 0x00c09f20, 0x00000e00, 0x00000000, +0x02098815, 0x238c0867, 0x08040670, 0x84082100, 0xc28270b4, 0x102b146b, +0x5230a00c, 0xe1061840, 0xb043ec00, 0x021ac0ce, 0x2384906d, 0x5440ce10, +0x00000600, 0x00000000, 0x02190000, 0x0004808d, 0x03400051, 0x04488100, +0x400adb21, 0x08611113, 0x44922234, 0xc1004260, 0x91838408, 0x8038460a, +0x038400cd, 0x01402210, 0x00000420, 0x00000000, 0x00a30804, 0x0b050a4d, +0xb2502c54, 0x05624148, 0x0024142a, 0x238140e1, 0x28140904, 0xd550b350, +0x10034511, 0x0032512d, 0x070405cd, 0x00420114, 0x00000c20, 0x00000000, +0x03e58815, 0x3f4e0a5f, 0xf6c0fd00, 0x4c0f5303, 0x44f5902a, 0x819122e7, +0xf9823d7c, 0xdf02f7c0, 0x8c03c404, 0x0034c00f, 0x1bc403dd, 0x45400d34, +0x00000620, 0x00000000, 0x10170001, 0x005c0297, 0x05c00130, 0x7c029f08, +0xc029e081, 0x105f0017, 0x05700a70, 0x530004c0, 0xf0037c00, 0x00b5c00d, +0x137c82df, 0x07c817f0, 0x00000c00, 0x00000000, 0x008f0880, 0x03cc4073, +0x0fc00730, 0xce003300, 0xc003f200, 0x833b00ec, 0x033000cc, 0xb3801cca, +0x2443cc02, 0x007d0219, 0x03fc00bf, 0x00c00d30, 0x00000c22, 0x00000000, +0x00362081, 0x007c0291, 0x35400970, 0x4602d100, 0x422d3003, 0x01d10055, +0x0d100b44, 0x119025c2, 0x10035c00, 0x00a440cd, 0x0334129d, 0x15400110, +0x00000802, 0x00000000, 0x0020a001, 0x80040009, 0x30400810, 0x04004100, +0x40041222, 0x00858000, 0x88100134, 0xd5082240, 0x50034420, 0x06354009, +0x037400d9, 0x04401d10, 0x00000200, 0x00000000, 0x00102010, 0x032480c9, +0x00400490, 0x04408100, 0x44085081, 0x00450330, 0x44110215, 0x40201008, +0x58030480, 0x00316804, 0x033000c9, 0x51401c10, 0x00000080, 0x00000000, +0x4006b000, 0x004d0019, 0x05d00134, 0x450a1340, 0x50017404, 0x02174044, +0x1134004c, 0x924004d0, 0x70034d00, 0x0035c00d, 0x03be40db, 0x00c00130, +0x00000a80, 0x00000000, 0x203fb805, 0x03dc00f7, 0x3fc00f70, 0x7c81ff00, +0xc20f3023, 0x95fb2277, 0x8ff957ec, 0x37003dc8, 0xb803dc80, 0x003ec00f, +0x03fc00ff, 0x17c003f0, 0x00000e20, 0x00000000, 0x823fa003, 0x20ec002f, +0x3bc08fd2, 0xf000ff00, 0xc20ff203, 0x80f9003f, 0x0fc0038c, 0xf7003fc0, +0xf023fc00, 0x003fc00f, 0x13cc00e3, 0x0cc54b90, 0x00000e00, 0x00000000, +0x90b30801, 0x10440017, 0x074041d0, 0x74001308, 0x4001d000, 0x80113007, +0x01d10074, 0x17200740, 0xd0107440, 0x40374001, 0x2bd444f1, 0x054069b0, +0x00000c20, 0x00000000, 0x0233a011, 0x1026214d, 0x314040d0, 0x3600cd00, +0x400cd003, 0x00c91031, 0x0cd00316, 0x4d803340, 0xd0033600, 0x0032400c, +0x030410c1, 0x44410890, 0x00000e80, 0x00000000, 0x0035a803, 0x0044011d, +0x07400dd0, 0x64001500, 0x4001d000, 0x00110007, 0x01d90874, 0x9d800640, +0xd0006600, 0x00374001, 0x035400d1, 0x0d400990, 0x00000620, 0x00000000, +0x0837a802, 0x446c808d, 0x27c00df0, 0x7c001f01, 0xc009f001, 0xa09b0021, +0x49f00a5c, 0x9f0007c0, 0xd0127c00, 0x0037c049, 0x034c40d3, 0x08c089a1, +0x00000e20, 0x00000000, 0x043d8007, 0x25fc00b5, 0x1fc003e0, 0xfc00f300, +0xc207f00a, 0x407f141f, 0x07f001fc, 0x76003fc0, 0xf001f800, 0x003fc007, +0x03fc20ff, 0x1ec00bf1, 0x00000600, 0x00000000, 0x00350802, 0x0a4c805b, +0x27c011f0, 0x7c101700, 0xc009f001, 0x00971026, 0x0930425c, 0x1b0307c0, +0xf0025c00, 0x0837c609, 0x034c40df, 0x0bc019f1, 0x00000420, 0x00000000, +0x003ca013, 0x07441011, 0x17400dd0, 0x7012d100, 0x4205d006, 0x035d1897, +0x05b01974, 0xd810f740, 0xd0017400, 0x007f4405, 0x03c420fd, 0x4f400992, +0x00000200, 0x00000000, 0x0032a007, 0x03050101, 0x13400c90, 0x3403c504, +0x4004d006, 0x0c450213, 0x04900934, 0xc800f360, 0xd0011400, 0x84344004, +0x034481dd, 0x1f400958, 0x00000a00, 0x00000000, 0x24788004, 0x56840131, +0x6b4012d1, 0xb4012188, 0x401ad045, 0x01ad016b, 0x9a9006b4, 0x29104b44, +0xd006b421, 0x047b401a, 0x030401ed, 0x13403a18, 0x00000200, 0x00000000, +0x00301012, 0x030cc003, 0x13c080b8, 0x3c00c500, 0xc044f002, 0x84470013, +0x04b1213e, 0x4b1033e8, 0xf0011c00, 0x1033a804, 0x230c10cf, 0x4bc008f1, +0x00000040, 0x00000000, 0x043db802, 0x92d00877, 0x2fc20ff0, 0xfc003f00, +0xe80bf201, 0x00bf012f, 0x0974027c, 0xbf000fc2, 0xf002fc00, 0x203ec01b, +0x13fc80ff, 0x0bc00bf0, 0x00000660, 0x00000000, 0x3077a015, 0x054e001b, +0x05c00db0, 0x7c011f00, 0xc0017000, 0x00130047, 0x00f0005c, 0x931004c4, +0x31007c00, 0x3274c001, 0x1b7e44d3, 0x54c01bf0, 0x00000e00, 0x00000000, +0x21798812, 0x01840020, 0x39c00210, 0xb420ed00, 0xc00eb003, 0x00eb0033, +0x0ed0038c, 0x6b003940, 0xb0033600, 0x103ec00c, 0x33b4aaeb, 0x4ac00af0, +0x00000620, 0x00000000, 0x08710003, 0x878601b8, 0xc9401210, 0x34002500, +0x40321004, 0xc12510cb, 0x32d904b4, 0x2580c840, 0x100cb403, 0x017a4032, +0x279485ed, 0x0c401cd1, 0x00000400, 0x00000000, 0x00332812, 0x0b0404c1, +0x31000c10, 0x3401cd00, 0x400cd01b, 0x01cd0033, 0x0cd00704, 0xd981b140, +0x90037400, 0x0032420c, 0x033600cd, 0x4a5008d0, 0x00000c20, 0x00000000, +0x0015a817, 0x15cd007b, 0x55c005b4, 0x34035f10, 0xc0153009, 0x05570057, +0x14f0415c, 0x5700d0c0, 0x30057c01, 0x0014c017, 0x017c005f, 0x5cc005f0, +0x00000620, 0x00000000, 0x00870012, 0x187c021f, 0x8fc003f0, 0xf8183f00, +0xc0039208, 0x0039000d, 0x03f000dc, 0x3e200fc0, 0xf000fc00, 0x0087c003, +0x007c021a, 0x49c00170, 0x00000c00, 0x00000000, 0x00270810, 0x166c039f, +0x67c059f0, 0x4c009f80, 0xc809b006, 0x208f0067, 0x99f0064c, 0x9f0064c0, +0x30027c00, 0x00e4c009, 0x027c009f, 0x43c00934, 0x00000c20, 0x00000000, +0x01262001, 0x0e443299, 0xe74a09d0, 0x44809d06, 0x40a9300e, 0x419d2027, +0x19700e54, 0x95102548, 0x51025400, 0x20654009, 0x0274009d, 0x07400951, +0x00000800, 0x00000000, 0x1024a018, 0x0a64008d, 0x2e4009d0, 0xc400a900, +0x430f9022, 0x04fd023f, 0x0bd052c4, 0xb1292c40, 0x1012c400, 0x0024400a, +0x0234009d, 0x63400990, 0x00000200, 0x00000000, 0x07202210, 0x2204088d, +0x2a408ad0, 0x8428ad00, 0x620a1083, 0x00ad082b, 0x0a500294, 0xa5002940, +0x50029402, 0x0021402a, 0x1234088d, 0x434088d0, 0x00000080, 0x00000000, +0x1186b01d, 0x582c021d, 0x07c021f1, 0x4c021d00, 0xc001b000, 0x001f0007, +0x01f2004c, 0x1f0004c0, 0x30005c00, 0x0514c803, 0x2c7c025f, 0x77c161b0, +0x00000ac0, 0x00000000, 0x2127b819, 0x12fc04ab, 0xa7c069f0, 0x7c069f00, +0xc029f00a, 0x028d00a7, 0x29700a74, 0x9f00a3c0, 0xf05a7c02, 0x0527c029, +0x267e269f, 0x67c04b70, 0x00000e60, 0x00000000, 0x01ada018, 0x326c02a3, +0x2cc44bf0, 0xc410b300, 0xc14b3002, 0x34bf052c, 0x0b3002cc, 0x93002bc0, +0x3062fcc2, 0x04afe049, 0x224c02bf, 0x63c14f30, 0x00000e00, 0x00000000, +0x0007081c, 0x30440411, 0x06c301d0, 0x6c061b00, 0xc021b028, 0x021d1086, +0x01b2806c, 0x1b0a8740, 0xb0907c24, 0x0085c0e1, 0x1844041d, 0x73406114, +0x00000c20, 0x00000000, 0x02332010, 0x12040481, 0xa34868d0, 0x04508020, +0x40081002, 0x808d0020, 0x28590a04, 0x8d202340, 0x18122646, 0x00234108, +0x0a240685, 0x43410810, 0x00000e80, 0x00000000, 0x8025a818, 0x02440091, +0x264009d2, 0x64809920, 0x48098003, 0x009d0826, 0x09900264, 0x9d002740, +0x90825402, 0x00254009, 0x022620dd, 0x63420910, 0x00000620, 0x00000000, +0x40278805, 0x124c1193, 0x2744e9e0, 0x4c009300, 0xc4092202, 0xa09f1024, +0x09700244, 0x970027c0, 0x30026420, 0x20274009, 0x026d009f, 0x17c01930, +0x00000e20, 0x00000000, 0x04218014, 0x035c089f, 0x274009f0, 0x7c009f10, +0xc009d002, 0x409d0027, 0x09e0027c, 0x9b0027c0, 0xf0023c20, 0x0027c009, +0x025c008f, 0x53c039f0, 0x00000600, 0x00000000, 0x00040814, 0x284c001f, +0x01c80030, 0x3c000281, 0xc000f080, 0x100f2044, 0x01f02030, 0x170041c0, +0x30007c00, 0x0007c001, 0x004c041f, 0x53c01130, 0x00000420, 0x00000000, +0x089ca014, 0x2d55007d, 0xdce41511, 0xf4107594, 0x42a7d009, 0x607d909d, +0x157001f4, 0x51109d45, 0xb0015480, 0x0a1dc205, 0x01540079, 0x53400750, +0x00000200, 0x00000000, 0x80b2a014, 0x0304125d, 0xf3441c58, 0x3440c110, +0x603cd04b, 0x02cd0000, 0x1cd09334, 0xc8002040, 0x90030400, 0x00f3420c, +0x032408cd, 0x53400c90, 0x00000a00, 0x00000000, 0x00388005, 0x0714033d, +0x32402ed0, 0xb6002500, 0x610cd003, 0x00ed0019, 0x1e5003a0, 0xe9003940, +0x90039401, 0x00ab400e, 0x33b400e9, 0x17402ed0, 0x00000200, 0x00000000, +0x00781015, 0x058d08af, 0x7bc01f74, 0xbc01e100, 0xc016f007, 0x01ed0058, +0x1cf007b4, 0xcf0039c0, 0xb006dc15, 0x006bc83e, 0x172c03ef, 0x57c01eb0, +0x00000040, 0x00000000, 0x0015b810, 0x097c009f, 0x35c00d30, 0x7c001f10, +0x4205f102, 0x009f1017, 0x0df0017c, 0xd70037c0, 0xf0025c06, 0x2024c14d, +0x0b5c40cf, 0x43c00f70, 0x00000660, 0x00000000, 0x024fa000, 0x8fc901a3, +0x7bc01fb0, 0xcc09bb80, 0xc69a7007, 0x09e30068, 0x1f3007bc, 0xff006fc0, +0x3027d411, 0x027c429f, 0x2ffc05f3, 0x03c01730, 0x00000e00, 0x00000000, +0x023d8815, 0x128412a1, 0x1b408a10, 0xac08318a, 0xc0cab023, 0x086b2a1a, +0x0eb023ac, 0xef209b42, 0x1033ec00, 0x008ac80f, 0x33dc00ab, 0x5749aa10, +0x00000620, 0x00000000, 0x88090000, 0x010408b1, 0x0b400e18, 0x8400a900, +0x600ed001, 0x08e10038, 0x8e100296, 0xed023b40, 0x53028408, 0x083a440e, +0x03b404e1, 0x03400618, 0x00000400, 0x00000000, 0x00132804, 0x00042081, +0x43401810, 0x24010100, 0x401c1000, 0x01090052, 0x1c900c14, 0xc5005340, +0x58062401, 0x002641ac, 0x03140089, 0x13080810, 0x00000c20, 0x00000000, +0x4035a815, 0x134c0183, 0xb7483d30, 0x44435b04, 0xc031d048, 0x031300c4, +0x3d300754, 0xfd00f7c0, 0x740e4c03, 0x0006c03f, 0x03fc00f3, 0x57801914, +0x00000620, 0x00000000, 0x00270001, 0x0b7c029f, 0x37c0cd70, 0x7c0c1f09, +0xc0e1f011, 0x0cdf0307, 0xcdf02a64, 0xdf03b700, 0xa0327c0c, 0x0027c00d, +0x035c02cf, 0x07c807f0, 0x00000c00, 0x00000000, 0x013f0880, 0x04cc00bf, +0x7cc00af1, 0xcc006f01, 0xc0037002, 0x00330108, 0x1e3147bc, 0xe310f8c0, +0xf006cc80, 0x0024c00e, 0x038c00f1, 0x00c08b30, 0x00000c22, 0x00000000, +0x00462081, 0x0c54009d, 0x354089d0, 0x5c001d00, 0x4130d006, 0x10950005, +0x8d500064, 0xd500b540, 0xd02654a0, 0x08ed500d, 0x035402d5, 0x044025b0, +0x00000802, 0x00000000, 0x1004a001, 0x51440895, 0x26400dd8, 0x44101d00, +0x4041d990, 0x041100a4, 0x0d102974, 0xd5003440, 0xd0024400, 0x0054480d, +0x034400d5, 0x04400010, 0x00000200, 0x00000000, 0x00202210, 0x8014009d, +0x01400890, 0x34000908, 0x4000d801, 0x00450021, 0x0c508026, 0xc5003140, +0x90021440, 0x4000400c, 0x03040081, 0x40500090, 0x00000080, 0x00000000, +0x0006b000, 0x004c00b7, 0x06c009f0, 0x4c001f00, 0xc401fb80, 0x00130024, +0x0930007c, 0xe50034c0, 0xf1024c00, 0x2034c00f, 0x03cd0057, 0x00c60130, +0x00000ac0, 0x00000000, 0x080f9805, 0x00fc00bf, 0x0fc00bf0, 0x9c003f00, +0xc003d000, 0x003f002f, 0x0bf003fc, 0xf7003fc0, 0xf002fc00, 0x002fc00f, +0x03fc003f, 0x17c003f0, 0x00000e60, 0x00000000, 0x003fa003, 0x03cc00f3, +0x37c00ff0, 0xfc00f700, 0xc02df003, 0x00fb20bf, 0x2df023ec, 0xff003fc0, +0xe003b842, 0x023fc94f, 0x00ec10f3, 0x0cd08ff0, 0x00000e00, 0x00000000, +0x04050801, 0x4044101b, 0x07400150, 0x74001120, 0x4000d000, 0x14150004, +0x01d00044, 0x1d810744, 0x91004444, 0x213f4021, 0x0a4422d3, 0x0c4029d0, +0x00000c20, 0x00000000, 0x01012011, 0x133404c1, 0x21400052, 0x1400c500, +0x40485001, 0x00c10131, 0x4c120114, 0xcd051144, 0x50031404, 0x0131400c, +0x0b2481c9, 0x4c40ccd1, 0x00000e80, 0x00000000, 0x0837a803, 0x00250009, +0x17420d10, 0x74001100, 0x4005d002, 0x00058004, 0x01d08245, 0x1d002740, +0xd0004060, 0x10374001, 0x036400d1, 0x0c4009d0, 0x00000620, 0x00000000, +0x1836a882, 0x017c0051, 0x37c00df0, 0x78001401, 0xc005f003, 0x201b0123, +0x01f0027c, 0x9d0827c0, 0xf0927c04, 0x4037e429, 0x016c01db, 0x08c808d2, +0x00000e22, 0x00000000, 0x00018007, 0x0ecc83bf, 0x0fc001f0, 0x7c00ff00, +0xc40af010, 0x00df081f, 0x0ff001ec, 0x6f101fc0, 0xb281fc00, 0x003fc805, +0x03dc04ef, 0x1fc80db0, 0x00000600, 0x00000000, 0x00050802, 0x017c0053, +0x24c041f0, 0x7c081700, 0xc0c1b001, 0x00130025, 0x0130004c, 0x9d4007c0, +0x70027c00, 0x8033c029, 0x034c31db, 0x29c119d0, 0x00000420, 0x00000000, +0x0234a013, 0x82640095, 0x15413d50, 0x7400d900, 0xc00d5006, 0x00db0416, +0x0db40b54, 0x5702f660, 0x1001443a, 0x007c4077, 0x136c03db, 0x4d422dd1, +0x00000200, 0x00000000, 0x0032a007, 0x42340089, 0x14603c10, 0x2400c904, +0x402c1006, 0x00c90050, 0x0c501324, 0x41803340, 0x10010500, 0x06314044, +0x94040005, 0x5f420cd0, 0x00000a00, 0x00000000, 0x02488004, 0x05240165, +0x29401258, 0xb4012d11, 0x40125045, 0x4529022b, 0xd202a494, 0xa9254a40, +0x10069401, 0x0278401a, 0x06a449ad, 0x37401ad1, 0x00000200, 0x00000000, +0x00001012, 0x0234408b, 0x44c00052, 0x3c00c40b, 0xc0181000, 0x0dc30351, +0x5c40014c, 0x578a5740, 0x70011c08, 0x02358224, 0x230c00d7, 0x4bc20cf0, +0x00000040, 0x00000000, 0x083db802, 0x01fc0077, 0x3fc00f70, 0x7c001303, +0xc4077003, 0x201f0122, 0x83f002dc, 0xb7002ec0, 0xf4024c00, 0x02bd401b, +0x03fc00fb, 0x08c00bf0, 0x00000660, 0x00000000, 0x00752014, 0x005c0013, +0x10c40c62, 0x74001100, 0xe005f006, 0x001d0007, 0x01908274, 0x1d002642, +0x14004400, 0x2137c001, 0x016c4093, 0x54c009e0, 0x00000e00, 0x00000000, +0x0009091a, 0x038400fb, 0x2ac002d0, 0xb420e100, 0x420a2081, 0x00ed003b, +0x0e5001f4, 0xe9201840, 0x1003b400, 0x20bb601e, 0x038400bf, 0x4c500e91, +0x00000624, 0x00000000, 0x00410000, 0x04b50121, 0x484002d0, 0xb4012900, +0x40125084, 0x012d004b, 0x129004b4, 0x2d004840, 0x10048401, 0x817b4092, +0x87a6a1e1, 0x064018d0, 0x00000400, 0x00000000, 0x08312812, 0x034480c9, +0x30480cd0, 0x340dc180, 0x602c9883, 0x02dd8a73, 0xdc180334, 0xcd003050, +0x10033480, 0xa033400c, 0x030680d5, 0x4a400cd1, 0x00000c20, 0x00000000, +0x0015a837, 0x017c0052, 0x144005f0, 0x7c005b00, 0x40055001, 0x045d0017, +0x05b01174, 0x5d0116c0, 0x00114804, 0x001b2045, 0x45ec0271, 0x5e4107d0, +0x00000620, 0x00000000, 0x108f0012, 0x00fc022f, 0x0fc002f0, 0xfc003f48, +0xc1037000, 0x903f208f, 0x037208fc, 0x3b000fc0, 0xf000fc00, 0x00074003, +0x807c011f, 0x49c801b1, 0x00000400, 0x00000000, 0x02670830, 0x424c129f, +0x61e089b0, 0x4c008911, 0xc029f002, 0x00970261, 0x08300e0c, 0x970024c0, +0x30027c00, 0x4126c009, 0x025c0193, 0x41c008f0, 0x00000c20, 0x00000000, +0x04262001, 0x0254119d, 0x25400910, 0x54009d00, 0x44399042, 0x099d0027, +0x29d0166c, 0x95402540, 0x10020580, 0x01244808, 0x0244a999, 0x054029d0, +0x00000080, 0x00000000, 0x0024a018, 0x02c400bd, 0x2c400b94, 0xc404b500, +0x400bd002, 0x00b1042f, 0x0b1202f4, 0xad002845, 0x1002d400, 0x0024608b, +0x0654009d, 0x614029d8, 0x00000200, 0x00000000, 0x02282010, 0x229408ad, +0xa9480a10, 0x9600a500, 0x488ed002, 0x08a5022b, 0x0ad222a4, 0xa1802940, +0x100a8400, 0x8020408a, 0x26040489, 0x414088d8, 0x00000080, 0x00000000, +0x0086b01d, 0x084c021f, 0x054401b0, 0x4c801f10, 0xc021f100, 0x02172087, +0xa030087e, 0x1e0284c0, 0x32003c0a, 0x8502d167, 0x585c0b15, 0x75c161f0, +0x00000ac0, 0x00000000, 0x01a7b911, 0x1a7c269f, 0xa7c429f4, 0x3c029f00, +0xc069b10a, 0x069f01a7, 0x39701a4f, 0x9f00e7c0, 0xf40a6c03, 0x05278849, +0x12fc09b7, 0x66e04bf8, 0x00000e64, 0x00000000, 0x012fa018, 0x02cc00b7, +0x2f400a20, 0xf810b300, 0x4309e042, 0x80bf042c, 0x2930d2e8, 0x9320a680, +0x70527014, 0x012ec14b, 0x1afc0cb3, 0x60c06bf0, 0x00000e00, 0x00000000, +0x0097081c, 0xa86c2a01, 0x05c001b0, 0x74001b01, 0xc041d001, 0x0a100186, +0xf010895c, 0x3a02d740, 0xb0186c17, 0x46854031, 0x04740e11, 0x71484190, +0x00000c20, 0x00000000, 0x00a3a012, 0x02040085, 0xa3402858, 0x34008104, +0x4108d002, 0x00852420, 0x08500200, 0xa5012b44, 0x10422400, 0x10204148, +0x2234008d, 0x484868d2, 0x00000e80, 0x00000000, 0x0021a818, 0x02640091, +0x25610dd0, 0x76009900, 0x6089d002, 0x00810036, 0x09548214, 0xb9802f40, +0xd0024600, 0x00256001, 0x44740094, 0x61400990, 0x00000620, 0x00000000, +0x02e7a805, 0x824c6097, 0x67462971, 0x74009100, 0x4019d00a, 0x009f0024, +0x09500a44, 0x9500a7c4, 0x48026400, 0x00240a01, 0x0c7c079f, 0x14c009f1, +0x00000e20, 0x00000000, 0x00658016, 0x023c108f, 0x65c049b0, 0x7c209f06, +0xc01df102, 0x009f1027, 0x09b0027c, 0x9f0023c0, 0xb0022c00, 0x0021a100, +0x007c099b, 0x5bc809f2, 0x00000600, 0x00000000, 0x00050814, 0x2064001b, +0x02ca2170, 0x6c001f00, 0xc000f008, 0x001f0c07, 0x01f0087c, 0x3f008dc0, +0x31007c00, 0x0005c401, 0x08481013, 0x50d01130, 0x00000420, 0x00000000, +0x001ca014, 0x25d40071, 0x9c4027d0, 0xc6005d00, 0x41b51019, 0x3b7d009f, +0x05d005f4, 0x510014e0, 0x3001c400, 0x001f4003, 0x6cec0178, 0x50482710, +0x00000200, 0x00000000, 0x00322014, 0x0b2402c9, 0x72411c50, 0x0601cd80, +0x480c1143, 0x00c90007, 0x0c100704, 0xc5103344, 0x54031400, 0x0831403c, +0x033480c4, 0x51400d90, 0x00000a00, 0x00000000, 0x201a0005, 0x01a400e1, +0xb8411ed0, 0x8411ed01, 0x400e1006, 0x80ed010b, 0x1ed041a4, 0x21280840, +0x50238601, 0x411f410e, 0x273411e5, 0x14000390, 0x00000200, 0x00000000, +0x00421015, 0x07ac012b, 0xeac01e70, 0x8481ef01, 0xc8166207, 0x01ad804b, +0x167004bc, 0xf7007f42, 0x7047dc01, 0x00f9c09e, 0x07bc01f7, 0x55c016b0, +0x00000040, 0x00000000, 0x0025b810, 0x015c001f, 0x24c00df0, 0x1c008f08, +0xc1255002, 0x209f8247, 0x05d0027c, 0x37100ec0, 0x321b5c00, 0x0853c0cd, +0x436d40d9, 0x43c00870, 0x00000660, 0x00000000, 0x007fa000, 0x24dc017f, +0x7f009f72, 0xfc09bf12, 0xc41ff025, 0x013b024f, 0x1d30064c, 0xff0a7fc0, +0xf007d809, 0x0276409e, 0x275c0dd3, 0x02c01f30, 0x00000e00, 0x00000000, +0x061d8815, 0x02848061, 0x1a40c610, 0xbc01e116, 0xc382f00d, 0x101d0d43, +0x9eb06494, 0x2f030342, 0xd003841c, 0x015c408e, 0x07848dbf, 0x54408210, +0x00000620, 0x00000000, 0x00090000, 0x009400a5, 0x39600e52, 0xb400a500, +0x4006d003, 0x0869020b, 0x06110184, 0xcc003b48, 0xd003b600, 0x2138638e, +0x0b0400e5, 0x02500610, 0x00000400, 0x00000000, 0x00a72804, 0x4e041381, +0x92493410, 0x14938124, 0x4020504f, 0x024d0c81, 0x34104b14, 0x05068341, +0xd00b2412, 0x0010002c, 0x03040094, 0x10401852, 0x00000c20, 0x00000000, +0x1a67a815, 0x4a5c1297, 0xddc12570, 0x7412df00, 0x409dd14b, 0x095900ff, +0x2d120f44, 0xdd20f7c5, 0xf01ff403, 0x483e5aad, 0x0b4402d1, 0x56c01034, +0x00000620, 0x00000000, 0x30058001, 0x007c0097, 0xb6c025f4, 0x3c00cf08, +0xc00de00a, 0x004f0023, 0x0ef1813c, 0x3f000bc2, 0xf0131c00, 0x0011c00d, +0x0b6d0458, 0x07c0a1b2, 0x00000c00, 0x00000000, 0x025d0880, 0x02fc0273, +0x4fc007f0, 0xc400fd02, 0x80037009, 0x403300bf, 0x07b080cc, 0xf3003c80, +0x3003fc00, 0x011fc00f, 0x03dc20fb, 0x01d001f0, 0x00000c22, 0x00000000, +0x00760081, 0x24748051, 0xa5c8b5d0, 0x54009d04, 0x40115084, 0x001500f7, +0x07508254, 0x35000d40, 0x50037400, 0x0027440d, 0x1b440351, 0x054041f1, +0x00000800, 0x00000000, 0x0024a001, 0x01740011, 0x07000dd0, 0x54409d00, +0x448d1100, 0x02910017, 0x0d100264, 0xd1003540, 0x90037400, 0x00b7401d, +0x13541155, 0x054019d0, 0x00000200, 0x00000000, 0x80002010, 0x03248001, +0x01440490, 0x1404c900, 0x40004010, 0x00850002, 0x4c501014, 0x05900142, +0xd0833600, 0x0113621c, 0x13040481, 0x414000d1, 0x00000080, 0x00000000, +0x0016b000, 0x013c00c3, 0x87c00df0, 0x5c019f02, 0xc0a16014, 0x8ad323f7, +0x1531052c, 0xd10235c0, 0xb003fc00, 0x8557e00d, 0x075c1517, 0x01c001f2, +0x00000ac0, 0x00000000, 0x003fb825, 0x03fc00fd, 0x49c003f0, 0x7c089f01, +0xc013e320, 0x45ff8277, 0x977023fc, 0x3f014fc1, 0x7183fe00, 0x802fc00f, +0x0bfc00b7, 0x16c00b70, 0x00000e60, 0x00000000, 0x030fa003, 0x02ec00fb, +0x0cc043b0, 0xfc202300, 0xc00fb013, 0x00ff303f, 0x0ff103cc, 0xff002fc0, +0xf003ed80, 0x400ec80f, 0x238c0021, 0x0cc00731, 0x00000e00, 0x00000000, +0x01970801, 0x80c500f5, 0x26502510, 0xc4009140, 0x40057003, 0x401d3037, +0x0dd10344, 0xfd002f40, 0xd0034400, 0x0007400f, 0x03ec8a93, 0x04490512, +0x00000c20, 0x00000000, 0x2083a011, 0x023540c1, 0x32408499, 0x2400d100, +0x400cd18b, 0x001d0033, 0x0cd00334, 0xcd002140, 0xd0030400, 0x0023400c, +0x131555d1, 0x56408410, 0x00000e80, 0x00000000, 0x0031a803, 0x446400d5, +0x36402d10, 0x6400d000, 0x400d5003, 0x045d0037, 0x0dd00364, 0xdd002744, +0xd0134400, 0x0427400d, 0x037500d1, 0x1e418500, 0x00000620, 0x00000000, +0x4087a802, 0x077c40d3, 0x14c001b0, 0x7c011300, 0xc009b003, 0x331f0037, +0x0df0076c, 0xdf0027c2, 0xf0064c00, 0x4087c00d, 0x035c80d3, 0x02d03d34, +0x00000e20, 0x00000000, 0x20dd8007, 0x038c20f3, 0x3dc00f70, 0xdc98bf00, +0xc007f003, 0x00bf04ff, 0x9ff08bcd, 0xef082fc8, 0xd186fc00, 0x008f420f, +0x43e800f3, 0x0dc01de0, 0x00000602, 0x00000000, 0x00050802, 0x0f6c00d3, +0x34c025f0, 0x5c08df28, 0xc06d3023, 0x32df2034, 0x0d30037c, 0xd30026c0, +0x30026c00, 0x0230d00d, 0x23cc80df, 0x09d02f34, 0x00000420, 0x00000000, +0x84f4a013, 0x056407f1, 0xb4408fd0, 0xc402c104, 0x402db003, 0x01cd00f4, +0x0d102370, 0xff00ac40, 0x100e4450, 0x026c440f, 0x0b8400cb, 0x4c400d30, +0x00000200, 0x00000000, 0x2162a007, 0x020450d1, 0x044011d0, 0x14014511, +0x448c914b, 0x890d1032, 0xc890ab34, 0xc511a044, 0x916f0447, 0x40c0444c, +0x8f04a68d, 0x1d4005d0, 0x00000a00, 0x00000000, 0x20208004, 0x848400e1, +0x6c50d6d0, 0x8401f900, 0x505e1417, 0x09ed006a, 0x1a9407b4, 0xe4426810, +0x94878501, 0x600a001e, 0x078409b9, 0x18601e50, 0x00000200, 0x00000000, +0x02001012, 0x222c08d1, 0x30c14df0, 0x5c00c708, 0xc00d9003, 0x088f0432, +0x8d90137c, 0xd40424c0, 0xb0034c00, 0x0004810d, 0x030c08cf, 0x49c000f8, +0x00000040, 0x00000000, 0x012cb802, 0x00fc14ff, 0x3bc24ff0, 0xfc00f702, +0xc44ff013, 0x28ff003d, 0x8f70037c, 0xfb082dc0, 0x79237408, 0x00adc00f, +0x23fc28ff, 0x0bc087b0, 0x00000660, 0x00000000, 0x3037a015, 0x926c16df, +0x14c159f0, 0x4d005f00, 0xd0193477, 0x01534034, 0x09500749, 0xd30927c0, +0x38075c17, 0x000700cd, 0x33cc40cb, 0x44c01d30, 0x00000e00, 0x00000000, +0x00398812, 0xc08c04ed, 0x38402ed0, 0x8620ff00, 0x400e1013, 0x00e10038, +0x0a100bac, 0xe5832340, 0x10038486, 0x400b408e, 0x438400e1, 0x48400e10, +0x00000620, 0x00000000, 0x00590003, 0x07b401e5, 0x7a0c1e50, 0x8401ed04, +0x403e5017, 0x01f18078, 0x1e1057a4, 0xe1036b4a, 0x10078441, 0x025b401e, +0x078491f1, 0x0c400818, 0x00000402, 0x00000000, 0x22332812, 0x095400cd, +0x72700cd0, 0x0400c500, 0x400c1003, 0x01c10234, 0x0c140704, 0xc5002340, +0x14230500, 0x0233400c, 0x030440c1, 0x48400c10, 0x00000c20, 0x00000000, +0x005da817, 0x65fc0057, 0x5ec22570, 0x4c007d00, 0xc0071001, 0x03730014, +0x05300064, 0x538017c0, 0x10054400, 0x0057c005, 0x014c0273, 0x5cd02734, +0x00000620, 0x00000000, 0x00070012, 0x004c001f, 0x05c301d0, 0x7c001f00, +0xc021f008, 0x1a1f0087, 0x01f0886c, 0x1fc807c6, 0xf0087c02, 0x0007c001, +0x003d0317, 0x4bc100f0, 0x00000c00, 0x00000000, 0x00270810, 0x166c0097, +0x27c00d70, 0x4c009f11, 0xc439f186, 0x429300e7, 0x39f0147c, 0x930024c0, +0xf0867481, 0x0130d009, 0x024c8181, 0x40c00934, 0x00000c20, 0x00000000, +0x00a62001, 0x0274009d, 0x27404910, 0x45009d19, 0x40099002, 0x03914027, +0x39d21274, 0x90402458, 0xd0027400, 0x08a40039, 0x0a540895, 0x04401930, +0x00000800, 0x00000000, 0x00b4a018, 0x0244108d, 0x27408950, 0x44068d10, +0x400d5022, 0x00910027, 0x08d00274, 0x990a2041, 0xd0225408, 0x00244088, +0x0a44029d, 0x60401910, 0x00000200, 0x00000000, 0x02202010, 0x2214088d, +0xa3402810, 0x04028d00, 0x402cd21b, 0x028100b3, 0x28d20a34, 0x8900b040, +0xd00a3416, 0x80b06428, 0x0214048d, 0x40408810, 0x00000080, 0x00000000, +0x3586b01d, 0x586c160f, 0x07c14070, 0x0c001f00, 0xc000f004, 0x20530003, +0x01f0503c, 0x480004c0, 0xf0011c04, 0x0004c405, 0xa84c0b1f, 0x74c36130, +0x00000ac0, 0x00000000, 0x012fb819, 0x13fc049f, 0x2fc809f2, 0x7c00bf00, +0xc00b9822, 0x80af002f, 0x0bf200fc, 0x9700afc0, 0xf802fc00, 0x00b3c029, +0x0e3c09b7, 0x67c04b70, 0x00000e60, 0x00000000, 0x00afa018, 0x1acc02bb, +0x27c189f0, 0x7c829b00, 0xc2090032, 0x029f2022, 0x29308674, 0x9701a3c0, +0xf00a7c00, 0x012cc089, 0x62cc06a1, 0x67440b30, 0x00000e00, 0x00000000, +0x4007081c, 0x10450411, 0x070821d0, 0x74801321, 0xc9457208, 0x800d2105, +0x01305064, 0x11208748, 0xd2107484, 0x0582c043, 0x08548015, 0x73404154, +0x00000c22, 0x00000000, 0x00a3a010, 0x1a140681, 0x2f004ad0, 0xb400a904, +0x400a5032, 0x00ad042b, 0x0a9082f4, 0xa5022b40, 0xd002a410, 0x4220402a, +0x12042681, 0x43612c90, 0x00000e80, 0x00000000, 0x00a5a818, 0x12748091, +0x2f4909d0, 0xf480b104, 0x488b5082, 0x42bd002d, 0x4b1202e4, 0xb5002f40, +0xd002f400, 0x8036400b, 0x02540295, 0x634009d8, 0x00000620, 0x00000000, +0x00a7a805, 0x865d0093, 0xe3c039f0, 0x7c1b9b00, 0xc0393802, 0x029f8027, +0x09b0023c, 0x95002744, 0xf0027c00, 0x02a4c009, 0x024c0292, 0x17c059b2, +0x00000e20, 0x00000000, 0x00258014, 0x024c1097, 0x27c089f0, 0x7c019b05, +0xc009f002, 0x049f00e5, 0x0970266c, 0x9b0023c0, 0xf0027c00, 0x10630009, +0x0238049f, 0x53c03930, 0x00000600, 0x00000000, 0x01850814, 0x006c0007, +0x8cc20331, 0xfc023d00, 0x60032000, 0x0033410f, 0x03f080cc, 0x3a040fc8, +0xf000fc08, 0x0004c002, 0x904c0913, 0x53c00130, 0x00000420, 0x00000000, +0x015ca014, 0x0545227d, 0xd4500550, 0x74065d00, 0xc015502d, 0x00510817, +0x75f08140, 0x51001740, 0xd0094c0b, 0x001d4007, 0x0dc4027f, 0x53400510, +0x00000200, 0x00000000, 0x0076a014, 0x03040245, 0x78480e10, 0xb404ed80, +0x404e9003, 0x52e1027b, 0x3ed13794, 0xe000bb4b, 0xd06bb402, 0x0132400e, +0x0704001d, 0x53400c90, 0x00000a00, 0x00000000, 0x10388005, 0x0f840a6d, +0x38401ed0, 0x3400e904, 0x410e500f, 0x01e1007c, 0x0e500795, 0xe1043f40, +0xd0038400, 0x002b610c, 0x6384002d, 0x17400e90, 0x00000200, 0x00000000, +0x00581015, 0x06c601c7, 0x78c01f30, 0xbc41ed00, 0x401eb007, 0x01e3007b, +0x1ed0039c, 0xea407bc8, 0xf007bc41, 0xa07ac01e, 0x17cc816f, 0x57c81eb0, +0x00000040, 0x00000000, 0xa015b810, 0x825400df, 0x07c00172, 0x7c001f08, +0xc001c000, 0x005f0007, 0x01f0006c, 0x170007c0, 0xe0006c00, 0x882d4201, +0x017d005f, 0x43c00f74, 0x00000660, 0x00000000, 0x027fa000, 0x26c8417f, +0x7ec0df30, 0xdc01f302, 0xc09eb007, 0x29fe027c, 0x1ff007cc, 0xf3007fc0, +0xf027dc81, 0x03dda49f, 0x07cc01f3, 0x08d01b30, 0x00000e00, 0x00000000, +0x00098815, 0xb384046d, 0x38418eb2, 0xb488e102, 0xc28e7123, 0x0cff0b3a, +0x0ef20384, 0xe1823b40, 0x7023ac08, 0x022b40ce, 0x119d00fb, 0x56c00a10, +0x00000620, 0x00000000, 0x10390200, 0x828480ed, 0x38484e50, 0x9400e100, +0x480fd003, 0x00ed203a, 0x8ed043a6, 0xc1003f40, 0xd203b400, 0x0139481e, +0x832408e9, 0x60410f50, 0x00000400, 0x00000000, 0x00c32804, 0x020400cd, +0x824010d0, 0x34000170, 0x40b05000, 0x83450202, 0x00500024, 0x01800340, +0x50000480, 0x00274200, 0x831400c9, 0x18413c5c, 0x00000c20, 0x00000000, +0x0425a815, 0x8b4c00ef, 0x34c0bc50, 0x5c00d200, 0xc03d9003, 0x069d0034, +0x0dd20768, 0xd34037c0, 0xf0037400, 0x0275c00d, 0x02ec0a0b, 0x54481d74, +0x00000620, 0x00000000, 0x04370001, 0x133c005f, 0xb5c825a0, 0x3c90df00, +0xc84d700b, 0x909f2037, 0x2df20b5d, 0xdf20b7c8, 0xf20b7c80, 0x80e7c80d, +0x037c001f, 0x27c00d90, 0x00000c00, 0x00000000, 0x000b0880, 0x024c22d3, +0x3cc01930, 0xfc05ff0d, 0xc00fb007, 0x00bf013e, 0x1ff007fc, 0xf3007cc8, +0xb003fc00, 0x402fc00f, 0x229c0075, 0x07c21a30, 0x00000c20, 0x00000000, +0x00162081, 0x02540251, 0x05400d50, 0x74041d00, 0x40405000, 0x031d0105, +0xc0d0605c, 0x11060140, 0x50283401, 0x40e74081, 0x016c0055, 0x04401934, +0x00000802, 0x00000000, 0x0064a001, 0x02441099, 0x04406d90, 0x74001504, +0x40411160, 0x141d0006, 0x01500070, 0x11000448, 0x90085438, 0x00764409, +0x035480d5, 0x05404990, 0x00000200, 0x00000000, 0x40002010, 0x031400c1, +0x01600550, 0x34000d30, 0x40015800, 0x000d0003, 0x01900016, 0x00600540, +0x10006400, 0x80034008, 0x002401c5, 0x40400813, 0x00000080, 0x00000000, +0x0026b000, 0x024c0093, 0x00400930, 0x7c001700, 0xc0013000, 0x001f0006, +0x01f1007c, 0x10400448, 0xb0007c00, 0x002bc009, 0x035c00d7, 0x05c009b0, +0x00000ac0, 0x00000000, 0x200fb805, 0x02fc00ff, 0x3fc00ff0, 0xfc00ff00, +0xc00f7003, 0x20bf003c, 0x0ff003dc, 0xff003bc0, 0xd003fc00, 0x502fc007, +0x02fc007f, 0x15c00af0, 0x00000e60, 0x00000000, 0x001fb003, 0x01dc20f7, +0xbe408f70, 0xdc083301, 0xc08ff003, 0x4cfb0d3e, 0x0d30904c, 0xf60837c0, +0xf202ec48, 0x1104e04f, 0x011c0c4f, 0x0cd08f70, 0x00000e00, 0x00000000, +0x20a70001, 0x00c40201, 0x3c404f30, 0x6c041b40, 0x400dd1b3, 0x06510081, +0xcf112850, 0xfd013dc1, 0x54124c04, 0x2985c08f, 0x0154025d, 0x04406810, +0x00000c20, 0x00000000, 0x0217a211, 0x01140245, 0xb1400c14, 0x34004101, +0x404c9041, 0x88414000, 0x0c185004, 0xcd043340, 0x10432404, 0x0710400c, +0x0104228d, 0x45406810, 0x00000e80, 0x00000000, 0x0005a803, 0x09540095, +0x35480d10, 0x44021100, 0x400dd041, 0x04c10035, 0x0d118010, 0xdd003540, +0x50036400, 0x0c15400d, 0xc15000dd, 0x0d400910, 0x00000620, 0x00000000, +0x0256a802, 0x055c0097, 0x35d00d30, 0x74029100, 0xc00dd002, 0x019a0036, +0x0d340f4c, 0xcf0037c0, 0x30026c00, 0x0804c00d, 0x0c08124f, 0x09c00d34, +0x00000e20, 0x00000000, 0x102d8007, 0x086c007a, 0x3ec00fb1, 0xfc207f20, +0xc50ef026, 0x213f500b, 0x0ff00ffc, 0xff003fc2, 0xf0039c40, 0x0087c40f, +0xc8fc147f, 0x9ec08ab0, 0x00000600, 0x00000000, 0x00350802, 0x091c081f, +0x32c08cb0, 0x7c00df01, 0xe20d7408, 0x001f0107, 0x0df04f5c, 0xd30435c0, +0xf0035c10, 0x2014c20c, 0x214c0097, 0x08c08930, 0x00000420, 0x00000000, +0x03a0a013, 0x45440bd1, 0x7f402f90, 0x74105d01, 0x412f112c, 0x0a9111b7, +0x1fd00b6c, 0xf1003ec0, 0xd0077d03, 0x0094402f, 0x09ec02f1, 0x4c40a910, +0x00000200, 0x00000000, 0x0042a007, 0x113400d5, 0xb3490c90, 0x34d01900, +0x401c4000, 0x088d0032, 0x8cd00004, 0xc1023261, 0x90060520, 0x118042dc, +0x0d3000cd, 0x0c501890, 0x00000a00, 0x00000000, 0x00788004, 0x21a40921, +0x7b401e94, 0x2401ad11, 0x401e1054, 0x8521024b, 0x5ed804a4, 0xc1003a40, +0xd046b419, 0x11584c1f, 0x05f40df9, 0x00401e9c, 0x00000200, 0x00000000, +0x00000012, 0x213c0047, 0x33400cb1, 0x34084e02, 0x440c7096, 0x001f0143, +0x1df1271c, 0xc30b7664, 0xb0171d00, 0x0140c80c, 0x853c0dcf, 0x58d04cb0, +0x00000040, 0x00000000, 0x2019a802, 0x11cc00bf, 0x37c00f70, 0xfc08be03, +0xc00df022, 0x04bb203f, 0x0df0036c, 0xff3f3ec0, 0xf1137c00, 0x2025c00e, +0xb1ec20f7, 0x0bc00f70, 0x00000660, 0x00000000, 0x0007a015, 0x434c009b, +0x35c00df0, 0x5c00930c, 0xc00d3001, 0x00db0037, 0x4df0004c, 0xd32d37c8, +0x72024c9d, 0x1006c12d, 0xa50d00df, 0x57c00938, 0x00000e08, 0x00000000, +0x08398812, 0x03852041, 0x38412ed0, 0xb400e101, 0x400e1201, 0x004b000f, +0xaed000ac, 0xf3313b40, 0x51038444, 0x1019424e, 0x03ac43fd, 0x4b400eb0, +0x00000620, 0x00000000, 0x006d0003, 0x05844129, 0x7b405ed0, 0xb403e902, +0x429e1407, 0x0161904b, 0x5ed007a0, 0xe9107b48, 0xd0873484, 0x0048449c, +0x15c401ed, 0x0f441e14, 0x00000400, 0x00000000, 0x00332812, 0x030600c1, +0x34400cd0, 0x3601c940, 0x680c1103, 0x01c92037, 0x0cd00744, 0xc1103344, +0xd0073500, 0x0231440c, 0x0f0400dd, 0x5b400c90, 0x00000c20, 0x00000000, +0x189da817, 0x01cc017b, 0x17c005f0, 0xfc037b00, 0xc4051041, 0x1073001f, +0x05f01de1, 0x4b0013c0, 0xf0117c00, 0x005e0207, 0x21c0807f, 0x5fc40530, +0x00000620, 0x00000000, 0x20070012, 0x007c0a1f, 0x07c601f0, 0x7c041700, +0xc001f008, 0x001f0007, 0x01f1487c, 0x1f0007e0, 0x32084400, 0x0005e001, +0x207c001f, 0x4bc02130, 0x00000c00, 0x00000000, 0x04230810, 0x460c008b, +0x25c00834, 0x3c229310, 0xc0091022, 0x008f0024, 0x0932024c, 0x9b0127c4, +0x30064c00, 0x3026c009, 0x82688197, 0x40c409f2, 0x00000c20, 0x00000000, +0x40262001, 0x26458091, 0x24400910, 0x74409100, 0x4089144e, 0x00951025, +0x09100a45, 0x91106740, 0x514e0440, 0x10254028, 0x02540197, 0x055129d0, +0x00000800, 0x00000000, 0x0064a018, 0x02440091, 0x25400990, 0x54409100, +0x4008d002, 0x10990024, 0x09100364, 0x91802540, 0x98226402, 0x20e44009, +0x02740495, 0x604008d8, 0x00000200, 0x00000000, 0x00202010, 0x02240281, +0x20400890, 0x34088902, 0x40085022, 0x088500a1, 0x08107300, 0x8d422360, +0xd8226400, 0x1221448c, 0x42040885, 0x414088d9, 0x00000080, 0x00000000, +0x0502b01d, 0x504c1453, 0x81c000b0, 0x1c001305, 0xc08172d8, 0x161f0504, +0xe114186e, 0x1b0587c1, 0xb2586c9c, 0x0586e161, 0x047c9017, 0x74c161f2, +0x00000ac0, 0x00000000, 0x002fb819, 0x02dc00b7, 0x27c92970, 0xfc04b769, +0xc069b013, 0x04bb002f, 0x19f012fc, 0x93012380, 0x7012dc04, 0x012ff049, +0x02fc00bf, 0x66c04bf0, 0x00000e60, 0x00000000, 0x002fa018, 0x025c009f, +0x27c00970, 0x5c009f05, 0xcb69b00a, 0x009d0227, 0x69f04274, 0xbb132d44, +0x3042dc06, 0x142cc04b, 0x12ec12bb, 0x63c009f0, 0x00000e00, 0x00000000, +0x0007081c, 0x00440017, 0x87c06110, 0x44041d01, 0x40019000, 0xa0110107, +0x43700874, 0x11000440, 0x50582c84, 0x4187c001, 0x006c065f, 0x734040d1, +0x00000c20, 0x00000000, 0x002ba010, 0x0e9410ad, 0x2b418a50, 0x9410b904, +0x400ad002, 0x00a9912a, 0x6ad052b0, 0x8101a140, 0x58023586, 0x24604568, +0x0a040481, 0x414528d1, 0x00000e80, 0x00000000, 0x0425a818, 0x06c602b5, +0x2f440f10, 0xc400bd10, 0x400bd002, 0x80b10c2f, 0x0b5002f6, 0x91002400, +0x50926420, 0x40676809, 0x2264089d, 0x634019d0, 0x00000620, 0x00000000, +0x00a78805, 0x025c029d, 0x27400970, 0x5c098f00, 0xc009f002, 0x109f40a7, +0x09f19274, 0x914025c0, 0x70027c00, 0x20e44008, 0x064c0093, 0x15c209f0, +0x00000e20, 0x00000000, 0x00248014, 0x027c0097, 0x25c009f0, 0x3c019f00, +0xc2093002, 0x009f10e7, 0x0970267c, 0x970027c4, 0xf2067c00, 0x0027c009, +0x027e008f, 0x53c00df8, 0x00000600, 0x00000000, 0x008d0814, 0x008c0223, +0x0ce00330, 0xcc103f08, 0xc0033000, 0x403f000a, 0x027008f4, 0x072c0340, +0x10005c88, 0x00464801, 0xe04c881b, 0x50c01130, 0x00000420, 0x00000000, +0x0854a014, 0x01440051, 0x57e00550, 0x44005d00, 0x40051001, 0x01592017, +0x0510014c, 0x7d201c40, 0x10a9fc00, 0x08984007, 0x05c40175, 0x50400510, +0x00000200, 0x00000000, 0x4252a014, 0x00340901, 0x004a0012, 0x14000d08, +0x40001800, 0x00055803, 0x10180014, 0x4d003240, 0xd0834430, 0x0030400c, +0x070431cd, 0x50500c90, 0x00000a00, 0x00000000, 0x20708205, 0x033404c1, +0x39401e50, 0x9408fd0c, 0x408e1203, 0x07c1013b, 0x4c405704, 0x4d213841, +0xd003a100, 0x03185006, 0x068420f1, 0x14411290, 0x00000200, 0x00000000, +0x00581015, 0x05bd01a3, 0x48405330, 0xdc056f0d, 0x48963047, 0x33ae144e, +0x3234169c, 0x67217a80, 0xf1079c27, 0x014cc41f, 0x078800af, 0x54d016b4, +0x00000040, 0x00000000, 0x0035b810, 0x024c2a5f, 0xb7c02df0, 0x6d468f22, +0xc869f430, 0x065b0337, 0x1db0417c, 0x5e0827c8, 0x30827400, 0x0045c145, +0x823c498f, 0x43c02970, 0x00000660, 0x00000000, 0x005fa000, 0x049c037f, +0x5dc13330, 0x5c03bf00, 0xc213f104, 0x855a1046, 0x17d245fc, 0xfba67ec0, +0xb227cd8d, 0x1078c81d, 0x244c01d3, 0x00c03b30, 0x00000e20, 0x00000000, +0x01398815, 0x93ac20ad, 0xa9c04e10, 0x04046f00, 0x002fd017, 0x09ab0070, +0x7bd0028c, 0xa3001842, 0xf0073c0c, 0x0fdbc076, 0x1eac0d6d, 0x54408610, +0x00000660, 0x00000000, 0x00190200, 0x09d400ed, 0x1b40021a, 0xb410ed02, +0x50065093, 0x00c90008, 0x56d003a6, 0x61003144, 0xd041844c, 0x010e404e, +0x10e400a9, 0x00400210, 0x00000400, 0x00000000, 0x02b32804, 0x0204120d, +0x23600d1c, 0x24000500, 0x4009d800, 0x120904f0, 0x08d00004, 0x01000040, +0x42003440, 0x00c14005, 0x0224010d, 0x10420c10, 0x00000c00, 0x00000000, +0x00b5a815, 0x0254113f, 0x2fc00f10, 0xfc003d00, 0xc00b7000, 0x823a217e, +0x0bf080ec, 0xf3000dd4, 0xd0834c00, 0x002e4205, 0x016c81e3, 0x54f00d30, +0x00000600, 0x00000000, 0x21170001, 0x0d7c00cf, 0x95c001f0, 0x5c00df00, +0xc005f003, 0x80d70887, 0x05f2032c, 0x971035c4, 0xf1037d00, 0x0027c021, +0x007d08d7, 0x07c011f5, 0x00000c00, 0x00000000, 0x023b08a0, 0x038c0093, +0x28d29fb0, 0xfc307300, 0xc00ff003, 0x80b5007e, 0x0b3102ec, 0x43000bc0, +0xa003ac00, 0x54dcd04f, 0x419000d3, 0x00c00530, 0x00000c02, 0x00000000, +0x00162081, 0x10444051, 0x94403150, 0x74009500, 0x4421d100, 0x00550185, +0x05108174, 0x1b006740, 0x50024438, 0x00d04021, 0x2c5410d5, 0x045009f0, +0x00000802, 0x00000000, 0x00b4a001, 0x02440051, 0x34400d94, 0x74009500, +0x4009d000, 0x80510231, 0x0d900174, 0xd122876c, 0x54836400, 0x00045205, +0x015688d1, 0x06400892, 0x00000200, 0x00000000, 0x40102010, 0x01042081, +0x00480011, 0x24004100, 0x4004d013, 0x04850001, 0x40950234, 0x01001240, +0x50430500, 0x11004040, 0x600618c1, 0x42401c92, 0x00000080, 0x00000000, +0x0036b000, 0x034d00f3, 0x3cc00fb4, 0xfc40e700, 0xc40fd037, 0x05f701f8, +0xfdb023fc, 0x510207c5, 0x703d6c00, 0x2754c15e, 0x15582593, 0x02c401b0, +0x00000ac0, 0x00000000, 0x100fb805, 0x00fc003f, 0x0fc003f0, 0xfc003f00, +0xc003f0a4, 0x0837074d, 0x113004fc, 0x3f894fc0, 0xf0947840, 0x0057c003, +0x14bc05bf, 0x15c00ff0, 0x00000e60, 0x00000000, 0x003f8003, 0x02f4003f, +0x3cc00f50, 0xfc00ff00, 0xc80f3803, 0x00b1403f, 0x0f3003fc, 0xfd103fc0, +0xb123fc00, 0x018acc27, 0x51fc90a2, 0x0cc02f34, 0x00000e00, 0x00000000, +0x103f0801, 0x02748011, 0x05400911, 0x3c001d00, 0x48011200, 0x00110007, +0x01520074, 0x1d000740, 0x10147400, 0x2364c858, 0x49740095, 0x04500f10, +0x00000c20, 0x00000000, 0x0033a011, 0x02140001, 0x00600451, 0x36000d20, +0x40001400, 0x40090003, 0x00940024, 0x0d000340, 0xd0101480, 0x80324008, +0x11140009, 0x44404c90, 0x00000e80, 0x00000000, 0x0035a803, 0x02740211, +0x05400d18, 0x74101d00, 0x40811020, 0x90190007, 0x01902034, 0x1d820740, +0xd0083424, 0x10244049, 0x4174009d, 0x0c400d10, 0x00000620, 0x00000000, +0x4037a802, 0x027c2313, 0x04cc2850, 0x78021f10, 0xc001100c, 0xa05b0047, +0x21b0146c, 0x1f04c7c0, 0xf0245c41, 0x0806c011, 0x213c249b, 0x08c20d32, +0x00000e20, 0x00000000, 0x08398007, 0x02bc4573, 0xffc00ff2, 0xdc09ff08, +0xc01ff107, 0x80f700bf, 0x3f7007fc, 0xff007fc0, 0x3003fc00, 0x120dc00f, +0x057c00f7, 0x1fc00fc0, 0x00000600, 0x00000000, 0x00350806, 0x437c021f, +0xb5d505f0, 0x7c02db00, 0xc22d3083, 0x00d340b4, 0x2df21b7c, 0xdf00b7e8, +0xf00b7c02, 0x0035828d, 0x094c00d3, 0x0bc08d30, 0x00000420, 0x00000000, +0x013ca013, 0x8374005d, 0x35490d10, 0x4c0add01, 0x41ad100f, 0x0fd122f5, +0x0dd00b74, 0xd1103740, 0xd0037400, 0x012c44ad, 0x0d6c00fb, 0x4f420f10, +0x00000200, 0x00000000, 0x0072a003, 0x8e24010d, 0x835c3410, 0x74000d09, +0x50001440, 0x00010004, 0x00d82034, 0x05000340, 0xd0002480, 0x04954011, +0x41040089, 0x1f410d14, 0x00000a00, 0x00000000, 0x20788000, 0xb6b411ad, +0x7b409a14, 0x8401ed00, 0x421e1107, 0x05a00c7a, 0x1ed087b4, 0xe9007b40, +0x9027b421, 0x213c401e, 0x04a400b9, 0x13401e10, 0x00000200, 0x00000000, +0x18301016, 0x527c000f, 0xb3ca8c74, 0x3c00cf20, 0xc82d3043, 0x008320b4, +0x0cd3933e, 0xcf023744, 0xf0032c22, 0x1a31c00c, 0x510c088b, 0x4bc20c3a, +0x00000040, 0x00000000, 0x04bdb802, 0x92fc00bd, 0x3dc08e70, 0xdc08ff00, +0xc08f7003, 0x049f0035, 0x0ff803fc, 0xf7383fc0, 0xf003fc00, 0x003fc01f, +0x00fc08bf, 0x0bc10ff8, 0x00000660, 0x00000000, 0x00b7a015, 0x827c2113, +0x04c81130, 0x7c001700, 0xc001b000, 0x40532007, 0x01300440, 0x1f0007c0, +0x30047c00, 0x2a57c001, 0x074c088b, 0x57c16d30, 0x00000e00, 0x00000000, +0x00318812, 0x02bc402b, 0x38400eb0, 0xb400ed00, 0x400e1403, 0x00f1403f, +0x0e9003c4, 0xed103b40, 0x1003b400, 0x011b400e, 0x02ec01e1, 0x4b484e11, +0x00000620, 0x00000000, 0x80790003, 0x0f340061, 0x78411e10, 0xb411ed04, +0x401e1007, 0x21e1007b, 0x1e908786, 0xed007b41, 0x1107b6d1, 0x003b401e, +0x079407f9, 0x0f401e90, 0x00000400, 0x00000000, 0xa0332812, 0x035481c9, +0x78401e98, 0xf403ed00, 0x442e1007, 0x12e1007b, 0x0e900386, 0xed80bb40, +0x1407b402, 0x04b3600e, 0x023413d1, 0x4b400c90, 0x00000c20, 0x00000000, +0x0015a817, 0x05740473, 0x94d00530, 0x74005700, 0xc9051045, 0x115316d7, +0x8534094d, 0x5d22d7c8, 0x30417c81, 0x001fc057, 0x21dc017b, 0x5fc005b4, +0x00000620, 0x00000000, 0x00030012, 0x087c101f, 0x0fc403f0, 0xfc003f00, +0xc1037280, 0x003f000f, 0x037260ec, 0x3f020fc0, 0xf010fc02, 0x0007e003, +0x806c801f, 0x4bc02172, 0x00000c00, 0x00000000, 0x01270810, 0x224c0083, +0x23e00932, 0x6c109b04, 0xc4093042, 0x00930024, 0x09f0026c, 0x9f0067c0, +0xf0024c12, 0x1022c009, 0x024d809b, 0x43c009f0, 0x00000c20, 0x00000000, +0x20262001, 0x42442091, 0x27680910, 0x45009340, 0x50091102, 0x00914024, +0x09d0024d, 0x8d226740, 0xd00a4c01, 0x10245009, 0x02440091, 0x074129d0, +0x00000800, 0x00000000, 0x00248018, 0x82442191, 0x2f4e0d10, 0xc400a100, +0x400a1c03, 0x20a10028, 0x0fd802c4, 0xb5802f42, 0xd042c400, 0x0024602a, +0x02640091, 0x634009d8, 0x00000200, 0x00000000, 0x02202010, 0x22040081, +0x2b400a10, 0x8408a100, 0x508a1022, 0x08a10228, 0x0ad82284, 0xed0a2b40, +0xd00a8400, 0x10a0402a, 0x0a240081, 0x434008d8, 0x00000080, 0x00000000, +0x0586b01d, 0x584e1413, 0x17400134, 0x0c021300, 0x48213008, 0x02130084, +0x01f0084c, 0x170087c0, 0xf0000d00, 0x0500c003, 0x006c1e13, 0x77c3e1f0, +0x00000ac0, 0x00000000, 0x4127b819, 0x12fe00ff, 0xa7c228f0, 0x5c069f20, +0xc069b21a, 0x269f11a7, 0x29f01b5c, 0xdf01b7c0, 0xf00b5c02, 0x002fc029, +0x0adc01b7, 0x67c019f0, 0x00000e60, 0x00000000, 0x022fa018, 0x1acc049f, +0x24404a32, 0x4c309300, 0xc0693002, 0x009309a6, 0x4b30124c, 0xa3022c80, +0x30124c04, 0x1927c089, 0x12cc0291, 0x60c16bf8, 0x00000e00, 0x00000000, +0x0087081c, 0x90440a1d, 0x04500112, 0x05050140, 0x5071143c, 0x030140c0, +0x01101cc5, 0x11428450, 0x14c44502, 0x054f40b1, 0x50440445, 0x72c001d0, +0x00000c20, 0x00000000, 0x01a3a010, 0x9a24008d, 0xa0488810, 0x84108108, +0x40ca5042, 0x16a50229, 0x28146284, 0x81012040, 0x110a0408, 0x01ab400a, +0x32240681, 0x406008d8, 0x00000e80, 0x00000000, 0x0025a818, 0x8264809d, +0x24410914, 0xc4109104, 0x406b5002, 0x82b501ad, 0x291022c6, 0x91002440, +0x10224400, 0x042f426b, 0x02640095, 0x624009d2, 0x00000620, 0x00000000, +0x0027a805, 0x266c029f, 0xe4cc3930, 0x4c029300, 0xc029706e, 0x619780e5, +0x0930864c, 0x930024c0, 0x30024c00, 0x40e7c239, 0x026d0293, 0x14d009d0, +0x00000e20, 0x00000000, 0x00258014, 0x025c49df, 0x77c049f0, 0x7c099f02, +0xc019b006, 0x059b2066, 0x29f0067c, 0x9f00a7c0, 0xe0067c02, 0x00a7c009, +0x425c089b, 0x53c109f2, 0x00000600, 0x00000000, 0x2a050814, 0x207c800f, +0x80c03080, 0x8c020f02, 0xc0223008, 0x4023008b, 0x00f0008c, 0x0f0000c0, +0xf0080c00, 0x000cc443, 0x005c8013, 0x50c000b4, 0x00000420, 0x00000000, +0x0014a014, 0x2df4205d, 0xd4412750, 0x44005d90, 0x00053001, 0x20550017, +0x05700155, 0x7d001540, 0xd0014400, 0x001c5005, 0x09c48051, 0x52c01711, +0x00000200, 0x00000000, 0x0072a014, 0x0374004d, 0x324c1890, 0x0500cd04, +0x400c8403, 0x40c02033, 0x1cd00324, 0x4d027040, 0xd0030400, 0x0014422c, +0x2e1400d1, 0x52403c10, 0x00000a00, 0x00000000, 0x20288005, 0x0334032d, +0x3a400e10, 0x84006d10, 0x44021000, 0x0025100b, 0x2e5000a4, 0x6d003942, +0xd0038400, 0x302a4010, 0x050404e1, 0x1641ce10, 0x00000200, 0x00000000, +0x00681015, 0x26bc002d, 0x7ad21ab0, 0x8c41ed10, 0xc01eb107, 0x81e3407b, +0x1ef007ac, 0x6f0078c0, 0xf0058501, 0x405cc81e, 0x069c05e3, 0x56c01630, +0x00000040, 0x00000000, 0x0025b810, 0x037c061f, 0x95c00df0, 0x7c405f00, +0xc401f000, 0x001b1007, 0x0cf0005c, 0x5f0037c0, 0xf2017020, 0x0025c001, +0x017c42df, 0x43c00470, 0x00000660, 0x00000000, 0x007fa000, 0x07fc2372, +0xfcc0df34, 0xc801f322, 0xc09f3007, 0x41f3207c, 0x1b3007d8, 0x73027cc0, +0x3027cc01, 0x0058c01f, 0x85cc93f3, 0x00c01f34, 0x00000e00, 0x00000000, +0x02398815, 0x23f4002b, 0x284186b0, 0x94846b00, 0x41821010, 0x04210108, +0x8a100084, 0x61003840, 0xb0238490, 0x202ac042, 0x11c410fb, 0x56c00210, +0x00000620, 0x00000000, 0x00210000, 0x00b40221, 0x3a0a4e50, 0xb410e124, +0x480e9803, 0x40e1243a, 0x0a900386, 0x610c2840, 0x11018400, 0x005c610c, +0x819402e1, 0x00400650, 0x00000400, 0x00000000, 0x00272804, 0x82340909, +0x02561490, 0x37034900, 0x58119604, 0x03114002, 0x88940447, 0x41642050, +0x14450513, 0x01a05001, 0x210401c9, 0x12400050, 0x00000c20, 0x00000000, +0x0035a815, 0x027c0250, 0xb6c01d70, 0x6401d100, 0xc03db06b, 0x84d102b6, +0x29a06b5c, 0x4300e4c0, 0x101f4c80, 0x04d4401d, 0x061d01f3, 0x54c00b70, +0x00000620, 0x00000000, 0x00270001, 0x0278001f, 0x35c00df2, 0x4c045f04, +0xc0417080, 0x001f0005, 0x4970006c, 0x5f0827c0, 0xf0037c20, 0x0027c101, +0x017c00df, 0x07c009a0, 0x00000c00, 0x00000000, 0x806f0880, 0x02fc0833, +0x3ce15db0, 0x4500df10, 0xc00df003, 0x10df0036, 0x5930034d, 0x9f0037c0, +0xf0014c00, 0x001cc00f, 0x02cc00e3, 0x00c00230, 0x00000c22, 0x00000000, +0x80662081, 0x46740015, 0x146a0c10, 0xc4007d01, 0x40037100, 0x003d000c, +0x19f000c8, 0x9d027744, 0xd001c403, 0x502d5043, 0x015400d1, 0x05409114, +0x00000802, 0x00000000, 0x2124a001, 0x06340011, 0x14480d90, 0x5680cd00, +0x600dd103, 0x00dd0034, 0x09900340, 0x5d002740, 0x90034006, 0x0010400d, +0x014400d1, 0x04400910, 0x00000200, 0x00000000, 0x08202010, 0x82348005, +0x00600410, 0x14004d30, 0x50005900, 0x40090000, 0x08980004, 0x4d002340, +0xd0030520, 0x40204000, 0x015440c1, 0x41400010, 0x00000080, 0x00000000, +0x0026b000, 0x02fc0011, 0x14500db2, 0x5e00dd20, 0xc00df203, 0x00df0034, +0x09b0034c, 0x9f0027c0, 0xb0014c00, 0x0014c005, 0x014c20f3, 0x00c40130, +0x00000ac0, 0x00000000, 0x002fb805, 0x02fc003f, 0x0bc007f0, 0xec007f80, +0xc0034200, 0x003f000f, 0x0bf000dc, 0xbf102fc0, 0xf001fc00, 0x002fc40b, +0x01fc40ff, 0x17c002f2, 0x00000e60, 0x00000000, 0x010f8003, 0x70ec08ff, +0x3dc18330, 0x5c043f00, 0xc0cfb04b, 0x84d7a006, 0x0f3011dc, 0x270236c0, +0xb000dc00, 0x003fc009, 0x034c301f, 0x0cc00ff0, 0x00000e00, 0x00000000, +0x1aa30801, 0x080c06fd, 0xbf426190, 0xc40e5f02, 0x04eea003, 0x10f12597, +0x4fd02155, 0x15103440, 0x1201140a, 0x0034400b, 0x8bd4021d, 0x04402fb0, +0x00000c20, 0x00000000, 0x8503a011, 0x902608cd, 0x31484094, 0x16108d05, +0x120c9893, 0x02c50080, 0x0c500104, 0x01013261, 0x90221400, 0x00334004, +0x232420cd, 0x444028d8, 0x00000e80, 0x00000000, 0x28258803, 0x126400cd, +0x37418198, 0x4400d500, 0x440d9003, 0x00d10014, 0x0d500154, 0x01487440, +0x10025002, 0x0034511d, 0x037400dc, 0x0c4009c0, 0x00000620, 0x00000000, +0x02478802, 0x006c00df, 0x35c23110, 0x541b1d00, 0xd00db103, 0x00d526c6, +0x0d74444c, 0x57007280, 0xb2085421, 0x8877c83d, 0x036c009f, 0x08c41de0, +0x00000e20, 0x00000000, 0x042d8007, 0x85dc00df, 0x3fc01772, 0x7c005f00, +0xc00e3043, 0x00f7005b, 0x0fe017bc, 0x3f003fc0, 0xf001bc85, 0x027dc00c, +0x03dc09af, 0x1fc09e30, 0x00000600, 0x00000000, 0x00950802, 0x106c00d7, +0x34d02930, 0x4c088301, 0xd00db003, 0x84d70885, 0x0d72007c, 0x534077c0, +0x70034c02, 0x4036c0ad, 0x037c00d3, 0x08c00920, 0x00000420, 0x00000000, +0x01f4a013, 0x0b443bf1, 0xbcc00d92, 0xdc00df01, 0x800fb103, 0x03e1001e, +0x5f3203fc, 0x1b0036c4, 0x1003ec03, 0x12f440bd, 0x1bdc0ed1, 0x4c41a9b0, +0x00000200, 0x00000000, 0x1092a007, 0x034403cd, 0x70400c18, 0x64840100, +0x400c180b, 0x01c51020, 0x1d500326, 0x0581b141, 0x10001453, 0x0032400c, +0x1324004d, 0x1c4218c8, 0x00000a00, 0x00000000, 0x80588204, 0x048701e9, +0x7e401e91, 0x86012502, 0x409e1037, 0x1de5007c, 0x1e5007f6, 0x69407800, +0x1037b631, 0x057940ce, 0x0794096d, 0x104018d1, 0x00000200, 0x00000000, +0x00b01012, 0x830408cd, 0x70404d38, 0x05080101, 0x429c1027, 0x45c7a160, +0x4c70073c, 0x01037148, 0x300c1800, 0x0176c0dc, 0x077c85cf, 0xc8c04cb4, +0x00000040, 0x00000000, 0x003db002, 0x02de30f7, 0xb9c00f72, 0xfe003c25, +0xc02fb023, 0x20eb222b, 0x0cb823bc, 0x7f4032c0, 0xf482ec80, 0x0236d08f, +0x037c80d3, 0x0bc01f30, 0x00000660, 0x00000000, 0x0007a015, 0x837c04df, +0xb6c00134, 0x7c00d340, 0xc02db813, 0x34db0046, 0x7db0067c, 0x5b0836c0, +0x30004c00, 0x2034c16d, 0x974c00d3, 0x54c04970, 0x00000e00, 0x00000000, +0x18098012, 0x01b408ed, 0x3c400610, 0x3400e909, 0x404c524b, 0x02c1101c, +0x4c100fc4, 0x2d813941, 0x10138480, 0x0038500f, 0x231400c1, 0x48410a50, +0x00000620, 0x00000000, 0x04790003, 0x479405ed, 0x79441a10, 0xb481e900, +0x429e1017, 0x85e10208, 0x9e925394, 0xc9027800, 0x10358481, 0x0479411e, +0xa79401e5, 0x0d401c18, 0x00000400, 0x00000000, 0x00332812, 0x073400cd, +0x31013c14, 0x3402c900, 0x500d5803, 0x00c90490, 0x0c100304, 0xdd003140, +0x14070401, 0x0061413c, 0x031400c5, 0x49400c5a, 0x00000c20, 0x00000000, +0x001da817, 0x01dc005f, 0x1f503730, 0xbc117100, 0xe0053681, 0x005b001c, +0x05b025dc, 0x79804c80, 0x1045cc03, 0x4815d027, 0x015c0075, 0x5dd00530, +0x00000620, 0x00000000, 0x00070012, 0x007c020f, 0x00c081f0, 0x7c101700, +0xc001b000, 0x00170205, 0x2178004c, 0x1f0205c2, 0xf0007d8c, 0x0006c001, +0x0074101b, 0x4ac001f0, 0x00000c00, 0x00000000, 0x00270810, 0x026c009f, +0x26c00870, 0x4c029308, 0xc009b202, 0x01870025, 0x5930024d, 0x9f0007c0, +0xb0020d00, 0x0025c009, 0x024c0093, 0x41c009b0, 0x00000c20, 0x00000000, +0x00a60001, 0x0a54029d, 0x26c00914, 0x74009500, 0x50091406, 0x09910020, +0x29120204, 0x99208740, 0x720a4400, 0x40245009, 0x02450091, 0x045009d0, +0x00000800, 0x00000000, 0x80a4a018, 0x43050095, 0x64400950, 0x54009900, +0x40090012, 0x00910026, 0x09180254, 0x99006742, 0x10424500, 0x00234808, +0x02640081, 0x614008d0, 0x00000200, 0x00000000, 0x02202810, 0x7204028d, +0x2251c890, 0x7414c100, 0x40681023, 0x80810224, 0x8c101a54, 0x8d026348, +0x50220408, 0x02224c88, 0x32240881, 0x404088d0, 0x00000080, 0x00000000, +0x3586a01d, 0x180c1457, 0x04cc6070, 0x5c041b05, 0xe041b0d8, 0x7e110187, +0x2130105c, 0x5f0507c0, 0x30d80c16, 0x00838161, 0x0c6c9613, 0x75c161b1, +0x00000ac0, 0x00000000, 0x012fa819, 0x12fc009f, 0x24c04b70, 0x7c14bf45, +0xc069f012, 0x219f032f, 0x69f00aec, 0xfb012fc0, 0xf012fd04, 0x012dc24b, +0x325c04bf, 0x67c24bf0, 0x00000e60, 0x00000000, 0x812fa818, 0x0aec00b3, +0x2fc229b0, 0xfc30bf00, 0xc029f00a, 0x06af0126, 0x2af0824c, 0xa301eec2, +0xf05a1c02, 0x00a6c489, 0x1a44829f, 0x60c029b2, 0x00000e00, 0x00000000, +0x4507081c, 0xb9442413, 0x07420152, 0x74000f09, 0x44217010, 0x401d1581, +0xa1f0083c, 0x1b418442, 0xd01c4400, 0x01455075, 0x0444051d, 0x7040f310, +0x00000c20, 0x00000000, 0x0323a010, 0x1206168d, 0xa3402850, 0x34108d14, +0x4268d28a, 0x068d0220, 0x08d04a16, 0x8921a240, 0x50022402, 0x012b502a, +0x1aa406ad, 0x40404a10, 0x00000e80, 0x00000000, 0x0025a018, 0x02460095, +0x27422954, 0x74029508, 0x54085002, 0x009d1025, 0x09510274, 0x94023440, +0xd0026400, 0x012d420b, 0x02e400bd, 0x60408b10, 0x00000620, 0x00000000, +0x01652005, 0x0a440095, 0x27440930, 0x74009d00, 0x4009d102, 0x009d2224, +0x09d0864d, 0x890826c0, 0xf04a7c03, 0x0027c039, 0x026d1b9e, 0x14d01934, +0x00000e20, 0x00000000, 0x01258014, 0x025c009b, 0x27c00930, 0x7c009f00, +0xc0097802, 0x509f0024, 0x09f1267c, 0x9b0067c1, 0xf00e5d03, 0x0067c039, +0x025c009f, 0x53c019f0, 0x00000600, 0x00000000, 0x00850014, 0x083c0013, +0x07c00134, 0x4c101101, 0xe801f000, 0x00130004, 0x80b0006d, 0x13004580, +0x30086c06, 0x200ec002, 0x00cd023f, 0x50d00230, 0x00000420, 0x00000000, +0x201ca814, 0x05f40771, 0x5f480500, 0xc4007404, 0x64079085, 0x0265201c, +0x3750017c, 0x71109c48, 0x12017c23, 0x00144205, 0x0144005d, 0x50400554, +0x00000200, 0x00000000, 0x0236a014, 0x063404d9, 0x73440c10, 0x0402c100, +0x480cd887, 0x42c12030, 0x2c500334, 0xcc103540, 0x10032402, 0xa832640c, +0x036420dd, 0x52400c50, 0x00000a00, 0x00000000, 0x00308005, 0xc03401e1, +0x62440e10, 0x8401e140, 0x408e9043, 0x02250078, 0x4e1027b4, 0x65a90844, +0x1007f442, 0x00095002, 0x00240029, 0x16400258, 0x00000200, 0x00000000, +0x40581015, 0x06bc006b, 0x7b407e32, 0x8d01e300, 0x501cf005, 0x01e32578, +0x1e7017ad, 0xeb416dc6, 0x3005ad01, 0x007ac01c, 0x07ac01ff, 0x56c01c30, +0x00000040, 0x00000000, 0x9015b010, 0x027c0057, 0x27e56df0, 0x7c201f08, +0xc02db101, 0x895b01b7, 0x9d77137c, 0xd90277c8, 0xf1817c06, 0x0004c201, +0x005c001f, 0x41c201f0, 0x00000660, 0x00000000, 0x007f8000, 0x37e4293f, +0x7d801f70, 0x4d295f00, 0xc03fb007, 0x01db0674, 0x9fb02fec, 0x73087ac0, +0x30a73503, 0x007ed01f, 0x07fc09f3, 0x00c21f32, 0x00000e00, 0x00000000, +0x01298835, 0x21fc00e7, 0x13c00eb1, 0x84017d01, 0xc04cf017, 0x8531047a, +0xca1a9710, 0x29034948, 0x11472c08, 0x02054042, 0x34b40121, 0x545003b0, +0x00000620, 0x00000000, 0x20190000, 0x13b4402d, 0x29410c11, 0xa414ed80, +0x420ed010, 0x08a10038, 0x069053a4, 0x61090e40, 0x1201f400, 0x0438400c, +0x033404e1, 0x00400e90, 0x00000400, 0x00000000, 0x01832804, 0x435680c5, +0x01401c10, 0x24010c00, 0x440dd100, 0x00011070, 0x00900345, 0x05001300, +0x10052401, 0x00010060, 0x00340001, 0x10400190, 0x00000c20, 0x00000000, +0x00a58815, 0x0f74009d, 0x15401f14, 0x6c009f00, 0x440f9202, 0x00e3807c, +0x0fb003ee, 0xc3005ed2, 0x310f7c31, 0x4034e22d, 0x037c00d1, 0x54c20d92, +0x00000620, 0x00000000, 0x98810001, 0x217c009f, 0x26c00df8, 0x5c009f00, +0xc80d7088, 0x009f0037, 0x0470035c, 0x5b0204c4, 0xfc037d00, 0x0005c001, +0x007c001f, 0x07c001f1, 0x00000c00, 0x00000000, 0x002f0880, 0x03fc293b, +0x1fc80ff0, 0xfc89f320, 0xd20ff023, 0x00ff003c, 0x19b0c3ec, 0xf3000fc0, +0xf041cc00, 0x003ec00e, 0x03cc00f0, 0x01c00f35, 0x00000c22, 0x00000000, +0x12442081, 0x07740113, 0xa7400df0, 0x74801100, 0xc40d1199, 0x22dd0036, +0x31100344, 0xd1001041, 0xd001ec80, 0x08084803, 0x80c42031, 0x044003b0, +0x00000802, 0x00000000, 0x8434a001, 0x13740011, 0x91680d50, 0x34001500, +0x400d1002, 0xd0550034, 0x4d150356, 0x55081440, 0xd0034400, 0x0035400d, +0x030400c5, 0x05480d10, 0x00000200, 0x00000000, 0x00002010, 0x012400c9, +0x12400c90, 0x34000142, 0x462c5600, 0xa4090430, 0x00101304, 0x11100448, +0xd0432720, 0x40057020, 0x40051005, 0x40400098, 0x00000080, 0x00000000, +0x4036b000, 0x0374201b, 0x05c00f70, 0x7c01d700, 0x482f301e, 0x091e21fc, +0x01300fbd, 0x616345d0, 0xf02d4c00, 0x22b5c22d, 0x0f4c0bd7, 0x01800d39, +0x00000ac0, 0x00000000, 0x800fb005, 0x03fc00f7, 0x4fc00f70, 0xfc083f01, +0xc05e9074, 0x8d3e257b, 0x0ff0774c, 0x3d00d5c0, 0xf015dc00, 0x014ec013, +0x547c851b, 0x97c003f0, 0x00000e60, 0x00000000, 0x050f8003, 0x02dc0033, +0x1b800bf0, 0xac007300, 0xc0029100, 0x0073400c, 0x0e3023fc, 0xe3030fc0, +0x3002ec08, 0x000cc007, 0x02fc006b, 0x0ed00a37, 0x00000680, 0x00000000, +0x20070801, 0x036c6013, 0x17420b34, 0x54004101, 0x400d1003, 0x00d50084, +0x0db083e4, 0xd9030740, 0x5002ec00, 0x04346105, 0x43741091, 0x04400d30, +0x00000c20, 0x00000000, 0x0003a011, 0x021600c9, 0x13440855, 0x04004104, +0x40009000, 0x00412203, 0x0c511334, 0xc9010342, 0x18020484, 0x11054044, +0x12140401, 0x44400950, 0x00000c80, 0x00000000, 0x0031a803, 0x0b6448d9, +0x164019d1, 0x50004410, 0x200d1003, 0x24d10405, 0x0d920374, 0xd9006740, +0x520e6403, 0x08354004, 0x027430d1, 0x0c408d51, 0x00000620, 0x00000000, +0x0a07a802, 0x015d031b, 0x07c01975, 0xcc047240, 0xc003b000, 0x001102b1, +0x0d70837c, 0xd34067c0, 0x300f4c00, 0x00054005, 0x061c005b, 0x0ac13170, +0x00000e20, 0x00000000, 0x002d8007, 0x83bd0067, 0x5fc40b30, 0xfc407b02, +0xf00f7203, 0x01bf045e, 0x2ff003ac, 0xe3102bc4, 0xf003bc00, 0x483ef807, +0x267c00bf, 0x1fc81730, 0x00000600, 0x00000000, 0x00550802, 0x196c009f, +0x17040934, 0x5c305301, 0xc1007014, 0x001b40a4, 0x8d30235c, 0xdf2224c9, +0xf00a6c00, 0x4004d005, 0x024c0213, 0x08c02130, 0x00000420, 0x00000000, +0x0034a013, 0x0b4413dd, 0xd7442900, 0x6c025100, 0x405d1083, 0x139b04ce, +0x0d5107f4, 0xfc02a440, 0xd0024400, 0x02704005, 0x224512d1, 0x4c401450, +0x00000200, 0x00000000, 0x0002a007, 0x0a24138d, 0x97430000, 0x5400c508, +0x40301008, 0x07c52001, 0x08100714, 0x0d004040, 0xd04a3400, 0x8083000c, +0x020411c5, 0x1c409cd0, 0x00000a00, 0x00000000, 0x18588004, 0x27a4098d, +0x5b401a19, 0xa401e501, 0x401e1087, 0x81ec0069, 0x1e502734, 0xfd024840, +0xd0068401, 0x0479401f, 0x2784ad31, 0x10400fd2, 0x00000200, 0x00000000, +0x00001012, 0x226c08cf, 0x13c08816, 0x1c00c70c, 0xc0007000, 0x00c74015, +0x4c31031c, 0x0f0210c0, 0xd0033c04, 0x0307e04c, 0x230c0c83, 0x48c084a0, +0x00000040, 0x00000000, 0x00bdb802, 0x23dc88fe, 0x1fc00bb0, 0xec08fb41, +0xc08ff003, 0x40dba636, 0x0ff003fc, 0xff023fd4, 0xf003fc00, 0x023ec00e, +0x23fc2d7f, 0x0bc04730, 0x00000660, 0x00000000, 0x0097a815, 0x034c009f, +0x17481970, 0x6c00d605, 0xc811f004, 0x00d71034, 0x6d701b48, 0xd70025c0, +0xb0130c01, 0x0004d00d, 0x034c00df, 0x57c40530, 0x00000e00, 0x00000000, +0x00399812, 0x032c00ad, 0x1f488e10, 0x8400e100, 0x840fd003, 0x00bb417e, +0x8eb04b2c, 0xf6002b40, 0x1143ac04, 0x0038400e, 0x0385002d, 0x4b480612, +0x00000620, 0x00000000, 0x00d90003, 0x078401ed, 0xda400ed8, 0x8601e510, +0x4012d000, 0x41e5107a, 0x1e121784, 0xec00e949, 0x9007d408, 0x0048401e, +0x070641ad, 0x0f401610, 0x00000400, 0x00000000, 0x00f32012, 0x032400dd, +0x5344cc14, 0x0400c500, 0x480cd003, 0x81890336, 0x0c900364, 0xc1007340, +0x10452400, 0x0030400c, 0x0705004d, 0x4b400410, 0x00000c20, 0x00000000, +0x0015a017, 0x61cc007d, 0x52c027f4, 0x6d005700, 0xc005f001, 0x0177005e, +0x15720144, 0x6f009dc8, 0xb041dc00, 0x0014c005, 0x014d005f, 0x5fc02734, +0x00000620, 0x00000000, 0x20070012, 0x087c0a1f, 0x07c001f4, 0xfc001a00, +0xc023f800, 0x081f0007, 0x01f0007c, 0x140407c0, 0x70087801, 0x000fc001, +0x287c003f, 0x4bc011b0, 0x00000c00, 0x00000000, 0x04370010, 0x260c4197, +0x74c00830, 0x14049361, 0xc8096002, 0x059b00e7, 0x4d30026c, 0x93202348, +0x30020000, 0x5024d409, 0x020c0093, 0x43c00930, 0x00000c20, 0x00000000, +0x00a62001, 0x4644029d, 0xe4400915, 0x54809180, 0x412910a6, 0x008141e7, +0x79b00244, 0x95002760, 0x100a4400, 0x20204009, 0x0a6c0081, 0x07408910, +0x00000800, 0x00000000, 0x00a4a018, 0x02450add, 0x24410d14, 0xd400810c, +0x480b1002, 0x40d91027, 0x09120264, 0x91822760, 0x10024400, 0x002d400c, +0xa24400b1, 0x63400910, 0x00000200, 0x00000000, 0x02202010, 0x2204088d, +0x24400810, 0x94088102, 0x402a1022, 0x02901523, 0x08902204, 0x95192340, +0x10821400, 0x00ad4028, 0x0a2402b1, 0x43480810, 0x00000080, 0x00000000, +0x0586b01d, 0x584c160f, 0x84c94130, 0x5c164100, 0xc1433258, 0x001a0107, +0x0130086c, 0x130ac340, 0x34514c0a, 0x0005c000, 0x014c8023, 0x77c00134, +0x00000ac0, 0x00000000, 0x2137b819, 0x12fc44bf, 0x27c80bf4, 0x7c049f21, +0xc009b012, 0x00bf052f, 0x09d01a58, 0xbf027fc2, 0xf102ec23, 0x20268809, +0x0add209f, 0x67c00bf0, 0x00000e60, 0x00000000, 0x012fa818, 0x32cc0cb3, +0x2f404930, 0xcc03bf00, 0xc019300e, 0x00bb222f, 0x0b30dac4, 0xdf01a4c0, +0x7012c806, 0x4026c029, 0x52cd0093, 0x60c00b70, 0x00000e00, 0x00000000, +0x4507081c, 0x38040e1b, 0x0742a114, 0x440a0d05, 0x48015038, 0x14110287, +0x01b0086c, 0x13008444, 0x11086806, 0x01474011, 0x18440121, 0x71000170, +0x00000c20, 0x00000000, 0x0323a010, 0x02240081, 0x23480811, 0x20048d00, +0x01289002, 0x00890123, 0x08109204, 0x8d01a144, 0x500e1082, 0x042f4048, +0xc20494a1, 0x404009d0, 0x00000e80, 0x00000000, 0x0225a818, 0x02655091, +0x67600914, 0x64009d00, 0x40091202, 0x00910027, 0x09900264, 0x91012450, +0x10026400, 0x802f4009, 0x024400b1, 0x61400950, 0x00000620, 0x00000000, +0x00e78005, 0x02640191, 0x674b0930, 0x6d009f08, 0xc0093002, 0x009b4067, +0x0830024c, 0x9f0065c0, 0x720a1c00, 0x0023c009, 0x264c4193, 0x14c04878, +0x00000e20, 0x00000000, 0x04258014, 0x025c149f, 0x27c14df0, 0x1c409f00, +0xc50df602, 0x109f0163, 0x89f0022c, 0x852077c2, 0xf00e7c00, 0x0027c009, +0x067c049f, 0x53c00970, 0x00000600, 0x00000000, 0x00010014, 0x404c2217, +0x07c021f4, 0x4c0c0f02, 0xc401f040, 0x001f0004, 0x4171004c, 0x1b0084c0, +0x70807c00, 0x480fc001, 0x804c8033, 0x53c02130, 0x00000420, 0x00000000, +0x089ca014, 0x01c4037d, 0x5dc00510, 0xd4007d10, 0x4005d019, 0xa171041d, +0x1711056c, 0x51005400, 0x1005f400, 0x10174205, 0x05d40051, 0x53400510, +0x00000200, 0x00000000, 0x02f2a014, 0x0744099d, 0x63022cd4, 0x0400cd00, +0x502cd003, 0x83d51060, 0x38508204, 0xc8007000, 0x50253440, 0x0031400c, +0x4f1400d9, 0x53400c54, 0x00000a00, 0x00000000, 0x18388005, 0x0984006d, +0x39400e10, 0x94816d02, 0x409cd801, 0x0a711039, 0x2b100ba4, 0xe100d060, +0x1001b40c, 0x100b480e, 0x47940029, 0x17400a10, 0x00000200, 0x00000000, +0x00781015, 0x878c018f, 0x7b4016f0, 0x8c206f10, 0xc71ef007, 0x05ef007c, +0x1a7006cc, 0xeb0078c0, 0x70053c15, 0x007dc01e, 0x04dd01eb, 0x57c01a70, +0x00000040, 0x00000000, 0x0035b010, 0x1b7d127f, 0x574145f5, 0x7c105f00, +0xc045d001, 0x004f0016, 0x0cd0837c, 0xdf0197d4, 0xf0036c06, 0x100fc00d, +0x006c0037, 0x43c009f0, 0x00000660, 0x00000000, 0x004fa800, 0x2fde01b3, +0x7ec4cfb1, 0xdc017f00, 0xc01f3007, 0x0163006d, 0x1af006e8, 0xff00fcc0, +0x3005dc01, 0x427f401f, 0x07cc8df3, 0x00c01f30, 0x00000e00, 0x00000000, +0x028d8015, 0x3384006b, 0x38400eb0, 0x94826900, 0xd10eb043, 0x006b0028, +0x8af00394, 0xeb191844, 0xb0018400, 0x470b600e, 0x03c55c31, 0x56c00a30, +0x00000620, 0x00000000, 0x00090000, 0x43340021, 0x38400412, 0x34082d00, +0x408e9023, 0x0821002e, 0x0ad00284, 0xe50c3b41, 0x10019400, 0x2039400e, +0x41840ce9, 0x00400b90, 0x00000400, 0x00000000, 0x00c30804, 0x0f240001, +0x10404494, 0x34001980, 0x4234980b, 0x05090080, 0x1c428314, 0xc000d240, +0x900b0403, 0x2483400c, 0x05041219, 0x12401818, 0x00000c20, 0x00000000, +0x44358015, 0x0f580091, 0x38803110, 0x7c001f04, 0xc4bfb007, 0x45f302a3, +0x09d00344, 0xf502f7c8, 0x34091c06, 0x00f5c005, 0x034c03db, 0x54c04cb4, +0x00000620, 0x00000000, 0x04270001, 0x035c280f, 0x15c019f0, 0x1c021b00, +0xc828f0c2, 0x001f0037, 0x09f0031c, 0xdf1005d0, 0xf0037c10, 0x080fc007, +0x037c0037, 0x07c009f0, 0x00000c00, 0x00000000, 0x001f0080, 0x42ec00bf, +0x7ed01330, 0xcc003300, 0xc81d3103, 0x03f2103c, 0x1b318bec, 0xe3006fc0, +0x344d4e80, 0x003fc207, 0x058c00f3, 0x00c00b30, 0x00000c22, 0x00000000, +0x00c62081, 0x460c111d, 0x1444003b, 0x6c031b04, 0x4001b00f, 0x010f0014, +0x45b00744, 0xdb400700, 0xb40b5480, 0x000e4027, 0x85440031, 0x07c009b0, +0x00000802, 0x00000000, 0x12348001, 0x0744219d, 0x34404910, 0x44411521, +0x40651047, 0x00550024, 0x49500364, 0xd1023300, 0x50086480, 0x08314425, +0x1b4400d5, 0x04400d10, 0x00000200, 0x00000000, 0x00202010, 0x0306000d, +0x12401892, 0x24000d00, 0x40001c03, 0x00051024, 0x08910204, 0xc1000340, +0xd0023400, 0x60024004, 0x01050001, 0x43400890, 0x00000080, 0x00000000, +0x0016a000, 0x03cc00ad, 0x34c00114, 0x4c002540, 0xd0051003, 0x00151024, +0x0970036c, 0xe10033c0, 0x74006400, 0x0033c205, 0x014c00d7, 0x00c00930, +0x00000ac0, 0x00000000, 0x000f9805, 0x03e0003f, 0x1dd20350, 0xfc003b00, +0xc003e103, 0x003f000b, 0x0bf802fc, 0xff101fc0, 0xb102dc00, 0x000fc007, +0x81fc803f, 0x15c00af0, 0x00000e60, 0x00000000, 0x048fa803, 0x30ec00b2, +0x3dc08370, 0x7c601300, 0xc00ef053, 0x04df083c, 0x43600bfc, 0x2c301fc0, +0x74135e44, 0x013cc02f, 0x00cc14f3, 0x0e820f30, 0x00000e00, 0x00000000, +0x00971005, 0x084c029b, 0x36484118, 0xf4001100, 0x400dd00b, 0x02f12c34, +0x6d110bc5, 0x1d141749, 0x1023dc02, 0x20bd406f, 0x200102f3, 0x0c410ff0, +0x00000c20, 0x00000000, 0x9103a011, 0x08240215, 0x22480510, 0x14544500, +0x481d9003, 0x08c52075, 0x20501b04, 0x01311340, 0x10032002, 0x0232402c, +0x503400c5, 0x4f400c50, 0x00000e80, 0x00000000, 0x81958007, 0x04540015, +0x36400554, 0x74001500, 0x401dd003, 0x00d12075, 0x0d110344, 0x99005740, +0x50835404, 0x0035400d, 0x107040d1, 0x0c400d50, 0x00000620, 0x00000000, +0x4027a800, 0x406cc095, 0xb4d00911, 0x58a41444, 0xc00d8003, 0x00df0035, +0x09710368, 0x9a0917c4, 0x71034c40, 0x4034800d, 0x187d00d7, 0x0ac00d50, +0x00000e22, 0x00000000, 0x103d8087, 0x01cc09bb, 0x3dc40bb0, 0xf8141b01, +0xd00fd803, 0x10ff003e, 0x4ff0037c, 0xef003f84, 0xb003bc21, 0x0037c80f, +0x808c00f7, 0x3fc00ff0, 0x00000600, 0x00000000, 0x00210802, 0x085c401b, +0x37c089f1, 0x7c021300, 0xc90d3043, 0x00db4036, 0x09f1033c, 0x1f0097c0, +0x32137c40, 0x0036c80c, 0x495c20d3, 0x0bc80d34, 0x00000420, 0x00000000, +0x0134a013, 0x03c411a1, 0x64400ed0, 0xc4027320, 0x420c1017, 0x03ef2030, +0x0d1003d4, 0xfd003740, 0x104bc40a, 0x00ba420f, 0x014400f1, 0x6f408f90, +0x00000200, 0x00000000, 0x0002a007, 0x01562088, 0x77405090, 0x14030502, +0x40bcd50f, 0x20c10032, 0x04900314, 0x4921b340, 0x92072700, 0x24f3420c, +0x2d0000d1, 0x1f600d90, 0x00000a00, 0x00000000, 0x02488804, 0x058441a1, +0x7a4012d1, 0x940d2d42, 0x401e1087, 0x19e9287a, 0x1e900794, 0x6d005b40, +0x9017c541, 0x057b429e, 0x058429e9, 0x37441e91, 0x00000200, 0x00000000, +0x00240012, 0x011c000b, 0x27c005d0, 0x1c1d4700, 0xc00cf017, 0x2dd32036, +0x14f0233c, 0x4f0373e0, 0xb4172c00, 0x0073c00c, 0x050d05d2, 0x4bc00c20, +0x00000040, 0x00000000, 0x002da802, 0x01fc0027, 0xadd007f0, 0x0c307308, +0xc90f3863, 0xc8f7023d, 0x0f300bcc, 0xfe0017c0, 0x70638c08, 0x063cc00c, +0x016c28d7, 0x0bc10f70, 0x00000660, 0x00000000, 0x1017a011, 0x017c009b, +0xb64081f0, 0x5c920f00, 0xec5c0103, 0x93df01b5, 0x1df00b60, 0xbf2035c0, +0x34cb1000, 0x2136c0ad, 0x006c04d9, 0x57402df2, 0x00000e00, 0x00000000, +0x801d8812, 0x05bc02a0, 0x30404a10, 0xa8a42b00, 0x0cce3033, 0x42ec9538, +0x0e701384, 0xed813942, 0x50139601, 0x0238408f, 0x00840ec1, 0xcf414ed0, +0x00000624, 0x00000000, 0x00790083, 0x25240121, 0x6b501250, 0x04256120, +0x421e1017, 0xa5c90379, 0x1c90a784, 0x3d077a40, 0x1407c403, 0x4979405e, +0x069481e1, 0x07481ed0, 0x00000400, 0x00000000, 0xd2372812, 0x055440c1, +0x85413c10, 0x0406c90c, 0x41ac9203, 0x00c904f0, 0x2c500314, 0xdd04b340, +0x50031410, 0x0031400c, 0x173400c1, 0x4b400cd0, 0x00000c20, 0x00000000, +0x00dda817, 0x0de05373, 0xd7c07770, 0xc5075101, 0x44231001, 0x005900cd, +0x0790014c, 0x7f00ce42, 0x31019403, 0x2015c806, 0x11dc005b, 0x5fc004f0, +0x00000620, 0x00000000, 0x01070012, 0x247c0117, 0x04d00174, 0x3e001f00, +0xe0213080, 0x001f0207, 0x01f1804c, 0x1f8005c1, 0xb0007c80, 0x20845021, +0x00488017, 0x4bc001f0, 0x00000c00, 0x00000000, 0x00270810, 0x027c059b, +0x34c00930, 0x5000d100, 0x4011b002, 0x209d0007, 0x09f0027e, 0x9d300744, +0x90225405, 0x1025c099, 0x224c0091, 0x40c009f0, 0x00000c20, 0x00000000, +0x00260005, 0x82740091, 0x24c0a910, 0x7c009100, 0x44015102, 0x00910002, +0x09d20274, 0x9d080744, 0x100a4f00, 0x01a6c009, 0x46440091, 0x07c02970, +0x00000800, 0x00000000, 0x4024a01c, 0x02540091, 0x24400c14, 0x44008540, +0x4061901a, 0x00990007, 0x09d00274, 0x99002740, 0x50025440, 0x00224009, +0x02448085, 0x604009d1, 0x00000200, 0x00000000, 0x00202010, 0x1a240081, +0x20600810, 0x34148502, 0x428810a2, 0x02c110a7, 0x88d12234, 0x8d022340, +0x54227408, 0x02224008, 0x22060285, 0x43508c50, 0x00000080, 0x00000000, +0x0706b019, 0x105e1413, 0x04c0a430, 0x4c041724, 0x4961b058, 0x541f1507, +0x61f05874, 0x1d0d87e1, 0xf0595456, 0x0503c1c5, 0x584d1417, 0x74d161f0, +0x00000ac0, 0x00000000, 0x013f9019, 0x1afc20bf, 0x66106bf0, 0x5c909b00, +0xc84b7012, 0x209b112e, 0x4bf0127c, 0xaf012fc0, 0xb0124c04, 0x4927c049, +0x12fc449b, 0x67c049f0, 0x00000e64, 0x00000000, 0x29afa818, 0x627654b3, +0x2cc14af0, 0xbc02b300, 0xc01bf012, 0x06bf0067, 0x29f01a4c, 0xa7116fc0, +0x30128c07, 0x052cc02a, 0x12ec0c93, 0x60c04af0, 0x00000e00, 0x00000000, +0x01870898, 0x10740011, 0x00c32110, 0x74045144, 0x4051d014, 0x041d0503, +0x81d0103c, 0x110d8740, 0x10744482, 0x04844071, 0x5054020b, 0x72c001f0, +0x00000c20, 0x00000000, 0x0323a010, 0x123400c1, 0x20644851, 0x34048102, +0x4328d04a, 0x068d00a3, 0x48d21a14, 0x95232500, 0x10020446, 0x68234029, +0x1a052881, 0x486028d0, 0x00000e80, 0x00000000, 0x0025a018, 0x02748091, +0xa0420d10, 0x70019100, 0x4001d000, 0x009d1027, 0x09d00274, 0x91002740, +0x12004402, 0x00234009, 0x02540091, 0x62400950, 0x00000620, 0x00000000, +0x40a70005, 0x4a741193, 0x645019f0, 0x74039320, 0x45b1d000, 0x009f06a7, +0x09f0024c, 0x850063c0, 0x34004500, 0x0025b009, 0x326c0091, 0x14f009d0, +0x00000e20, 0x00000000, 0x00250010, 0x027c039f, 0xa5c049f0, 0x7c009f00, +0xc411f000, 0x009f0027, 0x09f0026c, 0x9f026740, 0xf0407810, 0x0024e009, +0x066c008f, 0x59e009f0, 0x00000600, 0x00000000, 0x00850014, 0x085c0213, +0x06e04170, 0x4c001300, 0x40613010, 0x20130085, 0x01f0004c, 0x130004c0, +0x30002408, 0x0006c401, 0x084d0017, 0x50c01070, 0x00000420, 0x00000000, +0x40540814, 0x01748051, 0x16e037b0, 0xc4027108, 0x40333100, 0x82610017, +0x0510014c, 0x7d405c40, 0x1004d480, 0x001c45b6, 0x01c40055, 0x50403750, +0x00000200, 0x00000000, 0x0236a014, 0x03540081, 0x30461910, 0x1606c1a0, +0x603c1403, 0x04c90031, 0x0c980304, 0xc1027040, 0x112f2602, 0x0031600c, +0x030600c1, 0x52401c18, 0x00000a00, 0x00000000, 0x80288004, 0x03f400a1, +0x28410791, 0x94013900, 0x40ae988f, 0x00a9007b, 0xde100784, 0x6d083c40, +0x90139540, 0x2229400e, 0x07c404e1, 0x16414658, 0x00000200, 0x00000000, +0x007c1034, 0x179c01a1, 0x78501670, 0x9e41e342, 0xc89e3017, 0x254b4079, +0x1ff0178c, 0xe30078c0, 0x340fac81, 0x0173c00e, 0x878c8be7, 0x56d01670, +0x00000040, 0x00000000, 0x0025b010, 0x073c209f, 0xa7c009f1, 0x6f000701, +0xc26d706b, 0x081700b7, 0x2df0437c, 0x570033e0, 0x70c35e86, 0x0006c01f, +0x803c04df, 0x41c085f0, 0x00000660, 0x00000000, 0x007fa002, 0x47f801bf, +0x77d01f38, 0xfc01ff00, 0xc29df007, 0x0df784ff, 0xbdf0477c, 0xf7017dc0, +0x71076c03, 0x1276c01f, 0x07cc01f6, 0x00c21b30, 0x00000e00, 0x00000000, +0x00298815, 0x039c00af, 0x68444f18, 0x9c14a900, 0x001e7023, 0x01a1023f, +0x1cd007b4, 0x6100f040, 0x1017ac82, 0x0067489e, 0x474405c3, 0x56c00210, +0x00000620, 0x00000000, 0x02390000, 0x23b400ad, 0x38400e10, 0xa440ed20, +0x41ce9003, 0x2069043b, 0x0e9083a4, 0xa10b3841, 0x12132408, 0x0038400e, +0x338404ed, 0x00400210, 0x00000400, 0x00000000, 0x00632806, 0x07541285, +0xe0458c10, 0x14038900, 0x400c5103, 0x00090032, 0x0cd00364, 0x01007440, +0x14033400, 0x8003400c, 0x000400c1, 0x12000010, 0x00000c20, 0x00000000, +0x0075a815, 0x1bfc009d, 0xa4501930, 0x2c001f82, 0xc02d9a83, 0x803840bf, +0x3f9203ec, 0x4126b448, 0x32032c98, 0x002ec00e, 0x074d00ff, 0xd4500b30, +0x00000620, 0x00000000, 0x02270001, 0x135c019f, 0x34c00df4, 0x5c021b22, +0xc44df083, 0x02172137, 0x0cf0037c, 0x5f0097d0, 0xf0034c00, 0x00b5c02d, +0x037c00df, 0x04e020f0, 0x00000c00, 0x00000000, 0x007b0880, 0x036c40ab, +0x2cc00eb0, 0xfe103302, 0xc00f7007, 0x013d003e, 0x0f3103d4, 0x72207c41, +0x1113f420, 0x00cc4215, 0x03d400f7, 0x000089f0, 0x00000c22, 0x00000000, +0x00262081, 0x83440095, 0x35402910, 0x74721110, 0x400cb0a7, 0x120d0035, +0x0d10036c, 0x5b08d6c0, 0x100f5400, 0x21b44185, 0x004440d7, 0x044201d1, +0x00000800, 0x00000000, 0x0224a001, 0x03640191, 0x20400d90, 0x76081140, +0x401d1043, 0x28192034, 0x0d108364, 0xdd022540, 0x508b6600, 0x00214c05, +0x8344e0c1, 0x054025d0, 0x00000200, 0x00000000, 0x00300010, 0x03040085, +0x31400410, 0x76000141, 0x405d16a3, 0x000d2031, 0x4c141324, 0x5d130340, +0x500b1620, 0x11316105, 0x0b1410c1, 0x415004d0, 0x00000080, 0x00000000, +0x00263000, 0x036c0091, 0x6c4805b0, 0x740a1301, 0xc41d180b, 0x471f003e, +0x1e1087ec, 0xaf0861e2, 0x741f6c80, 0x0345d051, 0x0f5c0fe7, 0x01e005f2, +0x00000ac0, 0x00000000, 0x203fb805, 0x03f000f7, 0x3fc00ff0, 0xfc05380a, +0xc08ff007, 0x9d9f003f, 0x9fc1a3ec, 0x3b01c6c1, 0xb10ffc00, 0x4b7ec001, +0x346c05d6, 0x16c407f0, 0x00000e60, 0x00000000, 0x020fa003, 0x03bc003b, +0x2cc083f0, 0xfc003700, 0xc647f803, 0x00fb003f, 0xcf300bcd, 0xb7000ed2, +0xf0008e00, 0x000fc00b, 0x00ccc033, 0x0fc06f30, 0x00000e00, 0x00000000, +0x80071801, 0x53740811, 0x90404570, 0x74801300, 0x0025d083, 0x62d103b3, +0xed900b44, 0x81002c40, 0xd0285400, 0x30344009, 0x81c420d1, 0x0f402d30, +0x00000c20, 0x00000000, 0x2023a011, 0x83140001, 0x224800d0, 0x34000902, +0x40245082, 0x22090433, 0x4c921804, 0x85202660, 0x50810600, 0x20354008, +0x02248005, 0x4f40cc90, 0x00000e80, 0x00000000, 0x08b5a003, 0x03740211, +0x16402450, 0x74061900, 0x4025d003, 0x00191437, 0x8d900144, 0xd1082460, +0xd0015481, 0x0034400d, 0x476500d5, 0x1f421d10, 0x00000620, 0x00000000, +0x40c7a886, 0x037c0011, 0xe6d029f0, 0x7c021b02, 0xc021f001, 0x10db8887, +0x0d300b4d, 0x970246c8, 0xf0094401, 0x0435801d, 0x056c00d7, 0x23e009a4, +0x00000e22, 0x00000000, 0x81498007, 0x03fc0077, 0x1dc00bf0, 0xfc003340, +0x8007f003, 0x00e7400f, 0x0f6003bc, 0xef002fc0, 0xf001fc00, 0x083dc02f, +0x01d800eb, 0x0fc20bf0, 0x00000602, 0x00000000, 0x01250802, 0x027c0a5f, +0x23c08d30, 0x7c001700, 0xc025f003, 0x42570091, 0x0c34085c, 0x9f0425c0, +0x308b6c02, 0x0136c009, 0x0b7c80df, 0x2bc00934, 0x00000420, 0x00000000, +0x0034a013, 0x02764057, 0x17400d12, 0x74001502, 0xc727d007, 0x00510456, +0x0d104144, 0xdf0020d0, 0x1483cc0b, 0x0130405c, 0x2f7402dd, 0x4f000914, +0x00000200, 0x00000000, 0x00922003, 0x2724000d, 0xa3420092, 0x34100408, +0x4054d003, 0xa0c1c031, 0x00101717, 0x8d028241, 0x10002408, 0x08c34018, +0x27740e0c, 0x1f420858, 0x00000a00, 0x00000000, 0x004a0800, 0x07f40125, +0x6b401690, 0xb4012504, 0x4016d007, 0x21c18178, 0x94100704, 0xad006842, +0x1000e481, 0x1059441a, 0x27b4a9e9, 0x3f403a50, 0x00000200, 0x00000000, +0x02121016, 0x0334080f, 0xa3c00834, 0x34880700, 0x6404d0c2, 0x00871031, +0x20300a1c, 0xcf0021d0, 0x34292c02, 0x0013c00c, 0x017c080f, 0x4bc00870, +0x00000040, 0x00000000, 0x101d8802, 0x23fc2837, 0x3fc80f70, 0xfc203f00, +0xc006f003, 0x00bf013f, 0x35f0237c, 0xf7022fc0, 0xf011dc40, 0x001ec00e, +0x01fc08ff, 0x1bc00bb0, 0x00000660, 0x00000000, 0x0037a010, 0x075c021f, +0x27c00130, 0x7c809720, 0xc001b885, 0x00d30027, 0x2935036c, 0xd70110c0, +0x3405cc00, 0x0017c01d, 0xcb4c00df, 0x47c40d34, 0x00000e00, 0x00000000, +0x00299912, 0x039c10ed, 0x0f480210, 0xf400a100, 0x40461003, 0x00e11023, +0x0c100384, 0xf1043850, 0x1019ec00, 0x001b400e, 0x038400ed, 0x4f400ef0, +0x00000624, 0x00000000, 0x00790004, 0x06b483ed, 0x6b403e58, 0xb401ad00, +0x40969007, 0x01c1007b, 0x1a900624, 0x64007c50, 0x12078601, 0x005b400e, +0x070400ed, 0x07401e90, 0x00000402, 0x00000000, 0x00330012, 0x021450cd, +0x33400c50, 0x3400c901, 0x448c1007, 0x08d100f3, 0x2c820304, 0x41003040, +0x10812407, 0x0053400c, 0x03041acd, 0x4b400cd4, 0x00000c20, 0x00000000, +0x009f8817, 0x155c015f, 0x1fc03756, 0xfc077f00, 0xc036b16d, 0x01514617, +0x04b0016c, 0x77011cc0, 0x3049cc05, 0x00dfc115, 0xc98c037f, 0x5fc005b8, +0x00000620, 0x00000000, 0x24058012, 0x005c020f, 0x87c901b0, 0x7c831700, +0xc011f000, 0x001f2007, 0x0170003d, 0x1f0007c9, 0xf2847c00, 0x0247c001, +0x407c000f, 0x4bc021f0, 0x00000c00, 0x00000000, 0x00e50810, 0x065c89d3, +0x20c018f0, 0x7c019300, 0xe049f002, 0x00930260, 0x5935024c, 0x8b0021d0, +0xf0024c04, 0x00e44008, 0x0a4c429f, 0x40c018b8, 0x00000c20, 0x00000000, +0x00262001, 0x266c1391, 0x24400970, 0x74329501, 0xc009d006, 0x00910026, +0x19500a54, 0x91002540, 0xd0027c72, 0x00246009, 0x0244009d, 0x06c42912, +0x00000800, 0x00000000, 0x0020a01c, 0x02440099, 0x244089c0, 0x74089180, +0x4009d006, 0x00b10025, 0x0b1006c4, 0x99002748, 0xd0024400, 0x20274009, +0x0244029d, 0x60446990, 0x00000200, 0x00000000, 0x42302814, 0x22254089, +0x205088d0, 0x342085c2, 0x4149d022, 0x16a1472b, 0xce105394, 0x89002341, +0xd00a1408, 0x20a04008, 0x0a45028d, 0x42408804, 0x00000080, 0x00000000, +0x0586b01d, 0x0814141b, 0x84c161f0, 0x7c001305, 0x4041f108, 0x04130185, +0x6322104c, 0x1b2817c0, 0xf0d04416, 0x2805c001, 0x004c800f, 0x74c161b5, +0x00000ac0, 0x00000000, 0x112fa919, 0x12dc0097, 0x2fc04b70, 0xfc00bf09, +0xc00bf012, 0x809f0126, 0x49f0527c, 0xb700adc4, 0xf002fc24, 0x002ec02b, +0x0afc22bf, 0x67c04bf4, 0x00000e64, 0x00000000, 0x062fa818, 0x227c00b7, +0xa7c809f0, 0xfc00b305, 0xc00af00a, 0x18970227, 0x4b3052cc, 0xbb002ce1, +0x7012e810, 0x0820c34b, 0x12cc00af, 0x64c06f30, 0x00000e00, 0x00000000, +0x8087081c, 0x00740a11, 0x8741e1d0, 0x74001b01, 0x40a5d010, 0x82050387, +0x21301854, 0x1b028464, 0x10086c00, 0x10044021, 0x08044017, 0x72c00110, +0x00000c22, 0x00000000, 0x01210012, 0x1236018d, 0x234008d0, 0x34008981, +0x4008d002, 0x848d0023, 0x0814c214, 0x81002061, 0x508a0410, 0x0d214008, +0x0a24148d, 0x48422810, 0x00000e80, 0x00000000, 0x00e52818, 0x02760199, +0x270229d0, 0x74019901, 0x4009d022, 0x008d4127, 0x09110254, 0xd8002442, +0x10426402, 0x0d255a09, 0x02240095, 0x62400d10, 0x00000620, 0x00000000, +0x0027a005, 0x2274039d, 0xa7c019f0, 0x74019b01, 0xc029f01a, 0x1b9d0027, +0x08326e5c, 0x9304a010, 0x700a4c23, 0x0065c009, 0x1269009f, 0x14c00934, +0x00000e20, 0x00000000, 0x00258012, 0x037c0097, 0x37c05df1, 0x7c009f00, +0xc099f002, 0x00970037, 0x0970027c, 0x8f0027c0, 0xf0963c04, 0x0076c039, +0x025c109f, 0x5bc208f8, 0x00000600, 0x00000000, 0x0e010010, 0x207c0417, +0x01c001f0, 0x7c001308, 0xc441f008, 0x20130000, 0x1130004c, 0x130006c8, +0x30a04c00, 0x2004c001, 0x084c0003, 0x50c01130, 0x00000420, 0x00000000, +0x001ca014, 0x0174127d, 0x154815d0, 0x74117502, 0x60079015, 0x00510414, +0x271001d4, 0x77001d40, 0x10016c22, 0x04140314, 0x15ec0275, 0x51402710, +0x00000200, 0x00000000, 0x0032a014, 0x0724018d, 0xb14004d0, 0x3411c900, +0x482cd203, 0x00c14050, 0x18140746, 0x85207150, 0x10070485, 0x2030401c, +0x07150241, 0x50480c10, 0x00000a00, 0x00000000, 0x00388001, 0x03b480ad, +0x184436d0, 0xb440ed04, 0x401e9203, 0x0ce10010, 0x68110994, 0x65041940, +0x1a03a402, 0x004c140e, 0x83b51065, 0x15400410, 0x00000200, 0x00000000, +0x00781011, 0x07bc81af, 0x5dc114f2, 0xbc01eb42, 0x401ed207, 0x21e30058, +0x7e34040c, 0xb7007140, 0x3807cc49, 0x2268801e, 0x079d11e3, 0x54c01234, +0x00000040, 0x00000000, 0x0035b810, 0x4a7c409f, 0x17c065f0, 0x7800d711, +0xd009b003, 0x14df0187, 0x0df0007c, 0x9f0035c0, 0xf4037c84, 0x0007c00c, +0x036d04df, 0x43c001f0, 0x00000660, 0x00000000, 0x027da800, 0x27cc09b3, +0x6fc01730, 0x8c09e324, 0xc61f3027, 0x01f300dd, 0x1f3007cc, 0xf7007ec0, +0x30078c01, 0x007dc0de, 0x12cc04ff, 0x08c41f30, 0x00000e00, 0x00000000, +0x041d1815, 0x03ac08a3, 0x0f4006b0, 0x84406704, 0x41063003, 0x28f1001a, +0x0f1800ac, 0xfb081941, 0x10139400, 0x001840ce, 0x828404bd, 0x554007b0, +0x00000620, 0x00000000, 0x10290000, 0x03843029, 0x0b400218, 0xc480f100, +0x402e1103, 0x00e9421b, 0x0e1400a4, 0xe5003340, 0x1203e410, 0x002960ce, +0x021442ed, 0x21408a90, 0x00000400, 0x00000000, 0x00412004, 0x03240109, +0xc3448091, 0x0600c504, 0x4010103b, 0x00c91002, 0x1c022424, 0x89003140, +0x90032420, 0x2491400c, 0x0214008d, 0x19400010, 0x00000c20, 0x00000000, +0x00752015, 0x024c0191, 0xbfc0053a, 0x4d01d300, 0xc41d300f, 0x00f90015, +0x17120364, 0x550032c0, 0x34a36c01, 0x0035c00d, 0x025c035f, 0x75c00190, +0x00000620, 0x00000000, 0x00b70001, 0x027c0697, 0x87c001f0, 0x7c01d700, +0xc00d7083, 0x00c70011, 0x05f0837c, 0x5f0107c0, 0x70035c02, 0x10564219, +0x856c81dd, 0x074029f0, 0x00000c00, 0x00000000, 0x801d0084, 0x23fc03bf, +0xdfc05730, 0xfc00ff08, 0xc00df803, 0x20f34017, 0x5e40000c, 0x63009dd0, +0xe00bcc00, 0x082dc01f, 0x078c00a3, 0x04d00380, 0x00000c20, 0x00000000, +0x04960085, 0x8264859d, 0x85c01140, 0x5e08dd00, 0x44097002, 0x00d90006, +0x0df00444, 0x1b0447c0, 0xd0074401, 0x1a164419, 0x0444419b, 0x24402054, +0x00000800, 0x00000000, 0x0034a001, 0x0224009d, 0x87400500, 0x6400dd28, +0x400dd007, 0x00d90107, 0x05500e45, 0xd1002741, 0xd0024401, 0x0033484d, +0x1a6403d1, 0x04408158, 0x00000200, 0x00000000, 0x88102810, 0x0224408d, +0x00420014, 0x14804900, 0x40045003, 0x00c90003, 0x04140204, 0xc9000250, +0xd0020400, 0x2002400c, 0x01258089, 0x40400854, 0x00000080, 0x00000000, +0x0006b000, 0x032c00dd, 0x07c00131, 0x74009d00, 0xc00dd003, 0x00f31007, +0x0552004c, 0xf38005c8, 0xf0024c00, 0x2025c00d, 0x036c80f3, 0x04c00174, +0x00000ac0, 0x00000000, 0x000f8805, 0x03ec00af, 0x0dc403f0, 0xfc00bf10, +0xc0037003, 0x00f3100e, 0x0ff880fe, 0xbf000dc0, 0xf002fc00, 0x000fc00f, +0x00dc00bf, 0x17c80b70, 0x00000e60, 0x00000000, 0x023f8003, 0x03ac00ff, +0x34c40fb0, 0xfc08f300, 0xc00c300b, 0x42eb003c, 0x0f700b7c, 0xf3003ed0, +0xf0237000, 0x00bfc02f, 0x73cc00fb, 0x0cd003b0, 0x00000e00, 0x00000000, +0x00071801, 0x004c201d, 0x06c80112, 0x34001b01, 0x44415000, 0x00150105, +0x01700074, 0x1520044c, 0xd0007484, 0x01074201, 0x0bc40011, 0x04420110, +0x00000c60, 0x00000000, 0x1133a011, 0x030414cd, 0x30420410, 0x3404c504, +0x430c1013, 0x04410411, 0x04509334, 0x41083040, 0xd0012410, 0x00334044, +0x13241441, 0x444005d0, 0x00000e80, 0x00000000, 0x00858007, 0x8064201d, +0x04428910, 0x74241541, 0x42215000, 0x00958025, 0x09d01874, 0x95080442, +0xd0027400, 0x00034049, 0x03250498, 0x0c428154, 0x00000600, 0x00000000, +0x00afa882, 0x21cc287f, 0x5c501f34, 0xfcd1f500, 0xc02b3021, 0x88f3402d, +0x0a720ffc, 0xb30a1cc0, 0xf002ec00, 0x24bf401f, 0x834c01fb, 0x08e001f0, +0x00000e22, 0x00000000, 0x001d8007, 0x869d01ae, 0x27c802f0, 0xb8003b00, +0xc006c006, 0x0137081f, 0x077284fc, 0x7f106fc0, 0xf001fc00, 0x420f4803, +0x03dc2037, 0x1fc001a0, 0x00000600, 0x00000000, 0x00a50802, 0x314c0053, +0x154005f0, 0x4c0ad300, 0xc0093001, 0x00530004, 0x41f0034c, 0x1f0016c0, +0x30805c00, 0x01b6c005, 0x035c185f, 0x09c481f1, 0x00000420, 0x00000000, +0x0014a013, 0x0e440191, 0x20600911, 0x6c001b00, 0x40051582, 0x609b0175, +0x2dc0806d, 0xdd202440, 0xf00b7405, 0x00044209, 0x07c5018d, 0x4c42b620, +0x00000200, 0x00000000, 0x40122003, 0x8e140085, 0x21440851, 0x24810900, +0x40045802, 0x80890036, 0x1cd00024, 0xcd002640, 0x104b1401, 0x00006208, +0x67040389, 0x0d403050, 0x00000800, 0x00000000, 0x0a620804, 0x05840141, +0x58445412, 0xa609c902, 0x405a1105, 0x2169104a, 0x12d007a4, 0x2d825841, +0x9014b401, 0x00784016, 0x0784896d, 0x10401214, 0x00000820, 0x00000000, +0x00121012, 0x021c1087, 0x61500050, 0x2c080301, 0xc4143006, 0x08094016, +0x45f2140c, 0x4d0062c0, 0x32955c00, 0x0700d051, 0x175c091f, 0x49c00470, +0x00000840, 0x00000000, 0x002da002, 0x01fc007f, 0x1fc04f70, 0x7428df20, +0xc00bf011, 0x00df122d, 0x0bf023cc, 0xbf021dc0, 0xf0027c00, 0x023de08f, +0x23fc01ff, 0x0bc007f0, 0x00000620, 0x00000000, 0x0047a014, 0x004d8117, +0x06c01934, 0x4c001340, 0xd001b000, 0x209f0024, 0x0932007c, 0x9f0007c0, +0xd2026d80, 0x00044a19, 0x4b49019f, 0x43404130, 0x00000e00, 0x00000000, +0x20399932, 0x030400f1, 0x30400610, 0x8400f108, 0x400c1003, 0xc061001c, +0x061003f0, 0x41003f40, 0xd0018400, 0x00384606, 0x1b84006f, 0x4b40e218, +0x00000664, 0x00000000, 0x40090000, 0x04a40125, 0x48401010, 0xa4812900, +0x40121c04, 0x0125004a, 0x12d00496, 0x25104b40, 0xd2848401, 0x20484012, +0x1704410d, 0x13481650, 0x00000400, 0x00000000, 0x08732036, 0x0b2490c1, +0xb0420c12, 0x2400d924, 0x401d9203, 0x40c50072, 0x1cd88334, 0xd1003340, +0xd0070401, 0x0030400c, 0x030420c5, 0x5b400c50, 0x00000c00, 0x00000000, +0x0017a817, 0x01648057, 0x14d00530, 0x65005b00, 0x4005b009, 0x00570016, +0x05f0017c, 0x550217c0, 0xf1014ca0, 0x0014c005, 0x0146007d, 0x5fc10570, +0x00000e20, 0x00000000, 0x220d8012, 0x40dc823f, 0x8dc003f2, 0xdc403700, +0xc0837a10, 0x0033120d, 0xa31200fc, 0x37000fc4, 0xf020dc08, 0x200ee003, +0x087c803f, 0x4bc021a0, 0x00000600, 0x00000000, 0x00210810, 0x023c0093, +0x27e00930, 0x6c019300, 0xc0093202, 0x00830a24, 0x0830020c, 0x930022c0, +0x30025c00, 0x0867c009, 0x827c009f, 0xc3c10d31, 0x00000420, 0x00000000, +0x00262001, 0x82748291, 0xe7000910, 0x140f9303, 0xc4091702, 0x009b9025, +0x2950025c, 0x9d082540, 0xa0024400, 0x00674009, 0x0a740097, 0x05c08900, +0x00000800, 0x00000000, 0x4024801c, 0x02f402b1, 0x2b400b10, 0xe600b100, +0x400b1882, 0x20b10028, 0xab1002c4, 0xb9102d40, 0x11029400, 0x022f400b, +0x421400bd, 0x73440d14, 0x00000200, 0x00000000, 0x00282814, 0x02b408a9, +0x2b402a10, 0x9408a100, 0x400a1802, 0x88a92229, 0x2a522294, 0xad002940, +0x80228480, 0x023b408a, 0x0a3409a1, 0x51414c14, 0x000000a0, 0x00000000, +0x0802b01d, 0x00740213, 0x07400114, 0x2c001342, 0xc2813080, 0x82114004, +0x0110084e, 0x131007c0, 0x3008540a, 0x0087c020, 0xd07c803f, 0x77c0e120, +0x00000ac0, 0x00000000, 0x04a7a119, 0x0a7c0697, 0xa7c069f0, 0x7c069f11, +0xc469f04a, 0x069f00a7, 0x29f01a5c, 0x9784e7c0, 0xf01a7c06, 0x11a7c069, +0x127c069f, 0x67c049f0, 0x00000e24, 0x00000000, 0x41efa018, 0x16ec05b3, +0x6dc63930, 0x4c01b301, 0xc0d9301e, 0x0393056c, 0x5900064c, 0xb30167c0, +0x3016cc05, 0x036cc079, 0x5aec03b3, 0x63c029f1, 0x00000e00, 0x00000000, +0x0087101c, 0x80440401, 0x05c0e1b0, 0x54821f00, 0x40005008, 0x001b0584, +0xa110503e, 0x15078740, 0x10004c14, 0x00044001, 0x184c0011, 0x734041f8, +0x00000c60, 0x00000000, 0x02210812, 0x0a051681, 0xa0400814, 0x05168105, +0x40681022, 0x86850020, 0x081e0a05, 0x81102340, 0x144a1702, 0x01a07068, +0x02050681, 0x4b4008d0, 0x00000400, 0x00000000, 0x2025a018, 0x02440091, +0x264a0990, 0x54009500, 0x40091002, 0x20990024, 0x09100254, 0x95002740, +0x10025400, 0x00244009, 0x02660091, 0x63401950, 0x00000400, 0x00000000, +0x00278805, 0x02440093, 0x24c30930, 0x4c009100, 0xc0093002, 0x129504a4, +0x29300244, 0x930027c1, 0x104a5c12, 0x00244129, 0x024c1293, 0x17c449d2, +0x00000e20, 0x00000000, 0x40258032, 0x021c008f, 0x25d00970, 0x3c108f00, +0xd008f402, 0x209f4023, 0x0df2023c, 0x8f0027c1, 0xf0020c00, 0x4023c008, +0x425c409b, 0x4bc00db0, 0x00000600, 0x00000000, 0x02010810, 0x004c2013, +0x00c00030, 0x4c001324, 0xc0017000, 0x02030084, 0xa138004e, 0x130000c4, +0x30084d02, 0x0007c421, 0x007c0603, 0x43c06134, 0x00000420, 0x00000000, +0x00548014, 0x05c50271, 0xdcc00530, 0xc40071a0, 0x60075001, 0x007f0014, +0x353801ec, 0x51301c40, 0xb16dec01, 0x201dc005, 0x05f41073, 0x53410530, +0x00000200, 0x00000000, 0x4032a014, 0x1d0486c9, 0x32500c90, 0x0409414a, +0x500cde03, 0xc0c14030, 0x3c900304, 0xc1c03050, 0x50032600, 0x0033680c, +0x0a3400c9, 0x53403090, 0x00000a00, 0x00000000, 0x40308001, 0x408420e9, +0x30400c12, 0x84006100, 0x408ed033, 0x04e51028, 0x54102380, 0xe1093048, +0xd2032630, 0x0039408e, 0x47b404e1, 0x07400a12, 0x00000620, 0x00000000, +0x00780011, 0x058c01eb, 0x4a405e94, 0x0d017100, 0x405ef117, 0x07f10060, +0x56901784, 0xf10178c4, 0x7207ac81, 0x007fc0de, 0x04fc03eb, 0x47c09e32, +0x00000040, 0x00000000, 0x08b5a810, 0x003800d3, 0x06c12d60, 0x78005f00, +0xc02d30db, 0x50df00b7, 0xc1f4137c, 0xdf0237c1, 0xb0037480, 0x0007402d, +0x036c005f, 0x43c42df5, 0x00000620, 0x00000000, 0x00eda000, 0x04fc01f3, +0x6cc11f30, 0xcc017f00, 0xc03f3007, 0x09f300fe, 0x11b00fec, 0xbb0275c4, +0x3113cc25, 0x0374c09f, 0x11cc01bf, 0x03c09b31, 0x00000600, 0x00000000, +0x06291815, 0x009c00e1, 0x28418f30, 0x144b6004, 0x490f1143, 0x01e50629, +0x52111394, 0xab067141, 0xb0159404, 0x0479420c, 0x2304000d, 0x57400e10, +0x00000460, 0x00000000, 0x40690000, 0x00340861, 0x28400e10, 0xa4806502, +0x408e5803, 0x60e50068, 0x405083a4, 0x81013a40, 0x1213264c, 0x103a480e, +0x230400ad, 0x03420e90, 0x00000408, 0x00000000, 0x00652004, 0x28148a40, +0x60401c90, 0x640a4119, 0x404c140b, 0x09c500b0, 0x30102b04, 0x8180f348, +0x1029300c, 0x008340ac, 0x0346008d, 0x93400c94, 0x00000400, 0x00000000, +0x40e5a815, 0x007c4a53, 0xd4c03f14, 0xed405500, 0xd03f100f, 0x20f340f4, +0xb77003ec, 0xd1107ec2, 0x10036c02, 0x0076400f, 0x034d45bd, 0x57c029b1, +0x00000620, 0x00000000, 0x21270001, 0x403c000f, 0x15c24d70, 0x5c104720, +0xc00cf013, 0x10db2125, 0x0410433c, 0x9f0431c0, 0xf8431c00, 0x0c31c10c, +0x826c109f, 0x07c13d70, 0x00000c00, 0x00000000, 0x402f0884, 0x00cc02fb, +0x6ec00fb0, 0xcc007300, 0x400f3003, 0x00eb402c, 0x133003cc, 0xeb003cc0, +0x3217ec01, 0x003cc00d, 0x03cc01b3, 0x10c00f30, 0x00000c22, 0x00000000, +0x02662085, 0x08440391, 0x66420d10, 0x54025b00, 0xc00d9403, 0x00db0037, +0x85b00344, 0x91003440, 0x90076c01, 0x0006c00d, 0x00450013, 0x15404d10, +0x00000802, 0x00000000, 0x40348001, 0x18640259, 0x30440c10, 0x46085102, +0x400d9003, 0x00d90031, 0x21900344, 0x91803440, 0xd0024408, 0x0034600d, +0x07442481, 0x04481894, 0x00000200, 0x00000000, 0x00302810, 0x02340001, +0x71500c14, 0x05444102, 0x400c9203, 0x04c91021, 0x40950b05, 0xc1413164, +0x97402620, 0xd132500c, 0x05042249, 0x41400898, 0x000000a0, 0x00000000, +0x403ab000, 0x0065005b, 0xa2c00f10, 0xcc01d100, 0x42af300b, 0x01eb0028, +0x11900bc4, 0x9312f4d0, 0x723e4c00, 0x9174c22f, 0x234c0083, 0x00c00db0, +0x00000ac0, 0x00000000, 0x003fa025, 0x02cc003f, 0x6ec00f38, 0xbc028f80, +0xc05f70d7, 0x02df003f, 0x23f087ac, 0xff00fac0, 0x60147c00, 0x020de15f, +0x07fc05f7, 0x17c20f70, 0x00000e20, 0x00000000, 0x020fb003, 0x59cc08af, +0x0ec04bf0, 0xec00b343, 0xc402b003, 0x00a7023e, 0x0b300acc, 0xff000ec1, +0x3002fc04, 0x4008c2cf, 0x33cc0cf3, 0x0cc00b30, 0x00000e00, 0x00000000, +0x00070003, 0x1344009c, 0x84494930, 0x95025302, 0x420df200, 0x2491203d, +0x25120344, 0x9d010448, 0x10024440, 0x000440cd, 0x331406c3, 0x0c400950, +0x00000c20, 0x00000000, 0x0033a013, 0x135444cd, 0x00546c5c, 0x4482dd10, +0x40005803, 0x30950132, 0x08101304, 0xcd040050, 0x50032482, 0x0003400c, +0x020408c1, 0x4c501110, 0x00000e80, 0x00000000, 0x0805a803, 0x075400dd, +0x05500c54, 0x4400d912, 0x420c5240, 0x00d10030, 0x04140344, 0x9d021740, +0x11037440, 0x0637401d, 0x025400d1, 0x0c401959, 0x00000620, 0x00000000, +0x0107a880, 0x045d019f, 0x04d09d70, 0x48c2cb00, 0x80055003, 0x00971036, +0x2d31074c, 0x5f0046c9, 0x54036c06, 0x00a6c00c, 0x23480093, 0x00c00830, +0x00000e22, 0x00000000, 0x001d8087, 0x02ec05bf, 0x5ec00fb0, 0xdc407300, +0xc00ff04b, 0x50bf0037, 0x0ff013fc, 0xef804c00, 0xe00bcc05, 0x006cc00f, +0x07fc40e7, 0x1fc009f0, 0x00000602, 0x00000000, 0x2035080a, 0x035d00df, +0x15c44ff0, 0x5c02d3c0, 0xc025340b, 0xa0930035, 0x0d20035c, 0x570836c0, +0xb4037c00, 0x0095c409, 0x034c0093, 0x28c80170, 0x00000420, 0x00000000, +0x0014a013, 0x030440dd, 0x94402d90, 0x0700f182, 0xc8bdb04b, 0x01c5003e, +0xafb00d04, 0xf10246c2, 0x10814480, 0x0074404d, 0x034400d5, 0x4ec00b10, +0x00000200, 0x00000000, 0x0002a007, 0x03240099, 0x456008d0, 0x04008102, +0x4808d403, 0x02810830, 0x08902714, 0xc5004340, 0x10924700, 0x4017410c, +0x037400c1, 0x1c4008d0, 0x00000a00, 0x00000000, 0x0468801d, 0x07b441bd, +0x58419e90, 0x8409f10a, 0x419ed027, 0x01a4007a, 0x1f920f80, 0xe1004a4a, +0x00278401, 0x005a421e, 0x27b400e5, 0x7e401a93, 0x00000200, 0x00000000, +0x00300012, 0x0b34088d, 0x15c089f0, 0x0402c102, 0xc008f203, 0x90830130, +0x0834035c, 0xd70406c0, 0x20435408, 0x2d23c00d, 0x023c88c3, 0x48c048f1, +0x00000040, 0x00000000, 0x302da802, 0x03cc00bf, 0x1e408fb0, 0xec90ef02, +0xc20fb28b, 0x00ff003e, 0x0ef0037c, 0xfa001ec0, 0x58035c52, 0x003d400f, +0x02cc04ff, 0x0bc00b70, 0x00000660, 0x00000000, 0x0007a015, 0x025c00df, +0x04c14904, 0x5d80c300, 0xc00c709f, 0x21832330, 0x0db0034c, 0xdf0484c4, +0x32067c00, 0x2034c00d, 0x03cc009b, 0x54c08830, 0x00000e00, 0x00000000, +0x00398913, 0x02b400e7, 0x1c440c30, 0xc405e102, 0x400e1003, 0x20a10538, +0x2e1003ec, 0xec000840, 0x1003dc04, 0x003ec00f, 0x03d400e5, 0x4c400e50, +0x00000624, 0x00000000, 0x00790081, 0x069661ed, 0x5a4d1a90, 0xa605e900, +0x523f5803, 0x01a1007a, 0x6f900785, 0xec207840, 0x9007b405, 0x1078400e, +0x06841189, 0x04405b10, 0x00000402, 0x00000000, 0x00332812, 0x033688cd, +0x12401c10, 0x0540c100, 0x400d183b, 0x00c12034, 0x1d508104, 0xcd005047, +0x9415340c, 0x00b0401c, 0x061401c5, 0x48408c53, 0x00000c20, 0x00000000, +0x001da817, 0x41dc015f, 0x16c005b0, 0xdc846b41, 0xc007700d, 0x00730014, +0x27b005c4, 0x4f00d4c0, 0x9001f400, 0x04580105, 0x4544405b, 0x5cd41610, +0x00000620, 0x00000000, 0x20070012, 0x007c8017, 0x05d00072, 0x1c001740, +0xe401f000, 0x401f5807, 0x01b0307c, 0x1f2607c0, 0x70205c00, 0x0107c401, +0x007d001f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x01270810, 0x027c0093, +0x34c80d70, 0x4c009700, 0xc0193026, 0x00931024, 0x19b0027a, 0xdf0034e0, +0xf0024c00, 0x1124c009, 0x023c0083, 0x40d00930, 0x00000c20, 0x00000000, +0x01e62001, 0x02740091, 0x24400918, 0x4d009b40, 0x5019300e, 0x089b0024, +0x19100236, 0x9d002454, 0xd1064400, 0x40a5c068, 0x027c0091, 0x04400911, +0x00000800, 0x00000000, 0x00208018, 0x02140091, 0x20600950, 0x64c095c0, +0x40ad1002, 0xc0952024, 0x49100274, 0x9d862450, 0xd0064400, 0x44244019, +0x02440091, 0x60440910, 0x00000200, 0x00000000, 0x62202010, 0x22360081, +0x20608850, 0x04148982, 0x40881023, 0x088d0120, 0x88100664, 0x9d022040, +0xd0230434, 0x40a34089, 0x02348081, 0x40408910, 0x00000080, 0x00000000, +0x0587b01d, 0x581c0011, 0x84d16150, 0x4c841705, 0xc0213058, 0x221702c4, +0x61342874, 0x1f208441, 0xf2584c8e, 0x0004e961, 0xf85c1e13, 0x74c02130, +0x00000ac0, 0x00000000, 0x012f9919, 0x12fc14bf, 0x27c049b1, 0xfc00bf41, +0x644b7012, 0x04bb0267, 0x4a7006fc, 0x9f0127d0, 0xd012f901, 0x00ade04b, +0x06fc01bf, 0x67c06bfe, 0x00000e64, 0x00000000, 0x2037a018, 0x0a7c8893, +0xa6816950, 0xd490b701, 0xe08b7092, 0x02a3132c, 0x0a300aec, 0x93002cc1, +0x7042cc00, 0x04ace06b, 0x524c1293, 0x64c06930, 0x00000e00, 0x00000000, +0x0287089c, 0x3d344010, 0xccc03110, 0x04025500, 0x40017000, 0x44114904, +0x2130805c, 0x01080540, 0x10814420, 0x00856461, 0x58542411, 0x72900458, +0x00000c22, 0x00000000, 0x0523a012, 0x02340481, 0x2a400a11, 0x5400850a, +0x6048905a, 0x00c100a0, 0x49901334, 0x88252041, 0x50422516, 0x00704028, +0x02340489, 0x48402810, 0x00000e80, 0x00000000, 0x21258818, 0x02340891, +0x2c400a10, 0x44089500, 0x02095202, 0x84912024, 0x09101270, 0x88042540, +0x10822400, 0x00654009, 0x82740099, 0x62480850, 0x00000620, 0x00000000, +0x44a52805, 0x2a7c0093, 0x66c08934, 0x5c039502, 0x5009b202, 0x81924024, +0x09b41e74, 0x9b00e0c4, 0x702a6420, 0x08e44009, 0x027c409b, 0x14e42911, +0x00000e20, 0x00000000, 0x20310016, 0x067e619f, 0x25c019f0, 0x7c008f00, +0xc0097042, 0x019f2023, 0x09f2065c, 0x970667c0, 0xf0065c00, 0x0027c039, +0x165c0087, 0x59c009f0, 0x00000600, 0x00000000, 0x88850814, 0x087c001f, +0x0cd00334, 0x5c001320, 0xc88170a0, 0x011f0007, 0x41f20c52, 0x1f208448, +0xf0406c00, 0x0004c001, 0x005c001f, 0x50c001b0, 0x00000420, 0x00000000, +0x81942014, 0x017c005d, 0x15400510, 0x84005300, 0xc217b201, 0x027d0015, +0x06d009cc, 0x5d005d40, 0xd209c400, 0x001fc116, 0x0144005d, 0x50400510, +0x00000200, 0x00000000, 0x14328014, 0x032440cd, 0x34600c11, 0x1488c180, +0x401d580f, 0x00c90823, 0x0cd00354, 0xcd008044, 0xd0072400, 0x0836420c, +0x035020cd, 0x50400c1a, 0x00000a00, 0x00000000, 0x00388004, 0x039604ed, +0x09600210, 0x8480c9c0, 0x44029080, 0x09ec212b, 0x06d010a4, 0xed80d848, +0xd0438400, 0x107b040e, 0x031408ed, 0x14404e10, 0x00000200, 0x00000000, +0x04581014, 0x07a443ed, 0x7c501f32, 0x9c81e300, 0xc0167405, 0x81ea056b, +0x1ef01594, 0xef0028c1, 0xf026ac01, 0x083a901e, 0x2f9c35ff, 0x54d15e34, +0x00000040, 0x00000000, 0x0095b810, 0x037c01df, 0x07d401f1, 0x6c00d740, +0xc921f001, 0x20df0225, 0x45f1205c, 0xde0037da, 0xd0137c0a, 0x0027c00c, +0x036c06db, 0x43c0ad70, 0x00000660, 0x00000000, 0x007fa002, 0x07cc21ff, +0x7fc01f30, 0xfc81bb00, 0xc01f300e, 0x01f7226c, 0x167006ec, 0xfb024cc0, +0x7004cc03, 0x013ec01e, 0x07cc01f3, 0x08c01ff0, 0x00000e00, 0x00000000, +0x04298815, 0x038840fc, 0x0b484210, 0x9c04a001, 0x4402f020, 0x00710029, +0x465000fc, 0xfd040940, 0xb002a408, 0x043ad00e, 0x839408f1, 0x54400fd0, +0x00000620, 0x00000000, 0x00190000, 0x038408ed, 0x33000e10, 0xb410a924, +0x40061001, 0x00ed9022, 0x27500084, 0xed001a42, 0x90222510, 0x0030400e, +0x038400e1, 0x60400ed0, 0x00000400, 0x00000000, 0x00c32806, 0x9f0440cd, +0x43409010, 0x14008100, 0x48a0d429, 0x01490823, 0x04500014, 0xcd00d340, +0x824a2427, 0x04a0040c, 0x031408c1, 0x1840bcd0, 0x00000c20, 0x00000000, +0x0075a815, 0x4d4d00ff, 0x77c00d34, 0x34839b10, 0xc03d3407, 0x10fe002e, +0x05740ac4, 0xff1012c0, 0x30024d01, 0x0024501d, 0x83cc01f2, 0x74c21fd8, +0x00000620, 0x00000000, 0x00330001, 0x017c00df, 0x07c000f0, 0x7c009f00, +0xc041f410, 0xc0d70021, 0x15f00c7c, 0xdf0415e0, 0xb0055c40, 0x0027ca3d, +0x037c00df, 0x07c90df1, 0x00000c00, 0x00000000, 0x044f0880, 0x01bc00fb, +0x3cc00f30, 0x7c14a300, 0xd097b000, 0x80fb002c, 0x023040dc, 0xfb100cc4, +0xf002fc00, 0x0008000e, 0x039c00f3, 0x04c00f30, 0x00000c20, 0x00000000, +0x03062001, 0x09f400d5, 0x0c500351, 0x54019700, 0x40005004, 0x40cb0024, +0xb1b22415, 0xd1463441, 0xd00f7400, 0x00c6c015, 0x035480db, 0x04440d50, +0x00000800, 0x00000000, 0x00168001, 0x097400d1, 0x30400d10, 0x64009140, +0x400d100e, 0x21d9a024, 0x05104044, 0xd1000750, 0xd20c7400, 0x00f5403d, +0x035400c9, 0x04420c11, 0x00000200, 0x00000000, 0x00000010, 0x013400c5, +0x00520050, 0x44008140, 0x40015002, 0x00590020, 0x04100104, 0xc1800748, +0xd0003400, 0x00334008, 0x031440c8, 0x40400c40, 0x00000080, 0x00000000, +0x00061000, 0x013c00d3, 0x34c80d14, 0xfc009140, 0xc403b000, 0x00db002c, +0x03100054, 0xfb0017c0, 0xe002fc00, 0x0015c00d, 0x03dc00fb, 0x04c00f38, +0x00000ac0, 0x00000000, 0x000fb805, 0x81fc40ff, 0x0fc003f0, 0xfc40be08, +0xc002f000, 0x003e202f, 0x03f001fc, 0xff001cd2, 0xf202fc00, 0x000ec007, +0x03fc00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x402f8003, 0x43fc00bb, +0x3cd08f30, 0xec80f200, 0xc02b9003, 0x20e7213f, 0x0d30038c, 0xff003bc4, +0xf003dc20, 0x013dc08f, 0x004c081b, 0x0dc80fb0, 0x00000e00, 0x00000000, +0x04a70001, 0x0bc400db, 0xb4440db0, 0x7420d141, 0x400d0203, 0x00d1023f, +0x0d900344, 0xfd0d3640, 0xd073ed20, 0x06b4446f, 0x00440411, 0x04410f10, +0x00000c20, 0x00000000, 0x01b7a011, 0x031400c5, 0xb2505cdc, 0x4400c10c, +0x4044da07, 0x20d10032, 0x0c508754, 0xcd007140, 0xd0031402, 0x0831482c, +0x00140089, 0x46404851, 0x00000e80, 0x00000000, 0x0405a803, 0x834401d1, +0x76401d14, 0x7020d100, 0x491d5007, 0x06d04037, 0xadc00744, 0xd9007742, +0xd18364a0, 0x0224400d, 0x00550891, 0x0e400910, 0x00000620, 0x00000000, +0x00a3a802, 0x033c1151, 0x76c001d0, 0x2c02d308, 0xc01db883, 0x82c72033, +0x1d74031c, 0xdf0037c0, 0xf1035c00, 0x0075ca0d, 0x1154011a, 0x02c009e4, +0x00000e20, 0x00000000, 0x002d8007, 0x03dc00ef, 0x3dd001f0, 0xfc05ff00, +0xc00f3003, 0x01ff003f, 0x1f3003cd, 0xff083ec0, 0xf003fc00, 0x0063c00f, +0x81ac019f, 0x1dd00bf0, 0x00000602, 0x00000000, 0x00f50802, 0x137c04df, +0x14c008b0, 0x5c12d310, 0xc02df4c3, 0x06d34337, 0x0db0435c, 0xd34037c9, +0x30035ca8, 0x4034c00d, 0x214d0093, 0x08c00df0, 0x00000420, 0x00000000, +0x0014a013, 0x43f683d1, 0x75441b14, 0x4c23d100, 0xc20d000f, 0x83d100fe, +0x0d10034c, 0xe30837c0, 0xb283c400, 0x0826c00f, 0x015c0091, 0x4c420c30, +0x00000200, 0x00000000, 0x1022a007, 0x0f3400c9, 0x34500098, 0x040bcd10, +0x420c18af, 0x03cd00b3, 0x8c54a326, 0xc9003360, 0x10033410, 0x0030420d, +0x16240055, 0x1c404c11, 0x00000a00, 0x00000000, 0x02c88004, 0xa73401e1, +0xe840b210, 0xc705fd00, 0x401f1117, 0x05e90072, 0x5e502795, 0xe1007900, +0x8027a429, 0x006a401c, 0x26940965, 0x18409c51, 0x00000200, 0x00000000, +0x02340012, 0x033410cf, 0x244108b0, 0x0487cf06, 0xc04c5017, 0x11cd0133, +0x1c70a314, 0xd8013340, 0x30037c00, 0x0030c00c, 0x164c05c7, 0x48c00870, +0x00000040, 0x00000000, 0x0a2db802, 0x03fc00ff, 0x37c20b70, 0xae10e308, +0xc00c500b, 0x00f3003d, 0x0d306b6c, 0xff24bfc1, 0xf0435c00, 0x0037c00f, +0x02fd28fa, 0x0bc02bb0, 0x00000660, 0x00000000, 0x0027a815, 0x137c00d3, +0x24448d30, 0x3c12dc10, 0xc40db027, 0x03cb41f2, 0x2d301b0c, 0xdb0037c5, +0x70534c46, 0x2035a41d, 0x4f4c4053, 0x54c069b0, 0x00000e00, 0x00000000, +0x10018812, 0x53f400e1, 0x38400c90, 0x9e04ef00, 0x400f0213, 0x10e112b8, +0x4e100384, 0xe5013f40, 0x10138412, 0x1838414e, 0x03c420e1, 0x4ac00a10, +0x00000620, 0x00000000, 0x00790003, 0x37b401e1, 0x48505e95, 0xb485ed00, +0x401e5507, 0x79e1017d, 0x7ed40fa5, 0xc508fb62, 0x58070585, 0x0879409e, +0x07a401e9, 0x0c521cdc, 0x00000402, 0x00000000, 0x20372012, 0x03740fc1, +0x34400d90, 0x3400c511, 0x40bc5003, 0x00c10031, 0x3cd10f16, 0xc5007340, +0x10030400, 0x0030400c, 0x232410c9, 0x4a401c50, 0x00000c20, 0x00000000, +0x445da017, 0x01740271, 0x1cc00711, 0xfc1b3d20, 0x5017706c, 0x0333201d, +0x03f200ac, 0x5b008fc0, 0x70014c00, 0x0455c005, 0x056c005b, 0x5cc055f0, +0x00000620, 0x00000000, 0x40070012, 0x087c001f, 0x07c01170, 0x5c001f00, +0x4441b200, 0x00171004, 0x41209060, 0x1f000740, 0xf0007c00, 0x4007c001, +0x005c2007, 0x49e020a0, 0x00000c00, 0x00000000, 0x00270010, 0x227c029f, +0x25c00934, 0x5c021b00, 0xd009b208, 0x14170926, 0x8132206c, 0x960404c0, +0x30026c00, 0x0022c009, 0x024c0097, 0x41c019d0, 0x00000c20, 0x00000000, +0x40262801, 0x8e740391, 0xa4400910, 0x5c031102, 0x4009d00c, 0x071701e4, +0x20100874, 0x89000040, 0x10024401, 0x00274e09, 0x4a540091, 0x144189d0, +0x00000800, 0x00000000, 0x0064a018, 0x02740091, 0xa4508910, 0x54001900, +0x4008d008, 0x80150824, 0x01184074, 0x99200441, 0x10022488, 0x00264009, +0x22e40095, 0x70400992, 0x00000200, 0x00000000, 0x02602010, 0x122410c1, +0x2040d811, 0x1408c144, 0x6048d022, 0x08850a20, 0x88102274, 0xcd022050, +0x14220406, 0x85224088, 0x32a50ca1, 0xc05068d2, 0x00000080, 0x00000000, +0x0c06b01d, 0x317c0017, 0x05d14111, 0x5c161b00, 0xc041b058, 0x96170584, +0x6134586c, 0x174584c1, 0x10586c04, 0x0102c340, 0x50ec4607, 0x65d051f0, +0x000008c0, 0x00000000, 0x002f9019, 0x327c00b7, 0x2fc24bf4, 0xfc04bf04, +0xc12bf012, 0x04bf0127, 0x4ff012fc, 0x93012fc0, 0xf0127c02, 0x042fc009, +0x125c2c9f, 0x67c46bf0, 0x00000ce0, 0x00000000, 0x00afa818, 0x229c00bb, +0xbc450fb0, 0xdc0da704, 0xc509d036, 0x13bd00af, 0x1bd046ec, 0xb32c60c1, +0xf0325c04, 0x00a6c409, 0x129c009f, 0x64e06b30, 0x00000e00, 0x00000000, +0x4087081c, 0x00440211, 0x94404010, 0x440f5840, 0xc821d13d, 0x871d03c5, +0x11900444, 0x150d1400, 0xd0004402, 0x02845040, 0x086c0e19, 0x604061b0, +0x00000c22, 0x00000000, 0x40e3a030, 0x1a340395, 0x23450811, 0x14008d01, +0x41485a02, 0x409d0022, 0x49c24224, 0x85082045, 0xd01a161c, 0x00204128, +0x5a04108d, 0x41406894, 0x00000e80, 0x00000000, 0x1025a018, 0x02640099, +0x264a0910, 0x44a81909, 0x0009d980, 0x461d8005, 0x01900044, 0xd5222070, +0xd0024440, 0x02244009, 0x02450298, 0x60400990, 0x00000620, 0x00000000, +0x00278005, 0x0274029b, 0x24c10934, 0x5c030700, 0xc139f004, 0x231e0007, +0x00c21c6c, 0x9600e4c2, 0xf0025c80, 0x0066c009, 0x0244009f, 0x055099b4, +0x00000e20, 0x00000000, 0x12658014, 0x021c0397, 0x21d80970, 0x7c011740, +0xc849f020, 0x411f0005, 0x31b0506c, 0x9b4067c0, 0xf0027c40, 0x0027c009, +0x027c008f, 0x43c018f0, 0x00000600, 0x00000000, 0x00050014, 0x407e801f, +0x84c02174, 0x4c421301, 0x4021f898, 0x221f0205, 0x21f0007c, 0x170887c4, +0x30804c08, 0x0005c001, 0x902c0013, 0x53c10130, 0x00000420, 0x00000000, +0x029ca814, 0x1df4016d, 0x1cc00651, 0xc6423104, 0xc0056214, 0x1b2d200c, +0x73700cf4, 0x60201fc0, 0x10014401, 0x00144005, 0x09cc0071, 0x53400710, +0x00000200, 0x00000000, 0x0016a014, 0x013405c9, 0xe0601c50, 0x0412c900, +0x440c1003, 0xc0cd04f3, 0x7cd00320, 0xc9203340, 0x90032541, 0x0830480d, +0x050440c5, 0xd3440c10, 0x00000a00, 0x00000000, 0x04188005, 0x03b402ed, +0x28414a10, 0x8410f900, 0x009c1213, 0x00ed0239, 0x0ed043b4, 0xed013d40, +0x9407a484, 0x4178408e, 0x098400e5, 0x17400615, 0x00000200, 0x00000000, +0x00581011, 0x05b401ed, 0x7cd05e71, 0x8427e940, 0xc81e7107, 0x05ef037b, +0xfef007bc, 0x4f417b40, 0xb0272c05, 0x41fdc15e, 0x058d8dc7, 0x57801a30, +0x00000040, 0x00000000, 0x0015b010, 0x017c60df, 0x36d489f4, 0x7d34d300, +0x410d512b, 0x60db00b6, 0x2d703b74, 0x9322b7c0, 0x700b5c48, 0x05b7f20d, +0x007c06da, 0x43c009f0, 0x00000660, 0x00000000, 0x005fa800, 0x05f041ff, +0x6ec0cb41, 0x7c21fb20, 0xc05fb0af, 0x1bf3027c, 0x1ef10ffc, 0xa300f1c0, +0xf067df01, 0x407cc01f, 0x13cc11f3, 0x08c89734, 0x00000e00, 0x00000000, +0x10198811, 0x19b400ef, 0x68c24a10, 0x3400c917, 0xc1dfd097, 0x08c1003a, +0x5e6087b8, 0xeb017840, 0xd0078c04, 0x0079400e, 0x018404e3, 0x56c00614, +0x00000620, 0x00000000, 0x00990000, 0x01b400ed, 0x99440b50, 0xb408e181, +0x6c0ed003, 0x58e9303a, 0x4fd003a4, 0x25023d40, 0xd013b600, 0x2038400e, +0x018420ed, 0x00420290, 0x00000400, 0x00000000, 0x00332800, 0x017613c5, +0x30400c10, 0x3444c920, 0x503cd1c3, 0x00c90030, 0x3c500714, 0x8d80f102, +0xd2030420, 0x0031400c, 0x010420c0, 0x1a460090, 0x00000c20, 0x00000000, +0x0055a031, 0x017401dd, 0xf0c40f50, 0x7c84c308, 0x002f9003, 0x03d96032, +0x1cd01b74, 0xf74e3dc1, 0xd103f400, 0x003c400f, 0x014c08ff, 0x74a009b1, +0x00000620, 0x00000000, 0x00530001, 0x087c0a9f, 0x27c001f0, 0x7c02d700, +0xc04df02b, 0x20d70037, 0x0de08374, 0x51003681, 0xf0037c80, 0x0237c00d, +0x897c00cf, 0x07e00170, 0x00000c00, 0x00000000, 0x001f0080, 0x254c00f3, +0x3dc006b4, 0xec00fb40, 0xc80ff003, 0x10f34034, 0x0ff3c3fc, 0x7f003cd0, +0xe003ec00, 0x003de00d, 0x258c00ff, 0x04d82a32, 0x00000c20, 0x00000000, +0x00162085, 0x014410c1, 0x76402990, 0x0403d108, 0xc80d904b, 0x00d10034, +0x0dd10374, 0x0d003448, 0xd0035c20, 0x0037440d, 0x055420dd, 0x84400954, +0x00000802, 0x00000000, 0x0054a001, 0x014404d9, 0x66522910, 0x74b4d900, +0x480c9223, 0x21d18874, 0x1d580754, 0x99003640, 0xd1036580, 0x0035400d, +0x094500dd, 0x04480194, 0x00000200, 0x00000000, 0x00102014, 0x810400c1, +0x26400010, 0x1408c189, 0x430cd0c3, 0x41c10070, 0x5cd89734, 0x4d013244, +0xd0031400, 0x0033000c, 0x011402cd, 0x400000d0, 0x00000080, 0x00000000, +0x0016b000, 0x014500d3, 0x55c001b0, 0x7c02db04, 0x501ff21f, 0x02d31034, +0xddf0175c, 0x1e207ec0, 0xf117ee00, 0x1179c00f, 0x214c02ff, 0x04c000b0, +0x00000ac0, 0x00000000, 0x002fb805, 0x01fc007f, 0x31c00ef4, 0xec41ff40, +0xc00d3057, 0x15ff803d, 0x3df0a37c, 0x3f123dc0, 0xf0235c00, 0x00b7c00f, +0x85bc45ef, 0x17c00373, 0x00000e60, 0x00000000, 0x000fa003, 0x00fc103b, +0x1f440330, 0xcd043300, 0xc0c33220, 0x00fb4438, 0x8f3703dc, 0xfb003fc0, +0x35a3cd00, 0x001cc083, 0x22c804f7, 0x0cc16f30, 0x00000e00, 0x00000000, +0x00070801, 0x02740013, 0x97400d10, 0x4408d102, 0x40c1541b, 0x00d100bd, +0x41100144, 0xd5002744, 0x30004400, 0x2b946009, 0x125408d1, 0x04402d52, +0x00000c20, 0x00000000, 0x4203a011, 0x00301001, 0x11400410, 0x14800108, +0x40405120, 0x00558000, 0x4c100214, 0x05101340, 0x12030400, 0x44125800, +0x822400c9, 0x44604c52, 0x00000e80, 0x00000000, 0x28158803, 0x1a7400d9, +0x17404d10, 0x5400d104, 0x66155003, 0x00558405, 0x05100244, 0x10041640, +0x10020400, 0x08026000, 0x027600d9, 0x0c410550, 0x00000620, 0x00000000, +0x0c478802, 0x017c0013, 0x9fc01d34, 0xdc003340, 0xc0017000, 0x454f0264, +0x2130425c, 0x9b1087c1, 0x301f4c00, 0x00164801, 0x026c40f9, 0x08d0a971, +0x00000e20, 0x00000000, 0x017d8007, 0x03bc03b7, 0x1fc01ff0, 0xac00ff08, +0x8003f003, 0x095b006b, 0x03f0203c, 0xa7000bc0, 0x70117c88, 0x101dc003, +0x0a9c00c3, 0x1fc01bf0, 0x00000600, 0x00000000, 0x01810802, 0x017c005f, +0x90c00df0, 0x4d000720, 0xc4041020, 0x015f4007, 0xa5302244, 0x970094c4, +0x70434c02, 0x4114c081, 0x025c00db, 0x0bc020f1, 0x00000420, 0x00000000, +0x20b48013, 0x037405dd, 0x16c02d70, 0x4000db04, 0x4a05502b, 0x40710027, +0x05b02a6c, 0x91005442, 0x100344a5, 0x00ec5001, 0x126c40f3, 0x4f4011d0, +0x00000200, 0x00000000, 0x0002a007, 0x1836120d, 0x70506cd0, 0x340f0500, +0x4090c500, 0x00800033, 0x1c100904, 0x41007040, 0x10080400, 0x00134130, +0x023412c0, 0x1f441098, 0x00000a00, 0x00000000, 0x02488004, 0x05b4016d, +0x78401f50, 0xa409c100, 0x40169027, 0x09e1207b, 0x181005a4, 0x41066850, +0x10248409, 0x04594052, 0x87a401e1, 0x134012d2, 0x00000200, 0x00000000, +0x00101012, 0x003c000f, 0x34482cf0, 0x3c000500, 0xc080f020, 0x08830023, +0x0c13030c, 0x43023040, 0x34220d08, 0x0017c201, 0x023c00c3, 0x4bc108e9, +0x00000040, 0x00000000, 0x009db802, 0x01fc00ff, 0x3e800ef0, 0x9c00ff02, +0xc08771a3, 0x08d70047, 0x0ff623fc, 0x7b023bc0, 0xf0a2fc28, 0x040cc063, +0x03dc10ff, 0x0bc08bf0, 0x00000660, 0x00000000, 0x40f7a015, 0x027c0453, +0x37c00d30, 0x4d001300, 0xc0453480, 0x099f0137, 0x09f0014c, 0xd30024c8, +0x30194c01, 0x00169021, 0x02ee01d3, 0x54c05530, 0x00000e00, 0x00000000, +0x803d8812, 0x03b408e1, 0x33400e10, 0x2c00c180, 0x40861003, 0x00cd043b, +0x08d00184, 0xe5002050, 0x1021c480, 0x015a4203, 0x02c605e1, 0x48410412, +0x00000620, 0x00000000, 0x00f90003, 0x06f40169, 0x79411e14, 0x94012100, +0x44169004, 0x05ad007b, 0x1ed007a6, 0xe900f860, 0x98879411, 0x00104012, +0x868401e1, 0x0c401e94, 0x00000400, 0x00000000, 0x00732812, 0x07340dc9, +0x37401c12, 0x2400c100, 0x40849003, 0x04cd00b3, 0x3cd02726, 0xcd007040, +0x9c275402, 0x01244020, 0x224500c1, 0x4840ac12, 0x00000c20, 0x00000000, +0x0055a817, 0x4dfc035b, 0x17c02630, 0x5c005348, 0xc405b001, 0x005f045f, +0x45f00165, 0x5b409442, 0xb0015d00, 0x209ec424, 0x056c0053, 0x5cd01530, +0x00000620, 0x00000000, 0x20070012, 0x403cc217, 0x87c011f3, 0xf4002f00, +0xc0017008, 0x001f0183, 0x01f0001e, 0x136007c1, 0x70006490, 0x0007c101, +0x007c001f, 0x4bc101f0, 0x00000c00, 0x00000000, 0x04370810, 0x027c01df, +0x25c09934, 0x4c019302, 0xc21db202, 0x00930227, 0x0830927c, 0x9b0420c0, +0x32867c05, 0x0264c22d, 0x027c0093, 0x43c00d30, 0x00000c20, 0x00000000, +0x00260001, 0x02740c9d, 0xa7e02910, 0x04099504, 0x4019104a, 0x009110e7, +0x09120e4c, 0x91002440, 0x10227401, 0x00244009, 0x02740291, 0x07400951, +0x00000800, 0x00000000, 0x8124a018, 0x0274409d, 0x24510912, 0xc400b120, +0x40899006, 0x019140a7, 0x0b1442d4, 0xf1022e40, 0x1c0af400, 0x4024400d, +0x02740391, 0x63400914, 0x00000200, 0x00000000, 0x82202010, 0x0234088d, +0x20400910, 0x8500e502, 0x40881922, 0x08810523, 0x0a1402a4, 0xa1c02850, +0x1422b400, 0x02204489, 0x22344081, 0x43408810, 0x00000080, 0x00000000, +0x0586b01d, 0x503c161f, 0x85440130, 0xcc141335, 0xc161b058, 0x16130107, +0x4130505c, 0x1b0506e1, 0x3058fc14, 0x0584d021, 0x587c0013, 0x77c16130, +0x00000ac0, 0x00000000, 0x0127b819, 0x02fc049f, 0x27c00bd0, 0x7c809f01, +0xc049f012, 0x04bf052f, 0x49f0024e, 0x8f2027c1, 0xf2127c00, 0x012fc049, +0x12fc028f, 0x67c049f0, 0x00000e60, 0x00000000, 0x022fa018, 0x027c1c9f, +0x2dc809f0, 0xcc009302, 0xc029315a, 0x0033052f, 0xcb70c2bc, 0x9c202cd0, +0x11527c14, 0x0124d029, 0x52cc01bb, 0x60c06b30, 0x00000e00, 0x00000000, +0x0287081c, 0x2874800d, 0x04c003d0, 0x44003101, 0x40605018, 0x01110187, +0xe110086c, 0x5d028440, 0x11197e82, 0x019cc040, 0x18040211, 0x70400110, +0x00000c20, 0x00000000, 0x0123a010, 0x02b6448d, 0xa9400bd0, 0x2402a100, +0x40281012, 0x02c50423, 0x08100214, 0x8d102048, 0x5442b400, 0x462a401a, +0x420402c1, 0x40422810, 0x00000e80, 0x00000000, 0x2025a818, 0x0374809d, +0x36400bd0, 0x6400b100, 0x40494802, 0x40950427, 0x49500a44, 0x9c012440, +0x50021400, 0x2028400a, 0x02040091, 0x60400910, 0x00000620, 0x00000000, +0x00678805, 0x027c1a9f, 0x25c088d0, 0x6d009320, 0xc4190402, 0x06974067, +0x09340255, 0x9d0124c0, 0x700a7402, 0x0066c809, 0x024d009b, 0x14d0693c, +0x00000e20, 0x00000000, 0x22658014, 0x027c00df, 0x25c019f0, 0x5c208f40, +0xc00df002, 0xe01b0167, 0x08b0123c, 0xdf0027c0, 0xb0027c04, 0x0265c109, +0x027c008f, 0x53c009f8, 0x00000600, 0x00000000, 0x00050814, 0x004c0a1f, +0x03c101f1, 0x3c041302, 0xc001b000, 0x02030980, 0x0110007c, 0x170004c0, +0x34087c02, 0x0484d001, 0x00480113, 0x50840130, 0x00000420, 0x00000000, +0x11dca014, 0x0146015d, 0x54c125c0, 0xf4115500, 0xc005b009, 0x001b00de, +0x551035f4, 0x5d015440, 0x10017401, 0x00145025, 0x01440071, 0x50400510, +0x00000200, 0x00000000, 0x01f2a014, 0x012481cd, 0x536010d0, 0x36000100, +0x4004543f, 0x00c90230, 0x1c100634, 0xcd007044, 0x10473409, 0x0230600c, +0x03040041, 0x50401d90, 0x00000a00, 0x00000000, 0x00388005, 0x03a401ed, +0x08400ad0, 0x3400e500, 0x4036d021, 0x00e9003e, 0x081001a4, 0xed003840, +0x1041a400, 0x000c600e, 0x0284a061, 0x14402e94, 0x00000200, 0x00000000, +0x00781015, 0x05a501ed, 0x73c81ed0, 0xbc81e344, 0xc0967417, 0x05eb0058, +0x1e3106bc, 0xaf0078c0, 0x30073c01, 0x4078c81a, 0x078c0123, 0x54d01fb4, +0x00000040, 0x00000000, 0x0035b810, 0x035c021f, 0x26440df1, 0x7c009f40, +0x0821b005, 0x00df1013, 0x09f4837c, 0xdf0837d0, 0xf0037c02, 0x0093c40d, +0x5a7d000f, 0x43c00d70, 0x00000660, 0x00000000, 0x027fa000, 0xa5fc0bff, +0x5dc09ef0, 0xdc81f301, 0xc0173107, 0x99e3006f, 0x1b1206cc, 0xff006cc0, +0x3206cc03, 0x02fcc417, 0x07fc0133, 0x00c01b30, 0x00000e00, 0x00000000, +0x02198815, 0x63b40ced, 0x18800ed0, 0xac006141, 0x4006b041, 0x30e1412b, +0x4e1403bc, 0xef003850, 0x10128400, 0x0338404f, 0x22b408a1, 0x54400ab0, +0x00000620, 0x00000000, 0x22290000, 0x01b600ed, 0x270807d3, 0x0400e103, +0x40061003, 0x00e1201b, 0x08100224, 0xed003844, 0x91030408, 0x003e6402, +0x07b400a1, 0x00400810, 0x00000400, 0x00000000, 0x10c22804, 0x0b34060d, +0x20413cd0, 0x640dc110, 0x40101015, 0x00c10417, 0x2d100b12, 0xc5023040, +0x906f0400, 0x01f2404d, 0x06740081, 0x10502890, 0x00000c20, 0x00000000, +0x0345a815, 0x077c025f, 0x37482df0, 0xdc82d320, 0xc455160f, 0x00c30037, +0x6d300324, 0xdd003440, 0xb4074d00, 0x41f2c02d, 0x167c0053, 0x54c0ad30, +0x00000620, 0x00000000, 0x00070001, 0x017c084f, 0xa5c085f0, 0x7c00df00, +0xc005f009, 0x40df00b7, 0x0df0037c, 0xcf0037c0, 0x70017c00, 0x00a5c42d, +0x037c005f, 0x07c00874, 0x00000c00, 0x00000000, 0x30cf0880, 0x43cc0873, +0x5fc04ff0, 0xcc804300, 0xc097a003, 0x00f70067, 0x8f3043ec, 0xdf007cc1, +0x30038d01, 0x003cc05f, 0x22fc0033, 0x00c00f30, 0x00000c22, 0x00000000, +0x00c62081, 0x01442311, 0xc7411dd0, 0x440fd500, 0x4011b029, 0x01d10027, +0x1d100e55, 0xdd007452, 0x50034401, 0x41b5424d, 0x03740115, 0x05400910, +0x00000802, 0x00000000, 0x00b0a001, 0x03441011, 0x334009d0, 0x44009122, +0x4001d00b, 0x84d52627, 0x0d106344, 0xd5293640, 0x18234428, 0x0034420d, +0x02740851, 0x04404910, 0x00000200, 0x00000000, 0x00102010, 0x01050001, +0x33400890, 0x0500c500, 0x4000d001, 0x00c50023, 0x0c100304, 0xcd803000, +0x19030400, 0x0034400c, 0x032400c1, 0x41400c14, 0x00000080, 0x00000000, +0x0026b000, 0x034c0011, 0x1fc00df0, 0x4d00d300, 0xc001f001, 0x40d70007, +0x0d30034c, 0xdf003680, 0x34034c00, 0x0034c00d, 0x02bc0093, 0x00c00930, +0x00000ac0, 0x00000000, 0x000fb805, 0x01fc003f, 0x1fc007f0, 0xfc003f00, +0xc203a000, 0x00fb000f, 0x0ff003fc, 0xff003fc0, 0xf003fc00, 0x003bc00f, +0x03fc00bf, 0x17c00ff0, 0x00000e60, 0x00000000, 0x113ba003, 0xa3ac0023, +0x3ec08fb0, 0x7c003f00, 0xc04ff080, 0x44f9203e, 0x0f9013ec, 0xbf011fc0, +0xf012ec00, 0x013ec04f, 0x03cc00ff, 0x0cc00fb0, 0x00000e00, 0x00000000, +0x03370801, 0x1bf40411, 0x30c0ef10, 0x44004d00, 0x402e1080, 0x0af1203c, +0x2f500bcc, 0x41021040, 0x10090402, 0x25bc40af, 0x035400dd, 0x04400c10, +0x00000c20, 0x00000000, 0x0433a031, 0x0b341001, 0x32400cd0, 0x24400500, +0x408c9001, 0x00c92032, 0x2cd10b24, 0x09103240, 0x920a2488, 0x08b2480c, +0x034401cd, 0x44414d90, 0x00000e80, 0x00000000, 0x0075a803, 0x03760011, +0x74400d10, 0x44905d10, 0x400d12c1, 0x00d18835, 0x0d500344, 0xd1003442, +0x10014400, 0x8036400d, 0x035401dd, 0x0c401d11, 0x00000620, 0x00000000, +0x0147a802, 0x037c0111, 0x36c00df0, 0x68021700, 0xc40db008, 0x80db0036, +0x0db1036c, 0x3b101ec8, 0xb002ec02, 0x1036c20d, 0x070c00dd, 0x08d01db0, +0x00000e20, 0x00000000, 0x00098007, 0x03fc05bf, 0x2fc00db0, 0x4c00ff00, +0xe00fb080, 0x40ff043e, 0x0fb043fc, 0xfb001ec4, 0xb001fc00, 0x083dc00f, +0x4bfc70ff, 0x1fc00ff0, 0x00000600, 0x00000000, 0x00250802, 0x137d001f, +0x24c00d70, 0x5c821703, 0xc90d3049, 0x00d30235, 0x0d30036c, 0x1d0036c0, +0x70127c00, 0x1334c00d, 0x035c00db, 0x09c20d72, 0x00000420, 0x00000000, +0xc0b4a013, 0x83d409d1, 0xe5400e12, 0x4401d120, 0x682fb009, 0x85f120bc, +0x1f101be4, 0xd500b442, 0x10494c03, 0x00fc50bf, 0x1f4403c8, 0x4c400c92, +0x00000200, 0x00000000, 0x2232a007, 0xa7742305, 0xb0400c50, 0x54109580, +0x443d110a, 0x93d10035, 0x2d100b44, 0x15015445, 0x400a5410, 0x00b0400d, +0x031403c1, 0x1d400cd0, 0x00000a00, 0x00000000, 0x2af88004, 0x07a40131, +0x79401e12, 0x942de111, 0x405e91c6, 0x01f1007c, 0x5f1027c4, 0xe1105862, +0x10058421, 0x0038421c, 0x27c43df1, 0x10401e90, 0x00000200, 0x00000000, +0x02301012, 0x033d1005, 0x30c20c70, 0x1c1c8700, 0x435c3017, 0x00c32831, +0x1c30a72e, 0x070630c1, 0x70021c10, 0x0374c1cc, 0x0b1c0ccb, 0x49c00c70, +0x00000040, 0x00000000, 0x4a3db802, 0x03dc0037, 0x3fc10ff0, 0xec0cfd21, +0xc00ff013, 0x18e7063b, 0x0ef203bc, 0xf70035c0, 0xe025fc80, 0x13b7c18f, +0x033c2ecf, 0x0bc80f70, 0x00000660, 0x00000000, 0x10272015, 0x135c0013, +0x31426d70, 0x4c009300, 0x80cd1212, 0xc4db0132, 0xed70436c, 0x1b2016c0, +0x33024d01, 0x00f7c44d, 0x2f0c08d3, 0x57040d32, 0x00000e00, 0x00000000, +0x00290812, 0x4bb400a0, 0x2ac14c10, 0x1420c100, 0x410ce042, 0x14c51438, +0x0e100b8d, 0xc9001849, 0xb0010c20, 0x1933414e, 0x538504eb, 0x4a402e50, +0x00000620, 0x00000000, 0x28610003, 0x07343120, 0x7f401ed0, 0xa441a908, +0x445e9087, 0x85e1217e, 0x5ed10704, 0x2d007b44, 0xd806a401, 0x0279405e, +0x578491e9, 0x0f401e12, 0x00000400, 0x00000000, 0x10632812, 0x03340181, +0x20400c90, 0x3412c900, 0x400c1003, 0x00c50030, 0x0c910304, 0xcd003240, +0xd0010400, 0x0033400c, 0x030400c9, 0x4a401d50, 0x00000c20, 0x00000000, +0x415d8817, 0x017c0361, 0x5f4005f0, 0x6d025b84, 0xc0058401, 0x4073001e, +0x06f201c8, 0x5f0017c0, 0xf001ec02, 0x801dc007, 0x00cc0039, 0x5fc50330, +0x00000620, 0x00000000, 0x00470012, 0x083c011f, 0x87c80170, 0x5c000740, +0xc820f000, 0x40170885, 0x2172085c, 0x3b0085c8, 0xb100fe24, 0x1087c800, +0x887c4217, 0x4ac801f0, 0x00000c00, 0x00000000, 0x00230810, 0x066c0193, +0x65c01930, 0x0c009351, 0xc2395002, 0x248710e2, 0x0936020c, 0x9f0064c2, +0x12023c00, 0x20249809, 0x405c101f, 0x40c001f0, 0x00000c20, 0x00000000, +0x08262001, 0x46440a91, 0x2040a834, 0x6c1b9300, 0x4019706e, 0x07914024, +0x0930424c, 0x9d1626d0, 0x30027600, 0x14244809, 0x6c4c1307, 0x06c00170, +0x00000800, 0x00000000, 0x00648018, 0x12642291, 0x25400919, 0xd4009984, +0x40099002, 0x00958026, 0x49101254, 0xfd202440, 0xd01af406, 0x01244269, +0x0054041d, 0x604409d2, 0x00000200, 0x00000000, 0x08202010, 0x42040891, +0xa444c818, 0xb606a100, 0x406c150a, 0x228000b0, 0x6d100a05, 0xa900a040, +0x540ab442, 0x02204228, 0x2204089d, 0x42408843, 0x00000080, 0x00000000, +0x4386b01d, 0x286c0211, 0x05404530, 0xd4241900, 0xc1413010, 0x10150006, +0x4010404c, 0x1d000440, 0xf000f480, 0x0504c841, 0x585c161e, 0x74c161e0, +0x00000ac0, 0x00000000, 0x432f9819, 0x127c04bf, 0x2f8889d0, 0x64029f08, +0xc409f222, 0x009f20a7, 0x89700270, 0x9f00a7c0, 0xb00a7c02, 0x11278289, +0x12dc04a6, 0x67c04bf0, 0x00000e60, 0x00000000, 0x01afa018, 0x42ec02b3, +0x27c14b30, 0x4c009f00, 0xc0c97132, 0x169b0424, 0x49709a4c, 0x9c2165c8, +0x301e5c07, 0x08a4c089, 0x1ebc01b3, 0x62c199e0, 0x00000e00, 0x00000000, +0x0117081c, 0x08450411, 0x04406111, 0x140c1709, 0x44215000, 0x46010100, +0xe1301804, 0x19039450, 0xb0287e0a, 0x014c4000, 0x14741515, 0x704021d0, +0x00000c20, 0x00000000, 0x01a3a010, 0x52040085, 0x29030850, 0x8490ad04, +0x40ca1832, 0x04a96428, 0x0a503284, 0xad042851, 0x12028400, 0x81af486a, +0x1a34028d, 0x424049d0, 0x00000e80, 0x00000000, 0x0025a818, 0x03440091, +0x24400952, 0xd420fd00, 0x400b5012, 0x40b1002c, 0x0b9103d4, 0xb9102c40, +0x9002f450, 0xc02f400b, 0x0074801d, 0x604009d0, 0x00000620, 0x00000000, +0x42a7a805, 0x02440093, 0x65400944, 0x4c809d02, 0x50097002, 0x009b0024, +0x09700245, 0x9d00a4c0, 0x10024402, 0x2027d009, 0x6c7c041b, 0x16c018f0, +0x00000e20, 0x00000000, 0x00618014, 0x021c059f, 0x25c009b0, 0x7c208700, +0xc008f002, 0x408f8027, 0x0930826c, 0x9b0826ce, 0xf4022c80, 0x2824c008, +0x007c4413, 0x53c089f0, 0x00000600, 0x00000000, 0x40840814, 0x04741013, +0x0ce441b1, 0xbc003300, 0xc0037010, 0x0037290b, 0x427a00b8, 0x23418cc0, +0x3000fc18, 0x000dc083, 0xc87c0013, 0x52c00130, 0x00000420, 0x00000000, +0x221ca014, 0x09f41171, 0x16512713, 0x74095110, 0x45855041, 0x095100d7, +0x0510094c, 0x5100d551, 0x12057402, 0x005008a5, 0x44f40a33, 0x50420510, +0x00000200, 0x00000000, 0x0072a014, 0x013402d1, 0x3b401dd0, 0xb422e100, +0x403ed00f, 0x43e500fb, 0x3e504b94, 0xe1007a48, 0x144bb621, 0x0039401e, +0x0334c1cd, 0x50400c90, 0x00000a00, 0x00000000, 0x00988005, 0x13b400e1, +0x7f4c8c50, 0xb422e140, 0x400ed203, 0x70e1043b, 0x0c1003b4, 0xe1047a40, +0x1243b600, 0x00b0400e, 0x17b406e5, 0x14488f90, 0x00000200, 0x00000000, +0x407c1015, 0x0d3c0163, 0x78405ef0, 0xbe41e300, 0xc01ef107, 0x01e7007b, +0x1e52079c, 0xe20072c0, 0x34073c01, 0x3079c01e, 0x5fbc13ee, 0x54905eb0, +0x00000040, 0x00000000, 0x0805b810, 0x057c009f, 0x00000d20, 0x7c001f10, +0xc0012000, 0x001f0007, 0x01f00140, 0x1f0805c0, 0xf0007c00, 0x004fc001, +0x837cc6db, 0x41c46c77, 0x00000660, 0x00000000, 0x026ba000, 0x05fc01b3, +0x7fc01730, 0xcc09fb00, 0xc21f3207, 0x05db007f, 0x1eb007bc, 0xf3007cc0, +0x3027d401, 0x0278c0de, 0x27fc81f3, 0x00c03ef0, 0x00000e00, 0x00000000, +0x37298835, 0x01f40020, 0x3f408610, 0x9408e102, 0x400c5023, 0xa5ed013b, +0x1c109334, 0xeb0938c8, 0xb073ac82, 0x2870408e, 0x0fb405e1, 0x54404ed0, +0x00000620, 0x00000000, 0x082d0000, 0x01b41060, 0x3a400e10, 0x0600e900, +0x408e9003, 0x24e9203b, 0x0f1303e4, 0xe1103240, 0x13030400, 0x007c444f, +0x53b414ed, 0x00680fd3, 0x00000400, 0x00000000, 0x10372804, 0x01340301, +0x03400c10, 0x14010102, 0x4000d000, 0x800d0003, 0x00100134, 0x09000040, +0x90082405, 0xa8004000, 0x0f3423cd, 0x10420cd0, 0x00000c20, 0x00000000, +0x0075a815, 0x01fc22d3, 0x76c00f34, 0x4419db00, 0x420d9203, 0x00db0037, +0x0d10027c, 0xd10036c0, 0x320f4c13, 0x0038c40d, 0x037c80dd, 0x54d31ff0, +0x00000620, 0x00000000, 0x10b70001, 0x887c00df, 0x17c02cf0, 0x7c42df00, +0xc02d700b, 0x00cf08b7, 0x0d720274, 0xdf0037c8, 0xf2137c00, 0x0037c00d, +0x0b7c00d3, 0x07c00df1, 0x00000c00, 0x00000000, 0x803f0880, 0x04cc1073, +0x2dc03ff0, 0x8e01c300, 0xc09e7023, 0x09ff203e, 0x0ef202c8, 0xcc08bc18, +0x3007ac01, 0x093cc00e, 0x57fc31f1, 0x03c00ff0, 0x00000c22, 0x00000000, +0x04f62081, 0x24440091, 0x30405dd0, 0x44021106, 0x4061b1c0, 0x061d0245, +0x01d02444, 0x1d000442, 0xb0404401, 0x004450e1, 0x0b7442d1, 0x07400dd0, +0x00000802, 0x00000000, 0x4074a001, 0x01448089, 0x376409d0, 0x64081108, +0x40011080, 0x001d0406, 0x61d28054, 0x1d088540, 0x12104404, 0x68264801, +0x037406d5, 0x07440dd0, 0x00000200, 0x00000000, 0x00302010, 0x80040009, +0x166008d0, 0x04000140, 0x4000da08, 0x000d0001, 0x00d32015, 0x0d000141, +0x90000420, 0x80264000, 0x033400c5, 0xc3480cd0, 0x00000080, 0x00000000, +0x0036b000, 0x004c0059, 0x25c00df0, 0x6d001300, 0xc8817008, 0x011d0006, +0xf1f80858, 0x1f0005c0, 0x30006d00, 0x02668001, 0x877c01d7, 0x03c00ff0, +0x00000ac0, 0x00000000, 0x003fb805, 0x00fc0037, 0x3dc00fd0, 0xfc00ff00, +0xe81fb817, 0x08ff083e, 0x1df006ec, 0xff803ec2, 0xf003fc00, 0x0255c80f, +0x237c08db, 0x17c20ff0, 0x00000e60, 0x00000000, 0x000f8003, 0x03ec20f3, +0x3cc00eb0, 0xfc043300, 0xc002f002, 0x0433023e, 0x0f3203cc, 0xff0038c0, +0xf000ec08, 0x007cc01f, 0x17cc01e3, 0x0cc00f30, 0x00000e00, 0x00000000, +0x00070001, 0x03c400e1, 0x34400d90, 0x748a1100, 0x4061d003, 0x0011023c, +0x0f10eb45, 0xfd003441, 0xd0084506, 0x009440a9, 0x21440211, 0x04482f10, +0x00000c20, 0x00000000, 0x0003a011, 0x031400c9, 0x32400016, 0x34000d00, +0x4120d103, 0x08052021, 0x4c910314, 0xcd002160, 0xd0080408, 0x02304208, +0x060408c1, 0x44502c10, 0x00000e80, 0x00000000, 0x0005a803, 0x475500c9, +0x36400590, 0x74015d00, 0x4009d007, 0x00150025, 0x0d900254, 0xdd00a540, +0xd0084600, 0x44045105, 0x04440251, 0x0c400d90, 0x00000620, 0x00000000, +0x04c7a802, 0x0f7c20db, 0x36c12db0, 0x7c201f40, 0xc101f003, 0x0517007d, +0x0db4035c, 0xdf0255d0, 0xf0006c40, 0x0024d005, 0x034c09d3, 0x08c40d30, +0x00000e20, 0x00000000, 0x10c58007, 0x024c10f7, 0x7dc85f70, 0xf4007301, +0xd20bf383, 0x054b9278, 0x0e7027ec, 0xef003ed0, 0xf011fc00, 0x027bc249, +0x02bd21ef, 0x1f000d76, 0x00000600, 0x00000000, 0x00b10802, 0x0b6d00d3, +0x24c089b0, 0x4d001f00, 0xc125f003, 0x08d30024, 0x0df0024c, 0xd301e4c0, +0x300a6c08, 0x0084c040, 0x014c0053, 0x08c00df0, 0x00000420, 0x00000000, +0x0034a013, 0x2f4402f1, 0xb440ad10, 0x6c2b5d0b, 0x002cd013, 0x01d00424, +0x1fd02a40, 0xfb006444, 0x1003450a, 0x02844055, 0x28441111, 0x4c400fd0, +0x00000200, 0x00000000, 0x0032a007, 0x034488c9, 0x34401d91, 0x04000d00, +0x4800d013, 0x01090060, 0xbcd00744, 0xc100a241, 0x90040401, 0x08b04008, +0x030401c1, 0x1c400dd2, 0x00000a00, 0x00000000, 0x00788804, 0x078401e9, +0x78405f18, 0xa4112d00, 0x5012d027, 0x01691060, 0x1cd10784, 0xc9086a40, +0x92008409, 0x0070501a, 0x068401a1, 0x10400ed1, 0x00000200, 0x00000000, +0x40000012, 0x410c04cb, 0x30c00cb0, 0x0d005f20, 0xc048d003, 0x00cb0830, +0x0cf2720d, 0xc30032c0, 0xb0624e20, 0x0000c000, 0x010c0003, 0x48c08cf0, +0x00000040, 0x00000000, 0x000da802, 0x2bde00f7, 0x3fc04ff0, 0xfe027f00, +0xc00bf00b, 0x00d70277, 0x8ff022fc, 0xff001dc1, 0x7012dc02, 0x401fc08b, +0x00fc003f, 0x0bc04ff0, 0x00000660, 0x00000000, 0x0077a015, 0x137c04d7, +0x34c00c30, 0x3c005f00, 0xc0083003, 0x01130124, 0x0df0034c, 0xdf0024c0, +0x30004d0a, 0x0034c009, 0x437c0092, 0x57c04df0, 0x00000e00, 0x00000000, +0x20398812, 0x038412f1, 0x38400e90, 0xb4006720, 0x400ab003, 0x00214428, +0xced003ac, 0xcd002844, 0xb001ec24, 0x40084002, 0x01b44021, 0x4b410ed0, +0x00000620, 0x00000000, 0x00790003, 0x059405e5, 0x78401f90, 0xb4016d00, +0x481e1807, 0x01818070, 0x5cd00684, 0xed007940, 0x900f8461, 0x00684012, +0x06b401e1, 0x0f409ed0, 0x00000400, 0x00000000, 0x02b32812, 0x071400c1, +0x30400c90, 0x3401450a, 0x409c9003, 0x0b810030, 0x0dd02766, 0xcd019540, +0x90072500, 0x01c84002, 0x00b40321, 0x4b400cd0, 0x00000c20, 0x00000000, +0x001da817, 0x41fd0057, 0x54d02734, 0x7c005f00, 0xd0373001, 0x00734014, +0x05f0014c, 0x5f009dd0, 0xb00dcc00, 0x0154d025, 0x017e1253, 0x5fc005f0, +0x00000620, 0x00000000, 0x24070012, 0x006c001f, 0x07c00170, 0x7c081f08, +0xc011f008, 0x041f0003, 0x21f0087c, 0x1f008680, 0xf2047c00, 0x0007c101, +0x047c101f, 0x4bc801f0, 0x00000c00, 0x00000000, 0x00270810, 0x024c009f, +0x244109f0, 0x4c00d100, 0xc0083413, 0x00934024, 0x8932027c, 0x834026c0, +0x70060c00, 0x0020d009, 0x023c0093, 0x43c00930, 0x00000c20, 0x00000000, +0x08262001, 0x0244009d, 0x24500915, 0x05009140, 0xc0b91002, 0x08910025, +0x29120a74, 0x9100e445, 0x10064508, 0x00a44108, 0x0a740091, 0x07400914, +0x00000800, 0x00000000, 0x0024a018, 0x0204008d, 0x21400812, 0x44208500, +0x40091002, 0x00910026, 0x09900274, 0x91006640, 0xd0124400, 0x00ac4009, +0x0af408b1, 0x63400910, 0x00000200, 0x00000000, 0x02202010, 0x2204088d, +0x21408898, 0x06088502, 0x40881003, 0x00816221, 0x88957234, 0x8100a040, +0x9022040c, 0x0028400a, 0x02b400a1, 0x43404810, 0x00000080, 0x00000000, +0x0582301d, 0x5804021f, 0x85c22170, 0x4c161700, 0xc1653000, 0x14130586, +0x61b0197c, 0x130016c1, 0xf2584c03, 0x4004c401, 0x80fc0013, 0x77c0b130, +0x00000ac0, 0x00000000, 0x012db819, 0x92fd069f, 0x2ec04b70, 0x7c049b01, +0xc04bf202, 0x80bf012f, 0x497012fc, 0x9f002fc0, 0x7212fc0c, 0x00a3c028, +0x0a7c029f, 0x67c099f0, 0x00000e60, 0x00000000, 0x0027a018, 0x527c109f, +0x20c209b0, 0xfc049b0a, 0xc10bb003, 0x04b300ef, 0x6b7042cc, 0xbf002cc0, +0xf002fc04, 0x0024c00b, 0x034c0093, 0x60c0c930, 0x00000e00, 0x00000000, +0x22c7081c, 0x0d64431d, 0x44411012, 0x741f3100, 0x40211000, 0x001100c7, +0x01100844, 0x070006c0, 0xd0007440, 0x40044001, 0x00448011, 0x70400114, +0x00000c20, 0x00000000, 0x0423a010, 0x42d650ad, 0x2b420a10, 0x3404a100, +0x41489002, 0x038101a3, 0x28d05224, 0x8d056048, 0xd04a3402, 0x00204008, +0x82058081, 0x42406810, 0x00000e80, 0x00000000, 0x08258818, 0x02f600bd, +0x2f426a14, 0x7600b101, 0x40099202, 0x40910127, 0x09960264, 0x95006640, +0xd0027400, 0x04244009, 0x02441091, 0x62400810, 0x00000620, 0x00000000, +0x01e7a805, 0x0e5c209f, 0x27c00930, 0x3c099340, 0xc1a9b006, 0x83934027, +0x08f4262c, 0x9f0024c0, 0xd0127c00, 0x02e4d139, 0x464c0a93, 0x16500930, +0x00000e20, 0x00000000, 0x3d650014, 0x0a4c009f, 0x64d00971, 0x7c019f00, +0xc0097026, 0x059f0027, 0x0970025c, 0x9f0027c0, 0xf00e7c00, 0x0477c088, +0x137c00cf, 0x51c009f0, 0x00000600, 0x00000000, 0x30850814, 0x08fc002f, +0x0cc82334, 0x4c003340, 0xc0813000, 0x00030003, 0x1130004d, 0x1f0a44d0, +0xb0284c10, 0x4000c021, 0x100c0013, 0x50c00130, 0x00000420, 0x00000000, +0x00942014, 0x0134005d, 0x14100518, 0xc4005300, 0x40075001, 0x01730017, +0x271001c4, 0x7d009c50, 0x30054600, 0x08944427, 0x09440051, 0x50c00510, +0x00000200, 0x00000000, 0x00b2a014, 0x031480cd, 0x34400c12, 0x2400c500, +0x401c1005, 0x00890073, 0x0c104e04, 0xcd003248, 0x90061401, 0x00304938, +0x00060081, 0x50400c14, 0x00000a00, 0x00000000, 0x00388005, 0x00b4402d, +0x0c400210, 0xa4012108, 0x40065009, 0x11e100bb, 0x4e100e84, 0xad023a40, +0x9006c403, 0x00304028, 0x028402e0, 0x16404e10, 0x00000200, 0x00000000, +0x00781015, 0x07dc41ef, 0x78c01e10, 0xad01c720, 0xc0163006, 0x00c94073, +0x3c3402cc, 0xef007ad0, 0xb1069501, 0x0078c012, 0x060c01c3, 0x54d05f30, +0x00000040, 0x00000000, 0x0035b810, 0x807c001f, 0x07c001f0, 0x1ca01700, +0xc005f000, 0x20df0036, 0x1df0027c, 0xdb1035c0, 0x70023c00, 0x01b7d009, +0x1a7d06df, 0x41c16df0, 0x00000660, 0x00000000, 0x005fa000, 0x87dc01f3, +0x7cc01f70, 0x8c81f300, 0xc09f7004, 0x01f3007c, 0x1f3007cc, 0x3f007cc0, +0xf007cc01, 0x21bcc01b, 0x0ccc03f7, 0x00c11ff0, 0x00000e00, 0x00000000, +0x02198815, 0x40840021, 0x08400334, 0x84002100, 0x40273011, 0x10a34128, +0x2fb141c4, 0xed000840, 0xd022ac04, 0x02b8420a, 0x628408ed, 0x56c00ed0, +0x00000620, 0x00000000, 0x00190000, 0x231400c1, 0x38418e50, 0xb400e906, +0x48025002, 0x00a18038, 0x06d00184, 0x2d003940, 0xd002a400, 0x04384008, +0x028400e5, 0x00400cd0, 0x00000400, 0x00000000, 0x88132804, 0x1c140001, +0x00480110, 0x34410900, 0x41001020, 0x00892024, 0x04d20504, 0x8d008140, +0xd8262480, 0x00f04208, 0x0a040acd, 0x12400cd0, 0x00000c20, 0x00000000, +0x4035a815, 0x0b5c80d3, 0x74d00d70, 0x3d10db40, 0xe2247005, 0x00938824, +0x0ff2024c, 0xcf003dd0, 0xf0070c00, 0x0074c1b0, 0x0c4d00d7, 0x54c00ff0, +0x00000620, 0x00000000, 0x00370001, 0x006c001f, 0x07c40170, 0x4c001720, +0xe0057001, 0x20d78027, 0x2ca0037c, 0x1f00b640, 0xf0037c00, 0x24b3d025, +0x107c30cf, 0x07c00df0, 0x00000c00, 0x00000000, 0x003f0880, 0x038c00e3, +0x3cc00fb0, 0xf400ff00, 0xc8333306, 0x00f34224, 0x5f30028c, 0xbf040ec0, +0x300fec00, 0x003cc083, 0x00cc00db, 0x00c00e30, 0x00000c22, 0x00000000, +0x003e2081, 0x00d40031, 0x0c500310, 0x74003900, 0x500110a0, 0x80c1002c, +0x29500744, 0x1d000440, 0x50075c00, 0x00b54135, 0x080009d0, 0x06c00d10, +0x00000802, 0x00000000, 0x4010a001, 0x036400d1, 0x34400d90, 0x7400dd90, +0x40051001, 0x00d90024, 0x0d120754, 0x1d00f440, 0x10034406, 0x00344005, +0x084400d1, 0x04400d10, 0x00000200, 0x00000000, 0x00102010, 0x00340001, +0x00480010, 0x74000980, 0x40051101, 0x00894030, 0x04500104, 0x4d000050, +0x5a035400, 0x40314004, 0x020500c1, 0x42400c14, 0x00000080, 0x00000000, +0x0012b000, 0x034c00d3, 0x30c00db0, 0x7c00cf00, 0x40013002, 0x009b0024, +0x0532015c, 0x5f0004d0, 0x30034400, 0x2034c005, 0x024c00d3, 0x00c00f30, +0x00000ac0, 0x00000000, 0x001fb805, 0x00dc203f, 0x0fc203f0, 0xfc003f00, +0xc203f100, 0x00a7203f, 0x0bf001fc, 0x6f000fc0, 0xf003fc00, 0x001fc007, +0x02fc00f7, 0x17c00ff0, 0x00000e60, 0x00000000, 0x003f8003, 0x5bcc1033, +0x2cc04f70, 0xdc003701, 0xc203f013, 0x40eb023e, 0x0e7183c4, 0x331038c0, +0x9023ec08, 0x083ce40f, 0x03dc00ef, 0x0fc00ff1, 0x00000e00, 0x00000000, +0x00330001, 0x0bc40601, 0xb4416e10, 0xe8001b02, 0x4001d00b, 0x0a510134, +0x0d100344, 0x1109bd48, 0xd09b9404, 0x2034500d, 0x034422fd, 0x07414c10, +0x00000c20, 0x00000000, 0x0537a011, 0x120410c5, 0x10408c51, 0x04000500, +0x4000d223, 0x80490133, 0x1cd20704, 0x8904b042, 0xd00b0400, 0x0871401c, +0x036408dd, 0x47401c90, 0x00000e80, 0x00000000, 0x0065a803, 0x02041815, +0x34400d10, 0x45801500, 0x4081d003, 0x08512034, 0x1d980744, 0x99203542, +0xd0035408, 0x0075401d, 0x036100dd, 0x0f401d12, 0x00000620, 0x00000000, +0x0033a802, 0x074d0317, 0x44d00d50, 0x44039502, 0xc731f003, 0x115b2037, +0x0cf0034d, 0x5b403444, 0xf0034442, 0x0035400c, 0x6f2e00df, 0x03c00db2, +0x00000e20, 0x00000000, 0x402d8027, 0x27fc013b, 0x5bc00ef0, 0xec09bb00, +0xc406f083, 0x11bf003f, 0x0f7003fc, 0x77203bc8, 0xf003dc01, 0x003ec00f, +0x03cca0ff, 0x1fc00fb0, 0x00000602, 0x00000000, 0x02350802, 0x024d02c7, +0xb4d00d34, 0x5c085f00, 0xc1013023, 0x0a5f0034, 0x8d71235c, 0xd3103644, +0x32035c02, 0x0037c8cd, 0xc34c00d3, 0x08c40d70, 0x00000420, 0x00000000, +0x0064a013, 0x02440a11, 0x3c400f10, 0xfc0a7108, 0xc05b102b, 0x021d013e, +0xad102b04, 0xb1003c40, 0x1003ec00, 0x00f7463d, 0x0f4507e1, 0x4ec20d10, +0x00000200, 0x00000000, 0x0072a007, 0x82240005, 0x22400c90, 0x04015500, +0x401c1007, 0x039d21f4, 0x1cd00714, 0x11003248, 0x10031460, 0x4433442c, +0x231404c9, 0x1c400c50, 0x00000a00, 0x00000000, 0x00788004, 0x262401e1, +0x7e481e12, 0xe6057188, 0x54181027, 0x013d0078, 0x1fd007c4, 0x31027842, +0x14079401, 0x127f401e, 0x078409e9, 0x1a401e10, 0x00000200, 0x00000000, +0x02300012, 0x232400c7, 0x92c04cb0, 0x0c028700, 0x400c3203, 0x008e0030, +0xcce0031c, 0x034032c0, 0x30031002, 0x0573c00c, 0x231c00ca, 0x48c00c71, +0x00000040, 0x00000000, 0x002db802, 0x27cc08ff, 0xb9c02ff0, 0x3c24a700, +0xc187f4c3, 0x00bf003e, 0xaf2023bc, 0xaf38b7c0, 0xf003ec08, 0x003bc02d, +0x433c00d7, 0x0bc0aff0, 0x00000660, 0x00000000, 0x0037a015, 0x126c001f, +0x75c03d70, 0x481cd300, 0x450db113, 0x00572034, 0x6d70137c, 0x1328b7c4, +0x32136c00, 0x0977c88d, 0x134c10c3, 0x54c0adf0, 0x00000e00, 0x00000000, +0x00398812, 0x220400ed, 0x78404f10, 0x0404f101, 0x404e1233, 0x00a100bd, +0x4e1133bc, 0x23013f40, 0x505ba440, 0x04bb404e, 0x238502e3, 0x48400fd0, +0x00000600, 0x00000000, 0x00790003, 0x078401cd, 0x79409e50, 0xa505e903, +0x021e9007, 0x03e51178, 0xbe5077a4, 0xb9427b40, 0x50179418, 0x407e411e, +0x17a401f1, 0x0e405ed0, 0x00000402, 0x00000000, 0x00632812, 0x030400dd, +0x31400c10, 0x2482c970, 0x409d9003, 0x01810030, 0x0c900314, 0x81003340, +0x50031401, 0x0033403c, 0x032440c1, 0x4a401cd0, 0x00000c20, 0x00000000, +0x0015a817, 0x014dc07f, 0x55c40575, 0xec015b03, 0xd885b001, 0x0275101c, +0x037000f4, 0x790817c0, 0x70017c03, 0x060fc033, 0x60e00073, 0x5ed183d2, +0x00000620, 0x00000000, 0x12070012, 0x005c001f, 0x06c001f4, 0x5c0c1780, +0xc0017200, 0x411f0087, 0x4173007c, 0x170007c8, 0xf2006c01, 0x0087c021, +0x005c0017, 0x49d001f0, 0x00000400, 0x00000000, 0x40a70810, 0x020c0093, +0x35c40930, 0x4c00df00, 0xc00db002, 0x05970164, 0x8172607c, 0x93402742, +0x70020c00, 0x0007c111, 0x084d008e, 0x40d00150, 0x00000c20, 0x00000000, +0x00a20001, 0x02450091, 0xa5c00914, 0x45009d00, 0xc0090402, 0x029511e0, +0x31100c34, 0x95002744, 0x30026c00, 0x00c5c231, 0x0004039d, 0x04480011, +0x00000080, 0x00000000, 0x00a42018, 0x02440191, 0x25400914, 0x14008d04, +0x40095002, 0x82950026, 0x01504076, 0x8d002341, 0xd0025400, 0x010760a1, +0x0065069d, 0x62400150, 0x00000200, 0x00000000, 0x0a242010, 0x32040881, +0x21406894, 0x16048d00, 0x406850a2, 0x088120a0, 0x88102274, 0x81012342, +0x10a2340c, 0x02214088, 0x2304088c, 0x42480952, 0x00000080, 0x00000000, +0x0086901d, 0x10441613, 0x01c44130, 0x5c481f04, 0x40407050, 0x30148946, +0x61705876, 0x1f8307c5, 0x70501c15, 0x0597c161, 0x586c121d, 0x76c3c170, +0x00000a80, 0x00000000, 0x012fb819, 0x0afc04bf, 0x27c26972, 0x6c2c9d04, +0xd069b092, 0x80bf0327, 0x4b5112be, 0xafb32740, 0xf0126c04, 0x012bc24b, +0x92fc049f, 0x65c04bb0, 0x00000e60, 0x00000000, 0x802fa018, 0x227c10bf, +0x25c08938, 0x7c109702, 0xc8893012, 0x00bf152c, 0x5bf216dc, 0x9301a4c1, +0xf0a25c86, 0x256cc11b, 0x46cc00bf, 0x64c099b0, 0x00000e00, 0x00000000, +0x0007081c, 0xa874021d, 0x8440a110, 0x74221108, 0x48a39400, 0x041d0d84, +0x51d05445, 0x19010140, 0x90285604, 0x01c46031, 0x1d448b1d, 0x70400110, +0x00000c22, 0x00000000, 0x0423a010, 0x1234148d, 0xa1404810, 0xf034a501, +0x404a104a, 0x12cc2021, 0x48d03204, 0x8901a241, 0xd1122406, 0x0c225149, +0x4244149d, 0xc04068d0, 0x00000e80, 0x00000000, 0x0025a818, 0x1234009d, +0x20600910, 0xf400b124, 0x400b9002, 0x009d0824, 0x25d00044, 0x89082742, +0x90027400, 0x28064001, 0x0047001c, 0x60418850, 0x00000620, 0x00000000, +0x0227a805, 0x027c129d, 0xe5420930, 0x7c1b9720, 0x90592002, 0x869f8025, +0x01d1105c, 0x934026c8, 0xf202740c, 0x11066241, 0x0444001d, 0x14d029f1, +0x00000e20, 0x00000000, 0x00258014, 0x027c099f, 0x27c00974, 0x7c019f02, +0xc0997002, 0xc08f2427, 0x41f0107c, 0x970021c8, 0xb1025c01, 0x0145c541, +0x247c001f, 0x53c019b0, 0x00000600, 0x00000000, 0x01050814, 0x007c0413, +0x06ca00b0, 0xcc023f08, 0xc002f000, 0x00170205, 0x01f0107c, 0x130007c1, +0x30005220, 0x2387c801, 0x006c841b, 0x50c001f0, 0x00000420, 0x00000000, +0x001ca014, 0x01740271, 0x14400510, 0x04005d00, 0x4005d001, 0x0071001c, +0x52d100f4, 0x51001f42, 0x10014c00, 0x00cf4922, 0x64840121, 0x52c007d0, +0x00000200, 0x00000000, 0x00b2a014, 0x033401c1, 0x32400c90, 0x0400cd00, +0x420cd003, 0x00cd2070, 0x1cd80f14, 0xc5003340, 0x10033400, 0x0033403c, +0x0b0503cd, 0x40600cd0, 0x00000a00, 0x00000000, 0x00688005, 0x23b41061, +0x38400c10, 0x84000d00, 0x5000d000, 0x40690270, 0x8ed00bb4, 0xc5233a40, +0x10078404, 0x007b408e, 0x038410e5, 0x06404fd0, 0x00000200, 0x00000000, +0x407c1035, 0x07fc01a3, 0x5ac85eb4, 0x8c00ef10, 0x401ef087, 0x21ef0178, +0x5ef00fbc, 0xe7007bc3, 0x30171007, 0x157b815e, 0x1f8c01cf, 0x44c4fef0, +0x00000040, 0x00000000, 0x0025b810, 0x5b7cc01f, 0x15c0ad70, 0x7c001f00, +0xc401f000, 0x00d70076, 0x4de0537c, 0xdb4437c0, 0xf02b7c62, 0x11b3c06d, +0x035d06db, 0x43c00cf0, 0x00000660, 0x00000000, 0x005fa000, 0x17fca1f3, +0x7cc03f30, 0xfc01f700, 0xc09fb007, 0x01f3007a, 0x9ff207cc, 0xf3007ec0, +0x322f4c03, 0x2474c21f, 0x2fcc1bef, 0x08c01ff2, 0x00000e20, 0x00000000, +0x10898815, 0x13bc88e1, 0x3ac00f10, 0xb4002b00, 0x40c21020, 0x086b0198, +0x8ed00384, 0xf1003f4a, 0xb007c400, 0x0570400e, 0x03840ce1, 0x54400ed0, +0x00000660, 0x00000000, 0x001d0000, 0x333400a9, 0x18400e50, 0xb400e500, +0x440c9003, 0x10e1082e, 0x0ed00384, 0xe9003b42, 0x98038408, 0x0138500f, +0x03a480f5, 0x00400ed0, 0x00000400, 0x00000000, 0x00432804, 0x03141389, +0xd0400c10, 0x74801102, 0x4280d000, 0x91c10000, 0xacd16b05, 0xc9403340, +0x10034400, 0x20f0423c, 0x6f2400c1, 0x48400cd0, 0x00000c00, 0x00000000, +0x0435a815, 0x03f400db, 0x34500f60, 0x7840d510, 0xc00db003, 0x0180003a, +0x3cf00744, 0xf9003fc0, 0x9603c502, 0x00f4401c, 0x0b6600c7, 0x44c1bfe1, +0x00000600, 0x00000000, 0x50b70001, 0x037c0857, 0x37c80df4, 0x7c001f01, +0xc1003000, 0x00df80a7, 0x2df2037c, 0xd70033c0, 0xf0837c0c, 0x1137c0cd, +0x035d20d7, 0x27c00df0, 0x00000c00, 0x00000000, 0x004f0880, 0x03bc01ff, +0x1ec00fb0, 0x4c00ff00, 0x800f3003, 0x40bf005c, 0x1fb003e0, 0xe3403ec1, +0x9003cc00, 0x203ec80f, 0x07cc00ff, 0x94400ff0, 0x00000c00, 0x00000000, +0x20060081, 0x0374205d, 0x14c00d10, 0x45001d00, 0x40011400, 0x01910245, +0x9dd00354, 0xd1003540, 0x10036c00, 0x0034400d, 0x4b5500dd, 0x14400cd0, +0x00000802, 0x00000000, 0x8234a001, 0x0374181d, 0x36400c90, 0x5400dd08, +0x400d1003, 0x03950035, 0x0dd00754, 0xd1003640, 0xd0036400, 0x0074401d, +0x134621dd, 0x05400dd0, 0x00000200, 0x00000000, 0x80302010, 0x0334000d, +0x32400c10, 0x04000908, 0x40001000, 0x00910001, 0x0dd80714, 0xc1003040, +0x10132420, 0x2570501c, 0x0304a1c9, 0x51400cda, 0x00000080, 0x00000000, +0x0006b000, 0x03fc001f, 0x12c40fb1, 0x5c00df10, 0x400d3003, 0x40970005, +0x0db0037e, 0xf3403ec8, 0xf007ec00, 0x0976c80d, 0x034c00df, 0x05c00ff0, +0x00000ac0, 0x00000000, 0x000bb805, 0x03fc003f, 0x1de00ff0, 0xfc003f00, +0xc003f000, 0x20a7003f, 0x0ff003dc, 0xff003fc0, 0xf20bfc00, 0x057be00e, +0x03fc00ff, 0x06c80ef0, 0x00000e40, 0x00000000, 0x000fa003, 0x10fc243f, +0x1fc00330, 0xcc003301, 0xc46f2070, 0x0033023c, 0x033000cc, 0xb3583cc4, +0xb010cc40, 0x052fc00b, 0x03cc067f, 0x0cc08f30, 0x00000e00, 0x00000000, +0x00070803, 0x28742a1d, 0x174005b0, 0x445ad102, 0x4089b01b, 0x40114231, +0x0c100244, 0x91003040, 0x10281400, 0x04a74029, 0x13d4065d, 0x05404e10, +0x00000c20, 0x00000000, 0x0003a013, 0x0210000d, 0x11600810, 0x05040148, +0x404c1100, 0x00c12030, 0x0c100314, 0x85001140, 0x18001400, 0x09314028, +0x4324024d, 0x44404c50, 0x00000e80, 0x00000000, 0x0115a803, 0x0574301d, +0x17608d90, 0x4500d100, 0x640d9803, 0x00d100b5, 0x00100154, 0x85003550, +0x10034404, 0x0037401d, 0x0374635d, 0x0d400c50, 0x00000620, 0x00000000, +0x28a78882, 0x043c031f, 0x1dc12130, 0xcc003301, 0xe20d3400, 0x00932094, +0x0534235c, 0x9704b5c0, 0xb04d5e00, 0x0227c009, 0x032c015f, 0x08c00d74, +0x00000e20, 0x00000000, 0x0025800f, 0x01fc02ff, 0x1fc407d0, 0xbc00ff00, +0xe00af043, 0x05af003f, 0x0ff0076c, 0xbb38bec0, 0xf421fd01, 0x042fc00b, +0x03dc00ff, 0x1fc00fb1, 0x00000600, 0x00000000, 0x40850802, 0x005c00d3, +0x13c009f0, 0x7c000700, 0xc40d7000, 0x02d34030, 0xad34023c, 0x970634c1, +0xb4017c98, 0x4077c109, 0x035c43d3, 0x09c00d30, 0x00000420, 0x00000000, +0x2014a01b, 0x097600d0, 0x95c05dd0, 0x7404d112, 0x405db003, 0x00f12036, +0x05106274, 0x91c02440, 0x30817400, 0x00b74049, 0x6bc401d1, 0x4c400fb0, +0x00000200, 0x00000000, 0x8212a007, 0x40241001, 0x730010d2, 0x34200980, +0x401cd220, 0x06c10131, 0x18170d34, 0x81207700, 0x10126402, 0x0022442c, +0x07140051, 0x1d480c16, 0x00000a00, 0x00000000, 0x0048800d, 0x04b409e1, +0x714012d0, 0xb409e910, 0x401a9227, 0x21e1087b, 0x1e1007b4, 0xa1127a40, +0x9006b401, 0x006b401e, 0x878409e1, 0x10401e95, 0x00000200, 0x00000000, +0x01b01012, 0x011c00c1, 0x33c044f0, 0x3c000f44, 0xc08cf020, 0x02810031, +0x4c31833c, 0x850233c2, 0x30123c00, 0x0027c00c, 0x031c00d3, 0x49c00c30, +0x00000040, 0x00000000, 0x203db802, 0x01fc20ff, 0x3fc807f0, 0xbc00f702, +0xc00ff023, 0x00bf023e, 0x09f0017c, 0xbf123dd0, 0x3002fe00, 0x002ec00f, +0x03be20ff, 0x0bc10ff4, 0x00000660, 0x00000000, 0x1077a015, 0x016e0013, +0x37c40970, 0x4c001f00, 0xc00d3000, 0x01532137, 0x6d300358, 0x9b8034c0, +0x32034c01, 0x0030180d, 0x136c001b, 0x57c10d34, 0x00000e00, 0x00000000, +0x08218913, 0x01ad00e1, 0x33000a10, 0x2c20c110, 0x400ab003, 0x0261023b, +0x0e100384, 0xa3403c41, 0x1003c500, 0x5039400e, 0x330420a1, 0x4b424e10, +0x00000620, 0x00000000, 0x00790001, 0x058481e1, 0x7b401e50, 0xa4012580, +0x401e1004, 0x0589007b, 0x1c104784, 0xe1007a50, 0x18078401, 0x08fa4a1e, +0x37b60181, 0x0f401e10, 0x00000400, 0x00000000, 0x00b32812, 0x81260ac1, +0x37446c50, 0x6400c500, 0x400c9003, 0x028101b3, 0x0c1c0706, 0xc900f240, +0x101b0501, 0x0077401c, 0x03540589, 0x4b400c14, 0x00000c20, 0x00000000, +0x401da817, 0x11cc0b73, 0x17c82770, 0x6c005f00, 0xc0053001, 0x0a5b4097, +0x05300d4c, 0x53005ec8, 0x3419cc21, 0x0016c005, 0x017c077b, 0x5fc00530, +0x00000620, 0x00000000, 0x00870012, 0x487c021f, 0x07c40180, 0xfc002a00, +0xc001f008, 0x001f0003, 0xa1f0406d, 0x170205c0, 0xf0087c08, 0x0005c081, +0x006c0017, 0x4bc001f4, 0x00000c00, 0x00000000, 0x04270810, 0x024c008f, +0x24c00830, 0x44009311, 0x80093002, 0x00930037, 0x0930027c, 0x924424d8, +0x30263c00, 0x0024c008, 0x025c0083, 0x40d00830, 0x00000c20, 0x00000000, +0x89262001, 0x0a04029d, 0xe4441950, 0x44009141, 0x4499b002, 0x109b2427, +0x29120a76, 0x91a02440, 0xb0027403, 0x40a6c209, 0x02448091, 0x044009b4, +0x00000800, 0x00000000, 0x2064a018, 0x4344109d, 0x24404910, 0xd608b100, +0x40091002, 0x00912027, 0x0e1442b4, 0x95202040, 0x10025418, 0x00644009, +0x02540091, 0x60400910, 0x00000200, 0x00000000, 0x02202010, 0x8204008d, +0x20408850, 0x9408a102, 0x40889022, 0x08800223, 0x8e1022b4, 0x85223040, +0x90223408, 0x82224088, 0x0a040281, 0x40404890, 0x00000080, 0x00000000, +0x0586b01d, 0x504d141d, 0x84d16110, 0xdd161325, 0xc1613059, 0x16114583, +0x63315834, 0x050084c1, 0x30d95c02, 0x0594c160, 0x005c8013, 0x74c0b134, +0x00000ac0, 0x00000000, 0x012fb119, 0x03bc20af, 0x27c84bf0, 0x6c849f01, +0xc44be012, 0x04bf0127, 0x49f2137c, 0xb9012fc0, 0xf012fc86, 0x012fc04b, +0x0a7c02bf, 0x67c099f0, 0x00000e60, 0x00000000, 0x062fa018, 0x127c049b, +0x6cc12b30, 0x8c059301, 0xc1493006, 0x209b01a7, 0x893212c8, 0x92002040, +0x111a4c02, 0x01acc029, 0x02fc10bf, 0x60c00930, 0x00000e00, 0x00000000, +0x4087081c, 0x08740211, 0x85006111, 0x44161141, 0x41411058, 0x14510183, +0x01105044, 0x11050440, 0x1030440a, 0x01044063, 0x0074021d, 0x7041e172, +0x00000c20, 0x00000000, 0x0123a012, 0x22340881, 0xa0604c12, 0x14028104, +0x4008541a, 0x02950363, 0x3a920a14, 0xa1012c44, 0x90128400, 0x01a040ca, +0xc234348d, 0x40500810, 0x00000e80, 0x00000000, 0x0860a818, 0x027434d1, +0x24400910, 0x54809100, 0x44095002, 0xc4950023, 0x1b901245, 0xb1042c48, +0x900a8404, 0x2064400b, 0x0274e29d, 0x60400950, 0x00000620, 0x00000000, +0x01a7a805, 0x027c8293, 0x24d12934, 0x5d009340, 0xc0097002, 0x03874027, +0x09b20e5c, 0x9302a4d0, 0xb4024d00, 0x9064f019, 0x027c829f, 0x14c00932, +0x00000e20, 0x00000000, 0x04258016, 0x0f3c20d7, 0x27c099f0, 0x6d00df00, +0xc09db002, 0x21d30037, 0x0870026c, 0x9f4066c1, 0x700e7c00, 0x0027c049, +0x0274009f, 0x53c00970, 0x00000600, 0x00000000, 0x08850814, 0x007c041f, +0x03c20034, 0x0c000f04, 0xc0017084, 0x02134407, 0x01b0084d, 0x134184c0, +0x34484c40, 0x0045c101, 0x041c021f, 0x50c00034, 0x00000420, 0x00000000, +0x01dca014, 0x0170825d, 0x9e400555, 0xc5005d00, 0x41155049, 0x08500097, +0x3510615c, 0x5100d040, 0x100d4400, 0x149f4135, 0x0dc4415d, 0x50400530, +0x00000200, 0x00000000, 0x00f2a014, 0x133402cd, 0xb3400c50, 0x05804d02, +0x40140006, 0x00c10003, 0x3c900e44, 0xd100f040, 0x100b2401, 0x0031603d, +0x8314018d, 0x50400c90, 0x00000a00, 0x00000000, 0x00288005, 0x03f400ed, +0x0b400e50, 0x90104d00, 0x40065013, 0x00e1200b, 0x0a102794, 0xe100ac41, +0x1041a402, 0x002b400a, 0x600411ad, 0x14404e10, 0x00000200, 0x00000000, +0x00781015, 0x07bc91ef, 0x7b401e74, 0x8d016f20, 0xc116300e, 0x01c3022b, +0x14b2108c, 0x710058d0, 0x3007ed01, 0x0059c01e, 0x179c01bf, 0x54d03cb0, +0x00000040, 0x00000000, 0x0025b810, 0x133c06dc, 0x36c00df0, 0x6c021f00, +0xc041f003, 0x02de0127, 0x09f1032c, 0xdf0027d0, 0xf0015c40, 0x20374009, +0x847c009f, 0x43c16df0, 0x00000660, 0x00000000, 0x005fa000, 0x8ffc01f3, +0x7fc01ff1, 0xcc037302, 0x80173007, 0x03f3004f, 0x1f3006cc, 0xbb037cc0, +0x3006cc01, 0x007fc03f, 0x04ec01f3, 0x00c01f30, 0x00000e00, 0x00000000, +0x201d8815, 0x0bb408e3, 0x08400fd0, 0xac106b00, 0x4006100b, 0x00ef000b, +0x4bd002c4, 0xf1422840, 0x70528400, 0x000bc00e, 0x818444b5, 0x54400e54, +0x00000600, 0x00000000, 0x00290000, 0x03940061, 0x01400ed0, 0x34086980, +0x41061023, 0x00c900ab, 0x26900094, 0x21051958, 0x18030400, 0x001b400e, +0x203422e1, 0x00400e10, 0x00000400, 0x00000000, 0x20232804, 0x0b3446c1, +0x00441cd0, 0x34010900, 0x40001007, 0x00c91023, 0x01d04814, 0x81000540, +0x50450400, 0x0013400d, 0x01340085, 0x10400d50, 0x00000c20, 0x00000000, +0x4075a815, 0x0b5c03d3, 0x35c00df0, 0xfc035b00, 0xc0153047, 0x00dbc817, +0x0d3102dd, 0xd30075c8, 0x300f4d00, 0x0017c915, 0x03fc00d3, 0x54d00f34, +0x00000620, 0x00000000, 0x02270001, 0x2b7c03c7, 0x05c08df0, 0x6c045f00, +0xc405f043, 0x00d70013, 0x1d70036c, 0xd70136e0, 0xf0017c00, 0x0095c401, +0x004c80df, 0x07c00df0, 0x00000c00, 0x00000000, 0x001f0880, 0x43cc00f3, +0x3fc00fb0, 0xac405300, 0xe4173203, 0x00f3023c, 0x573000ec, 0x63015dc0, +0x7003cc00, 0x0154c007, 0x039c01e3, 0x01c00d30, 0x00000c22, 0x00000000, +0x01862001, 0x030400d5, 0x37400d10, 0x44001100, 0xc091b023, 0x06d500b6, +0x0d160344, 0x51403454, 0x10016c50, 0x54114021, 0x806c09d1, 0x04400db0, +0x00000802, 0x00000000, 0x0010a001, 0x034400d1, 0x33401d90, 0x46880180, +0x40005103, 0x00c10094, 0x0d100224, 0x91003540, 0x90220480, 0x201440ad, +0x005460d1, 0x05400d10, 0x00000200, 0x00000000, 0x00102010, 0x030520c5, +0x03600810, 0x04000100, 0x4000d001, 0x00c54032, 0x08508204, 0xc1002040, +0x15022400, 0x00016000, 0x012400c1, 0x40400c90, 0x00000080, 0x00000000, +0x0006b000, 0x014400d1, 0x07c00db0, 0x4d001340, 0x40017403, 0x00d34030, +0x0530006c, 0x11001148, 0x71024c00, 0x0814c80d, 0x005c20d3, 0x01c00f30, +0x00000a80, 0x00000000, 0x000fb805, 0x03fc00ff, 0x0fc00ff0, 0xf4203f00, +0x8003b003, 0x007f003f, 0x03b200fc, 0x3f000fc4, 0xf000fc80, 0x181f8203, +0x01fc40ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x003ba003, 0x02cc08fb, +0x0ec04fb4, 0xfc003f00, 0xc00eb000, 0x08392039, 0x0fb2539c, 0xe3083fc0, +0x700bcc00, 0x013cc04f, 0x01e81cf3, 0x0cc08f30, 0x00000e00, 0x00000000, +0x00370801, 0x6a4502d1, 0x07448d11, 0x44044f10, 0x400d1b00, 0x0e110034, +0x0d120bd4, 0xf702bf41, 0x1020c500, 0x25bd406f, 0xab4c02f5, 0x04404d52, +0x00000c20, 0x00000000, 0x0037a011, 0x126486c1, 0x02440810, 0x15100d00, +0x440c5000, 0x24090073, 0x4800d325, 0xc100324a, 0x50134580, 0x0230418c, +0x036424c9, 0xc4401c50, 0x00000e80, 0x00000000, 0x4135a803, 0x006401d5, +0x07400910, 0x4402d500, 0x440d5400, 0x80510076, 0x0d100344, 0xd9003741, +0x00114400, 0x0035400d, 0x234400dd, 0x0c401c50, 0x00000620, 0x00000000, +0x0033a802, 0x032407d3, 0x46ca19b0, 0x5c021d20, 0xc53dd422, 0x125b3037, +0x3d34037c, 0xd30037c0, 0x60044d80, 0x0034c20d, 0x0f2c00db, 0x08d00d50, +0x00000e20, 0x00000000, 0x003d8007, 0x82dd00d3, 0x4fc19b70, 0x7d107f01, +0xc08f30c6, 0x00a7043d, 0x3ff083fc, 0xf74037c2, 0xf001dc00, 0x003fc00f, +0x07fc00e7, 0x1fc00ff0, 0x00000600, 0x00000000, 0x00350802, 0x076c02d3, +0x07c08930, 0x3d001340, 0xc42d2082, 0x025b0036, 0x8930033c, 0xc74033c0, +0xf0014c04, 0x0034c00d, 0x0b4c00d3, 0x09c00df0, 0x00000420, 0x00000000, +0x0034a013, 0x014400d1, 0x84402912, 0x5403d101, 0x441c500e, 0x06f100b4, +0x391003c4, 0xf5203f40, 0xd0010482, 0x003c400f, 0x07c440f1, 0x4c420d10, +0x00000200, 0x00000000, 0x0032a007, 0x020405d1, 0xc2603892, 0x14009101, +0x419c5401, 0x138d1076, 0x0c100314, 0xc1003361, 0xd0030401, 0x0034400d, +0x030400c1, 0x1d400c50, 0x00000a00, 0x00000000, 0x406c8004, 0x0c0519a9, +0x4840da10, 0x8401e100, 0x409e50c5, 0x0135207c, 0x9e102784, 0xe1907b00, +0xd1058511, 0x907c041f, 0xc7d401f1, 0x10401f10, 0x00000200, 0x00000000, +0x00301032, 0x220d0803, 0x02e04d30, 0x1c908300, 0xc00c7401, 0x008f0432, +0x0c32033c, 0xc74033c0, 0xf2090c00, 0x0130c00c, 0x1a0c00c3, 0x49c00c70, +0x00000040, 0x00000000, 0x203db802, 0x82dc08b7, 0x0ac44f74, 0x5c88ff02, +0xc00fa021, 0x006b143f, 0x0bb043fc, 0xff043fc2, 0xf0215c08, 0x003bc08e, +0x43ac02ef, 0x0bc02e72, 0x00000660, 0x00000000, 0x0027a015, 0x034c009f, +0x24c00df0, 0x5d009f00, 0xc99db04b, 0x04db00b7, 0x7c32176c, 0xd78936c8, +0xf0114c13, 0x0934844d, 0x030808d3, 0x54c0ddb0, 0x00000e00, 0x00000000, +0x00298812, 0x008400ed, 0x38400ed0, 0x0400ed00, 0x044e1003, 0x11a1053f, +0x8e1033c4, 0xff023040, 0x10413402, 0x0531414c, 0x179c04f5, 0x48400f50, +0x00000620, 0x00000000, 0x00690003, 0x47a6016d, 0x69401cd0, 0xb4018d0c, +0x401f0007, 0x09e90179, 0x3a1407a4, 0xe8497a40, 0xd005b425, 0x037840de, +0xc78401e1, 0x0f705ed2, 0x00000400, 0x00000000, 0x00632812, 0x076409dd, +0x61401cd1, 0x2400cd00, 0x410c9003, 0x83c10032, 0x30180304, 0xc9003048, +0x10397480, 0x4031480c, 0x031400c5, 0x4b400c50, 0x00000c20, 0x00000000, +0x0115a817, 0x01ed007f, 0x99d905f0, 0x7c805f00, 0xc433b409, 0x124b06c9, +0x8530016c, 0x570016c0, 0xe005dc00, 0x001c8007, 0x01cc0073, 0x5fc003f0, +0x00000620, 0x00000000, 0x00070012, 0x805c001f, 0x46c001f0, 0x5c021f00, +0xc8813048, 0x00170085, 0x21f0005c, 0x17a007c2, 0xf0005400, 0x2007c001, +0x005c001f, 0x488011f0, 0x00000c00, 0x00000000, 0x00270810, 0x024c019f, +0x24d05830, 0x6c858300, 0xc021b002, 0x11d90046, 0x1db0025c, 0x930026c0, +0x30027c20, 0x08248408, 0x42480093, 0x43c00171, 0x00000c20, 0x00000000, +0x00262001, 0x0254119d, 0xa4401912, 0x04079314, 0x4031150e, 0x05930307, +0x09d00254, 0x91182740, 0x10027408, 0x0026c009, 0x86040093, 0x07400110, +0x00000800, 0x00000000, 0x00248018, 0x0745089d, 0x34440914, 0xe6009161, +0x40015012, 0x44950007, 0x89d28254, 0x95502342, 0x10027400, 0x00234009, +0x12540081, 0x63400150, 0x00000200, 0x00000000, 0x00a02010, 0x3604048d, +0x26406814, 0x8508a100, 0x408c1062, 0x00858227, 0x88d00204, 0x81842340, +0x10323004, 0x202340c8, 0x02140881, 0x43408810, 0x00000080, 0x00000000, +0x0006b01d, 0x504c8c1f, 0x04c04514, 0xec101300, 0xc9617009, 0x0e171507, +0x05d1b854, 0x131007c1, 0x34507c2c, 0x4387c061, 0x505d1413, 0x77c16170, +0x00000ac0, 0x00000000, 0x002fb819, 0x12fc0caf, 0x2dcc6bf0, 0x7c209e00, +0xc04af012, 0x0c8b412f, 0x09b0327d, 0x9d0467c0, 0xf212fc8c, 0x03260469, +0x02eca09f, 0x67c04bf0, 0x00000e60, 0x00000000, 0x0027a818, 0x72cc08bf, +0x2fd14bf0, 0xcc12b700, 0xc07bb882, 0x54aa236d, 0x0bf91a4c, 0xb7032ec2, +0xb00a6c04, 0x00a5c029, 0x42ec0093, 0x63c05c30, 0x00000e00, 0x00000000, +0x0407081c, 0x09440a1d, 0x04406110, 0x6c000da0, 0x4015187c, 0x26150017, +0x61d80046, 0x11800141, 0x103c4404, 0x0886c821, 0x084c0201, 0x734001b1, +0x00000c20, 0x00000000, 0x0923a210, 0x1224848d, 0x25510840, 0x04049d00, +0x40681402, 0x508121a1, 0x28d29a14, 0x8141a042, 0x10022412, 0x01a24068, +0x02261681, 0x43417898, 0x00000e80, 0x00000000, 0x0025a018, 0x0264009d, +0x34400910, 0x6000dd40, 0x60815100, 0x209d1227, 0x18d00255, 0x85002540, +0x11224400, 0x00264009, 0x43440091, 0x63401998, 0x00000620, 0x00000000, +0x0067a005, 0x0e6d119d, 0x23c00870, 0x4c069500, 0xe0311000, 0x01832021, +0x59d00254, 0x910024c0, 0x34126c00, 0x4026d009, 0x0e6c0093, 0x17c009bc, +0x00000e20, 0x00000000, 0x00a58034, 0x265c048f, 0x64c03970, 0x7c009702, +0xc3113000, 0x24970067, 0x09f2026c, 0x9b9027e4, 0xf0023c00, 0x0020c008, +0x263c009f, 0x53c00978, 0x00000600, 0x00000000, 0x00050014, 0x084c501f, +0x07c08134, 0x6c8e1f00, 0xc0213410, 0x00130887, 0x01320054, 0x174007c1, +0x30087400, 0x0007c801, 0x8c7c8004, 0x51c20130, 0x00000420, 0x00000000, +0x0014a814, 0x11c5137f, 0x1f408710, 0x84007d00, 0x41131048, 0x007b205f, +0x273081c4, 0x71101744, 0xb2017400, 0x10174005, 0x09c40051, 0x50400750, +0x00000200, 0x00000000, 0x0032a014, 0x0544028d, 0xf3401c12, 0x34019d01, +0x401cd10b, 0x00c10453, 0x2c100335, 0xcc0a7540, 0x18033400, 0x0033600c, +0x221400c5, 0x51400c90, 0x00000a00, 0x00000000, 0x09388005, 0x01a500ad, +0x3b400e10, 0x96006d04, 0x604e1003, 0x04e9041b, 0x02801714, 0x61003b4c, +0x9a033420, 0x003340ce, 0x020408e1, 0x14405f90, 0x00000200, 0x00000000, +0x00f81015, 0x050c81ff, 0x7bc01e10, 0x2c81ed00, 0xc29e7457, 0x4321085f, +0x1a240798, 0xef247bc0, 0x3007be11, 0x28fbc15e, 0x029c21e7, 0x55c03eb0, +0x00000040, 0x00000000, 0x01b59010, 0x01504057, 0x27c00df0, 0x6c00df10, +0xc06d3103, 0x011f0017, 0x0173736c, 0x1f0037c4, 0xf007fc00, 0x2437c86d, +0x025c02df, 0x43c12d70, 0x00000660, 0x00000000, 0x00ffa200, 0x05fc41b3, +0x7dc09f30, 0xfc017b00, 0xc19e7047, 0x01fb007d, 0x97b087ec, 0xfb406ec8, +0xf087d401, 0x037cc01f, 0x05c40bf3, 0x00c01fc0, 0x00000e00, 0x00000000, +0x02398815, 0x19b600ab, 0x38400e10, 0x8404ed00, 0x488e3003, 0x02fd001c, +0x021013ed, 0x61012948, 0xd053ac08, 0x023dc00e, 0x50840cf3, 0x54404ef0, +0x00000620, 0x00000000, 0x00390000, 0x013412e9, 0x3a640e10, 0x10008502, +0x008f1023, 0x0821021b, 0x081903a4, 0xe5802840, 0xd0838400, 0xa13a408c, +0x00a400e9, 0x00410ed0, 0x00000400, 0x00000000, 0x20772804, 0x093080c1, +0x72000c10, 0x0402cc00, 0x644c1183, 0x07042112, 0x30100324, 0x05002545, +0xd2071440, 0xc031400c, 0x082400c1, 0x90403c50, 0x00000c20, 0x00000000, +0x417ca815, 0x057003c9, 0x76500d14, 0x5d879701, 0x484d1c03, 0x07eb0117, +0x5db083ed, 0x5f0024c0, 0xf82bc500, 0x003ec00f, 0x086d00fb, 0x54c03fd0, +0x00000620, 0x00000000, 0x00370001, 0x4d3c809f, 0xb4c80df0, 0x5c00cf20, +0xc00db003, 0x021f1085, 0x01f4035c, 0x930025c2, 0xf9036c00, 0x0035c00d, +0x005c00df, 0x07c00df0, 0x00000c00, 0x00000000, 0x003f0880, 0x15cc00f3, +0x7cc00c30, 0xad10f300, 0xc80f3a0f, 0x01fa021f, 0x0b7083dd, 0x67206ec0, +0xf003cc08, 0x083cca0f, 0x44bc00f6, 0x00c30f30, 0x00000c22, 0x00000000, +0x40360081, 0x0b450151, 0x70d01d10, 0x44025100, 0x400d300f, 0x03110112, +0x01300364, 0xd3006740, 0xd0034412, 0x0036c40d, 0x047400d1, 0x04420cb0, +0x00000802, 0x00000000, 0x8034a001, 0x81550191, 0x36483d91, 0x6780d102, +0x481d5443, 0x28dd0017, 0x05d28335, 0x550a3762, 0x50036500, 0x0030400c, +0x107600d5, 0x04400d10, 0x00000200, 0x00000000, 0x20302010, 0x01140091, +0x32400412, 0x0400c040, 0x601c9403, 0x00058012, 0x00500324, 0x89003340, +0xd0030100, 0x0032400c, 0x003480c5, 0x40400c90, 0x00000080, 0x00000000, +0x883ab000, 0x015c00d1, 0x36c00db4, 0x6d001100, 0xc00d1003, 0x00df0017, +0x097003d4, 0x65403ac0, 0xf0036d00, 0x083cc00f, 0x007c00f7, 0x00c00f30, +0x00000ac0, 0x00000000, 0x803fb805, 0x02ed00ff, 0x1de00b70, 0xf800ff00, +0xc20f7403, 0x003b001f, 0x033003dd, 0xf5003fc4, 0xf083fc00, 0x003fc00f, +0x00fc00fb, 0x17c00ef0, 0x00000e60, 0x00000000, 0x023fa003, 0x53cfc0ff, +0x3b400ff3, 0xecc03300, 0xc483d200, 0x483b233c, 0x8370a0c5, 0x37020cc0, +0x31008d48, 0x003cc003, 0x00cd8423, 0x0cd40b35, 0x00000e00, 0x00000000, +0x01370801, 0x934410fd, 0x374005d0, 0x44009100, 0x400d9003, 0x061302bc, +0x01100044, 0x11002444, 0x10004400, 0x003ec541, 0x01444855, 0x04400910, +0x00000c20, 0x00000000, 0x2033a011, 0x532404c4, 0x67460cd0, 0x14004500, +0x4008d202, 0x08c50130, 0x44500344, 0x85613048, 0x50000444, 0x00324008, +0x01140001, 0x44400c50, 0x00000e80, 0x00000000, 0x8035a803, 0x036400dd, +0x17400dd0, 0x7520c500, 0x502d9003, 0x00d40034, 0x45100244, 0x11083041, +0x50504404, 0x88364009, 0x03140045, 0x0c400c10, 0x00000620, 0x00000000, +0x0037a802, 0x036400cf, 0x77c81df0, 0x6c62d700, 0xc094f069, 0x0217006c, +0x3d70480c, 0xd70014d0, 0x740f4d01, 0x0034d105, 0x015c0553, 0x08c01d70, +0x00000e20, 0x00000000, 0x043d8007, 0x83dc00ff, 0x3fc027f0, 0x4500fb42, +0xc40fb006, 0x2333026b, 0x14f000fc, 0xef080fc0, 0xb007fc21, 0x0031c046, +0xa3ec027d, 0x1fc09f92, 0x00000600, 0x00000000, 0x00350802, 0x037c00df, +0x37c00df1, 0x4d029b00, 0xc12d3003, 0x00db4164, 0x2db01f6c, 0xd74134c0, +0x30293c10, 0x0036d029, 0x014c10d3, 0x08c10d34, 0x00000420, 0x00000000, +0x0074a013, 0x43f400fd, 0xba404fd0, 0x4400b100, 0xc02d1002, 0x00e100a6, +0xa5100244, 0xd100f6c0, 0x10857411, 0x003c4019, 0x2f7c00f1, 0x4c404d10, +0x00000200, 0x00000000, 0x00b2a007, 0x073408cd, 0x13400cd0, 0x04008909, +0x400c161b, 0x00050020, 0x08900a24, 0x4d000050, 0x14073400, 0x0031400c, +0x01040249, 0x1c402c10, 0x00000a00, 0x00000000, 0x00788004, 0x07b409ed, +0x5a4032d0, 0x85012100, 0x60121105, 0x01f50062, 0x1a100404, 0x292c6840, +0x1806b605, 0x0079411e, 0x45b41979, 0x10401e10, 0x00000200, 0x00000000, +0x06301012, 0x033488cf, 0x13c088f8, 0x0502db00, 0xc00c3003, 0x02c74120, +0x48b1032c, 0xcf003440, 0x30033c00, 0x4131c80d, 0x014c004a, 0x48d00d30, +0x00000040, 0x00000000, 0x003db802, 0x43fc3aff, 0x3ec80bf0, 0xfe007f00, +0xc00afa03, 0x00e3106f, 0x0bf0027c, 0x37203fc0, 0xf0227484, 0x003c808f, +0x03fc0077, 0x0bc08ff0, 0x00000660, 0x00000000, 0x0037a015, 0x034c02df, +0x77c08d30, 0x6c00d700, 0xc92d3403, 0x00130137, 0x0df0024c, 0xdb0014c0, +0x38036c40, 0x23b4c005, 0x014c01d3, 0x54c01d30, 0x00000e00, 0x00000000, +0x00398812, 0x138404ef, 0x3b400618, 0x2400f101, 0x40061001, 0x07e1023b, +0x0cd000ac, 0xe1200840, 0x1a030400, 0x24384104, 0x038402e1, 0x48400e10, +0x00000620, 0x00000000, 0x00790003, 0x170401cd, 0x7b405e10, 0x8409a503, +0x481e9007, 0x05f11873, 0x1ed20786, 0xe9007870, 0x1007a441, 0x0078441e, +0x058605e1, 0x0c401e10, 0x00000400, 0x00000000, 0x10332812, 0x034440c5, +0x33408c14, 0x24098100, 0x409c9007, 0x00d10273, 0xacd00226, 0xd9007441, +0x11172408, 0x4830401c, 0x070704c1, 0x48409c14, 0x00000c20, 0x00000000, +0x0015a817, 0x0145004d, 0x9fc01732, 0x6c025701, 0xc015b215, 0x00734017, +0x15f0014c, 0x5b0614d0, 0x35156d43, 0x0014d025, 0x1dcc1173, 0x5cd00530, +0x00000620, 0x00000000, 0x20070012, 0x007c001f, 0xc7c001f0, 0x1d001f00, +0xc0007000, 0x001f0003, 0x01f0003c, 0x170087c0, 0xf0005c00, 0x0003e0e1, +0x047d001f, 0x4bc801f0, 0x00000c00, 0x00000000, 0x00670810, 0x024c009f, +0x24e09930, 0x4c109f00, 0x80493002, 0x05934024, 0x0938026d, 0x970024c0, +0x30060c10, 0x5024c019, 0x027c0093, 0x43c00930, 0x00000c20, 0x00000000, +0x0a260001, 0x0244029d, 0xa6e03810, 0x44008d00, 0x4009100a, 0x809b0124, +0x0910024c, 0x9100a450, 0x11064100, 0x00a55098, 0x0274009b, 0x07400914, +0x00000800, 0x00000000, 0x8024a018, 0x0245029d, 0xa4440910, 0xc7009d08, +0x400b180a, 0x00910065, 0x0a100244, 0xb520ac40, 0x1012c400, 0x0424400b, +0x02340091, 0x63400910, 0x00000200, 0x00000000, 0x82302010, 0x2204088d, +0x20401810, 0x84089d00, 0x508a1c22, 0x00890221, 0x2a142204, 0xa190a840, +0x10028722, 0x00a1408a, 0x0a340289, 0x43402810, 0x00000080, 0x00000000, +0x0086b01d, 0x584c961f, 0x04414114, 0x4c561f05, 0xc1633058, 0x14130585, +0x4112584c, 0x070500c1, 0x34504c14, 0x4500c967, 0x003c0003, 0x77c00030, +0x00000ac0, 0x00000000, 0x0127b819, 0x127e049f, 0x2bd00bf0, 0x7c24bf00, +0xc049f092, 0x14a7012e, 0x09f012dc, 0x9f0827c0, 0xf0027c00, 0x0026c049, +0x02fc02ff, 0x67c00bf0, 0x00000e60, 0x00000000, 0x022fa818, 0x52cc10bf, +0x25c14bf0, 0x4c1c9345, 0xd00b3022, 0x04bb4368, 0x295022dc, 0x9314a6c0, +0xb0426c30, 0x0024c02b, 0x0a4c0697, 0x60c02930, 0x00000e00, 0x00000000, +0x0107081c, 0x186d021f, 0x05c021d0, 0x44061100, 0x40211428, 0x06110004, +0x50100044, 0x3100c440, 0xd0014437, 0x05c040a5, 0x04440911, 0x70401211, +0x00000c20, 0x00000000, 0x0023a010, 0x4204148d, 0x214008d0, 0x04048100, +0x41689212, 0x199101a0, 0x28501a14, 0xa9012a40, 0xd0022400, 0x00a04048, +0x12a404b5, 0x40424a14, 0x00000e80, 0x00000000, 0x1825a018, 0x82440095, +0x254289d8, 0x45009106, 0x402d920a, 0x01911024, 0x88100a44, 0xb8812c42, +0xd0026400, 0x80204009, 0x52e000b1, 0x60000b11, 0x00000620, 0x00000000, +0x0027a005, 0x0245009d, 0xa1c049f0, 0x4c019300, 0xc009b002, 0x008b0024, +0x0970065c, 0x9b0126c0, 0xb1026830, 0x0024d0b9, 0x026c0297, 0x14d01938, +0x00000e20, 0x00000000, 0x00258014, 0x027c109f, 0x75c219f0, 0x3c049f00, +0xc1087003, 0x309e0027, 0x19d0227c, 0x975467c2, 0xf0035c00, 0x0027c018, +0x065c009f, 0x53c059f8, 0x00000600, 0x00000000, 0x03050014, 0x004c000f, +0x87c031f0, 0x4c001304, 0xc001f040, 0x00130040, 0x0130000c, 0x332889c0, +0xf0086c00, 0x0004d021, 0x08cc0233, 0x50d003f0, 0x00000420, 0x00000000, +0x005ca014, 0x0144027d, 0x974027d2, 0x45005100, 0x41177009, 0x0271009c, +0x05b001c4, 0x71001540, 0xd0114500, 0x00144197, 0x01440043, 0x504005d0, +0x00000200, 0x00000000, 0x0072a814, 0x030403cd, 0xb3400cd0, 0x0400d100, +0x400cd00b, 0x00c10030, 0x0c100624, 0x5540b140, 0xd0071400, 0x00304228, +0x035400c9, 0x51400dd0, 0x00000a00, 0x00000000, 0x00388005, 0x038402ad, +0x33420ad0, 0x8404e100, 0x60065003, 0x04e10028, 0x061009a4, 0xa9000940, +0xd0039400, 0x0038400e, 0x00940121, 0x154002d0, 0x00000200, 0x00000000, +0x00781015, 0x078d014d, 0x7bc01ef0, 0x8c23c124, 0xd01cf007, 0x05b34068, +0x1e1406ed, 0x474071c2, 0xf0079c01, 0x4058c01c, 0x079d00fb, 0x55c01ef8, +0x00000040, 0x00000000, 0x0015b810, 0x037c801f, 0x37c80df0, 0x7d86df40, +0xc005f01b, 0x099f0027, 0x05f001dc, 0x970006c0, 0xb00b4d00, 0x0017c009, +0x006d001f, 0x42c001f0, 0x00000660, 0x00000000, 0x407fb000, 0x27cc0133, +0x7cc09ef0, 0xdc03f700, 0xc017f02f, 0x0177007c, 0x1f3207dc, 0x73007c80, +0x300fec01, 0x407cc017, 0x07cd01f3, 0x03c01f30, 0x00000e00, 0x00000000, +0x00ad8015, 0x0380006b, 0x3840aaf0, 0x8408e101, 0x4087d043, 0x0061022d, +0x06a040fc, 0xab208850, 0x10038400, 0x00384003, 0x00c40025, 0x57400210, +0x00000620, 0x00000000, 0x00390000, 0x038400a1, 0x38700fd2, 0x9400e500, +0x4102d103, 0x0025000a, 0x8c1003a4, 0x61423840, 0x1003a401, 0x0010400e, +0x0b8418e1, 0x03400e10, 0x00000400, 0x00000000, 0x00032804, 0x030420d1, +0xb0403c52, 0x0402c104, 0x4030d00f, 0x00050003, 0x24904c34, 0x8900c043, +0x10072403, 0x50104408, 0x2004000d, 0x13400010, 0x00000c20, 0x00000000, +0x0035a815, 0x02450051, 0xf4c12dd0, 0x5c00f704, 0xc015f20b, 0x0c370036, +0x2d300e6c, 0x536274c0, 0x34466d2b, 0x0834c009, 0x070d00d3, 0x57c00d34, +0x00000620, 0x00000000, 0x00b70001, 0x037c801f, 0xb7c00df0, 0x7c10df00, +0xc045f203, 0x001b0005, 0x05f00a5c, 0x970287c0, 0xf1035c08, 0x0037c409, +0x007c0017, 0x07c001f0, 0x00000c00, 0x00000000, 0x006f0880, 0x0acc0053, +0x3ec13ef0, 0xcd20db00, 0x8032b043, 0x00234028, 0x0e3c02cc, 0x714030c0, +0xf0076c00, 0x001bc00b, 0x03cc40c3, 0x00c00f30, 0x00000c22, 0x00000000, +0x20062081, 0x03541011, 0x90401dd0, 0x4400d100, 0x4031100b, 0x0e110404, +0x07540614, 0xb1208c40, 0xd0074400, 0x001f4419, 0x00c5003b, 0x04400214, +0x00000802, 0x00000000, 0x0210a001, 0x02440011, 0xb6400dd0, 0x0500c900, +0x6005900b, 0x00150035, 0x0d104644, 0x45103550, 0xd0136400, 0x00374811, +0x034400d1, 0x04400d50, 0x00000200, 0x00000000, 0x00002010, 0x03140041, +0x31400cd0, 0x0500c100, 0x40041203, 0x00050001, 0x04500214, 0x81000140, +0xd0030600, 0x40334400, 0x00440009, 0x40400110, 0x00000080, 0x00000000, +0x0006a000, 0x024c8013, 0x36c005f8, 0x4c20fb00, 0xd001b003, 0x00170005, +0x0d10024c, 0x470015c0, 0xf0036c80, 0x4017c009, 0x034c00d3, 0x00d00d30, +0x00000ac0, 0x00000000, 0x800fa805, 0x03fe207f, 0x3ac00ff0, 0xfc00ff00, +0xc003f001, 0x002b000e, 0x07f002fc, 0xbf002ec0, 0xf003f820, 0x001fc00b, +0x00fc003f, 0x17c003f0, 0x00000e60, 0x00000000, 0x003fa003, 0x03dc4073, +0x0dc8c370, 0xcc183d03, 0xc982b040, 0x0033030c, 0x0f3003ec, 0xfd002ec9, +0x3083ec98, 0x023cd003, 0x23cc00f3, 0x0cc00ff9, 0x00000e00, 0x00000000, +0x803f0801, 0x03c400d1, 0x2440c910, 0x64069d03, 0x4061101b, 0x46911384, +0x2f0103c4, 0xf1002440, 0x100bf406, 0x01b44041, 0x030410f1, 0x04404dd0, +0x00000c20, 0x00000000, 0x4033a011, 0x032480c5, 0x10404050, 0x24000d01, +0x40409040, 0x12492101, 0x0c105326, 0xc9002240, 0x548a3404, 0x30b24100, +0x130404c1, 0x44415cd2, 0x00000e80, 0x00000000, 0x7035a807, 0x036400c1, +0x345a0910, 0x64009d04, 0x41040000, 0x10910000, 0x0d008306, 0xd1042440, +0x50227400, 0x02164108, 0x934480d5, 0x0c401dd2, 0x00000620, 0x00000000, +0x0037a800, 0x036106d5, 0xc4803570, 0x4d0a5e00, 0xd029b000, 0x801340b5, +0x0c30036c, 0xdb00a6c8, 0x70033400, 0x40768835, 0x034d00d1, 0x00c00cd0, +0x00000e20, 0x00000000, 0x083c8083, 0x039c00ff, 0x8fc092f0, 0x9c00ef20, +0xc24bf013, 0x00ff10bf, 0x0ff703fc, 0xff012bc4, 0xb003fe00, 0x4055e83f, +0x07fc20fb, 0x1fc00ff2, 0x00000602, 0x00000000, 0x40310802, 0x036c02d3, +0x94d025f0, 0x5c421f20, 0xc0217000, 0x024f80a6, 0x0d70035c, 0xd70025c0, +0x70227c20, 0x4130c801, 0x074c04c7, 0x08c40d39, 0x00000420, 0x00000000, +0x03bca013, 0x3bc404d1, 0x9440b110, 0x44009d03, 0x42051000, 0x00dd0024, +0x4f1003dc, 0xf1402440, 0xb0027400, 0x01b44009, 0x074411f1, 0x4ec00db1, +0x00000200, 0x00000000, 0x0032a007, 0x0b3400c1, 0xc34010d0, 0x14400d00, +0x4004d080, 0x000d0000, 0x7c500316, 0xc5811140, 0x40023400, 0x00b14000, +0x030402c5, 0x1c400c90, 0x00000a00, 0x00000000, 0x02788004, 0x071401c1, +0x7b421e10, 0x94010d01, 0x00165807, 0x012d0048, 0x1c100714, 0xe1215044, +0x92063401, 0x0269501b, 0x878509f5, 0x1a081f90, 0x00000200, 0x00000000, +0x00341012, 0x033c00c3, 0x034161f2, 0x5c004f00, 0xe0087013, 0xa0cd0832, +0x0c71031c, 0xc50011c0, 0x72033400, 0x2231c000, 0x030c08c7, 0x48c00cb0, +0x00000040, 0x00000000, 0x003db002, 0x43ce80ff, 0x34e80ff8, 0xec007f01, +0xc08db003, 0x00bf003f, 0x8fb82bfc, 0xff011fc0, 0xf007fc00, 0x020ec00a, +0x43fd08eb, 0x0bc18ef0, 0x00000660, 0x00000000, 0x0137a015, 0x1b7c12db, +0x24c01d34, 0x7c00d300, 0xc00da800, 0x00130016, 0xccf0037c, 0xd40024c3, +0x301a4d05, 0x20340805, 0x1b6c06db, 0x54c12df0, 0x00000e00, 0x00000000, +0x02398812, 0x23b400e1, 0x3c600c10, 0xb420eb08, 0x400e1003, 0x00601010, +0x0ed003b0, 0xe1402840, 0x5022840e, 0x0001400e, 0x338412f1, 0x48404ed0, +0x00000620, 0x00000000, 0x02710083, 0x17b401e1, 0x68401e12, 0xb401e900, +0x40181007, 0x81e81078, 0x1fd227b6, 0xed807a42, 0x9007a481, 0x287a4014, +0x07a425e9, 0x0c405ed0, 0x00000402, 0x00000000, 0x40332832, 0x0370e0d9, +0xb0406c11, 0x3001d100, 0x080c1003, 0x01d10074, 0x0cd00336, 0xc1003650, +0x50030600, 0x802340bd, 0x0f0400c1, 0x48400dd0, 0x00000c20, 0x00000000, +0x0015a817, 0x0170025b, 0x1cd04730, 0xf4877940, 0x90271401, 0x057b01de, +0x06d0017c, 0x5f019ec0, 0x30016c00, 0x009ee6a7, 0x04ec606b, 0x5cd403f0, +0x00000620, 0x00000000, 0x40070012, 0x087c1217, 0x07c401f3, 0x7c101f01, +0xc9007000, 0x001f4407, 0x01f0007c, 0x132485c0, 0xf0007c00, 0x0405e001, +0x287c021f, 0x4bc011f0, 0x00000c00, 0x00000000, 0x00270810, 0x164c05db, +0x23e03930, 0x4c009301, 0xc0093402, 0x00930024, 0x59f2026c, 0x9f0024c8, +0x70023c00, 0x30e25009, 0x404c4193, 0x40d001f0, 0x00000c20, 0x00000000, +0x40a60001, 0x1e440791, 0x27e03910, 0x04009100, 0x40091002, 0x00918024, +0x39d0024c, 0x9b202440, 0xb0127400, 0x00e44009, 0x00440091, 0x044001d0, +0x00000800, 0x00000000, 0xc420a018, 0x02640091, 0xa74a0990, 0x4400d1c0, +0x50091003, 0x00910834, 0x09d00244, 0x91802441, 0x50127400, 0x00a46009, +0x41450891, 0x604001d8, 0x00000200, 0x00000000, 0x02202010, 0x22050881, +0x21488c15, 0x061c8182, 0x41c81822, 0x1c810720, 0x89d02207, 0x89852040, +0x90523488, 0x052143c8, 0x22040891, 0x404888d8, 0x00000080, 0x00000000, +0x0586b01d, 0x082c9613, 0x87436130, 0x0d061305, 0xc0611058, 0x06114184, +0x61f0084c, 0x030114d1, 0x70107c16, 0x4106c861, 0x584c1653, 0x74d161f0, +0x00000ac0, 0x00000000, 0x0127b819, 0x1a7e0497, 0x2fc04bf0, 0xfe84bf01, +0xc04bf812, 0x04bf012f, 0x49f01a58, 0x9c052bc4, 0xf002bc04, 0x052ec44f, +0x13fd049f, 0x67c04af0, 0x00000e60, 0x00000000, 0x022fa018, 0x324c0293, +0x2f8d0bb0, 0x7c189f21, 0xc049721a, 0x0c9f2324, 0x2bf05a7e, 0x93022f88, +0x3012ac00, 0x012cc069, 0x36ac04b3, 0x67c65df8, 0x00000e00, 0x00000000, +0x0285089c, 0x08c40211, 0x07654110, 0x74001d05, 0x40e59018, 0x025d0394, +0x71d00874, 0x190a8740, 0x51204404, 0x438442e1, 0x08444401, 0x736040d0, +0x00000c22, 0x00000000, 0x21232010, 0x328486a5, 0xa3480818, 0x14008d08, +0x4148101a, 0x2c8c1020, 0x6cd01236, 0x89012040, 0x14022416, 0x04204048, +0x32241281, 0x434138d0, 0x00000e80, 0x00000000, 0x0025a818, 0x02c402b5, +0x27480912, 0x74009d02, 0x40099082, 0x049d0224, 0x01d80274, 0x99202740, +0x50026440, 0x00344009, 0x12644091, 0x634019d0, 0x00000620, 0x00000000, +0x40252805, 0x024d0097, 0x674009b4, 0x5c409f20, 0xd0193412, 0x029f00a4, +0x01f00274, 0x930026c8, 0x20226c20, 0x0124f009, 0x0a6c0091, 0x174009d0, +0x00000e20, 0x00000000, 0x00210014, 0x027c009b, 0x27c809f0, 0x7c489f00, +0xcc393016, 0x209f0027, 0x01f1027c, 0x970527c0, 0xf0065c00, 0x0027c038, +0x025c009f, 0x53c009f2, 0x00000600, 0x00000000, 0x00050814, 0x007c0413, +0x07c801f0, 0x3c001f01, 0xc001f008, 0x0213c087, 0x1172004d, 0x038205c2, +0xb4047800, 0x0885c021, 0x005c0007, 0x50c00172, 0x00000420, 0x00000000, +0x00142014, 0x01740251, 0xdf402710, 0xf4005d00, 0x4007d001, 0x005b8017, +0x0310014c, 0x51c09d40, 0xb44d8400, 0x029c4005, 0x01c40071, 0x50500730, +0x00000200, 0x00000000, 0x0022a014, 0x037400d1, 0x73412c58, 0x3600cd00, +0x4c0dc803, 0x40c11033, 0x0cd40304, 0xc1803560, 0xd0001400, 0x0035400c, +0x031600c5, 0xd0440c50, 0x00000a00, 0x00000000, 0x01188004, 0x02b400a1, +0x1b400e10, 0xb440ed14, 0x400ed213, 0x01c10173, 0x1e900704, 0xc1005140, +0xd0018404, 0x00304c4f, 0x07c400e1, 0x14405e10, 0x00000200, 0x00000000, +0x00e81014, 0x073c01e3, 0x7b401e50, 0xbc89ef00, 0xc81ef037, 0x17e1017b, +0x5ef0478c, 0xe10839c0, 0xf0079c53, 0x4049c75e, 0x079c41e7, 0x54d05e70, +0x00000040, 0x00000000, 0x4031b010, 0x1b7c00df, 0x07c00170, 0x7c04df00, +0xc02df003, 0x00df05b7, 0xac70137c, 0xdf001641, 0xb0035c04, 0x1017c02c, +0x037c001f, 0x43c18d70, 0x00000660, 0x00000000, 0x006fa002, 0x0fce8173, +0x6fc01730, 0xec91f300, 0x813fb047, 0x01f7147c, 0x1f3047fc, 0xff206cc8, +0x3005cda1, 0x911ec11f, 0x07cc01e3, 0x08c01f30, 0x00000e00, 0x00000000, +0x00198011, 0x92860021, 0x39c20600, 0xb400e102, 0x500ed003, 0x00ec0838, +0x0e1007f4, 0xfd000852, 0x50018400, 0x003c400e, 0xcbc404e3, 0x54400e51, +0x00000620, 0x00000000, 0x00210000, 0x83a400e1, 0x0b410e90, 0xa400e100, +0x400e9063, 0x08ed0038, 0x0e1033b4, 0xed022a40, 0x10019400, 0x042e408e, +0x23a400f9, 0x40410f10, 0x00000400, 0x00000000, 0x00332806, 0x032500c1, +0xc1403010, 0x3402c002, 0x401cd203, 0x0bc90030, 0x0d100334, 0xc9200240, +0x54010400, 0x1034400c, 0x0f240005, 0x18403c50, 0x00000c20, 0x00000000, +0x103da815, 0x016402d1, 0xb7c019b4, 0xe40af242, 0xc00f9023, 0x01ff003c, +0x0d3003fc, 0xff0036c0, 0x38815400, 0x0836c00f, 0x0f6c00d9, 0x74500f30, +0x00000620, 0x00000000, 0x40230201, 0x035c00df, 0x11c00d70, 0x7c00df00, +0xc08df103, 0x10df0037, 0x0df0033e, 0xdf1095c0, 0xf0017c00, 0x00a7e00d, +0x1a5d00db, 0x07c00df0, 0x00000c00, 0x00000000, 0x023f0a80, 0x23fc15f3, +0x3cc10b70, 0xcc00f300, 0xc80fb103, 0x00fb003f, 0x0d3203ec, 0xff201cc8, +0xb021dc00, 0x142fc70d, 0x05dc00ec, 0x04c10ff0, 0x00000c20, 0x00000000, +0x0c862081, 0x08741291, 0x044491d0, 0x6c80d308, 0x400dd083, 0x80dd0037, +0x0d100344, 0xdd0456c0, 0x101d3440, 0x2067480d, 0x2c442011, 0x04480cd0, +0x00000802, 0x00000000, 0x40368001, 0x097480d1, 0x264000d0, 0x4440c900, +0x400dd003, 0x00c50037, 0x1d100344, 0xdd207440, 0x10017400, 0x0077480d, +0x035400d5, 0x04400dd0, 0x00000200, 0x00000000, 0x40200030, 0x033600c1, +0x304004d0, 0x0600c160, 0x400cd003, 0x00cd0033, 0x1d121315, 0xcd001240, +0x10013440, 0x4023400c, 0x020600c5, 0x40500cd0, 0x00000080, 0x00000000, +0x40360000, 0x033c0043, 0x00d00979, 0xc500fb00, 0xc00ff803, 0x00fb003f, +0x0d3417ec, 0xff203480, 0xb4017e00, 0x2027c00f, 0x015c00d7, 0x04c00ff1, +0x00000ac0, 0x00000000, 0xc00f9825, 0x00fe003f, 0x0fc203f0, 0xf480ff00, +0xc40ef003, 0x00ff003f, 0x0ff0a36c, 0xff001f80, 0xf001b400, 0x002fc00f, +0x00fc0033, 0x17c00ef0, 0x00000e60, 0x00000000, 0x003f8003, 0x13de003f, +0x3ec20f30, 0xcc00fb40, 0xc08bf002, 0x08f3033c, 0x0bf102cc, 0x7f202fc0, +0x7000ec08, 0x401cc003, 0x038c2073, 0x0cd00330, 0x00000e00, 0x00000000, +0x88371801, 0x03442a9f, 0x344c0510, 0x040e9122, 0x408ad003, 0x84d50230, +0x0dd82244, 0x5d003740, 0x10034c44, 0x1214400d, 0x83440041, 0x04502110, +0x00000c20, 0x00000000, 0x0033a211, 0xa234408d, 0x30548880, 0x0450c140, +0x4008d003, 0x04c90136, 0x0cd00304, 0x4d003140, 0x510004c0, 0x08044400, +0x83040909, 0x44402010, 0x00000e80, 0x00000000, 0x2035a003, 0x03660195, +0x34480d94, 0x44c1d902, 0x4408d100, 0x00dd0036, 0x4dd00344, 0x5d003740, +0x10036620, 0x0a145a0d, 0x03200049, 0x0c400110, 0x00000620, 0x00000000, +0x0037a882, 0x0174118d, 0xf4c00db0, 0x4c05da00, 0xc01df002, 0x00db02b6, +0x09d0024d, 0xdf0137c0, 0x70004d00, 0x0054c201, 0x074c805b, 0x08c0b531, +0x00000e20, 0x00000000, 0x043d8007, 0x01d410ff, 0x31c00671, 0xfc802510, +0xc23ff00a, 0x00f7107d, 0x0ff0027c, 0xff003e84, 0xf203dc00, 0x007fc80e, +0x17dd0077, 0x1fc017f0, 0x00000600, 0x00000000, 0x00350802, 0x82ad229f, +0x3cf04b30, 0x4c10d341, 0xc60d3003, 0x00d740b4, 0x0d30024c, 0xcf04b7c0, +0xb0004c04, 0x0004c101, 0x234c1013, 0x08c00530, 0x00000420, 0x00000000, +0x04f4a013, 0x434400dd, 0xb4403d10, 0x6c12d100, 0x440d1001, 0x00f140b4, +0x2d10026c, 0xdda83740, 0x100b0403, 0x02b0431d, 0x07440055, 0x4ec1a732, +0x00000200, 0x00000000, 0x00f22003, 0x4b0400cd, 0x00400c94, 0x0403c502, +0x42081003, 0x00d144a4, 0x4c100204, 0x4d20e240, 0x90c82449, 0x00004020, +0x83140241, 0x1c422990, 0x00000a00, 0x00000000, 0x007a0a04, 0x070401fd, +0x58419490, 0xa409a500, 0x401e1437, 0x41f12248, 0x1e1006a4, 0x6d017b40, +0x1027a509, 0x0448489c, 0x07d60165, 0x12401b10, 0x00000200, 0x00000000, +0x08321012, 0x820e18cf, 0xa04408b0, 0x0c08c701, 0xc08c3013, 0x00d38280, +0x8c34030c, 0x4d0c33c0, 0xb8806400, 0x0004c080, 0x031c0003, 0x48c028b0, +0x00000040, 0x00000000, 0x003da802, 0x03dc60ef, 0x3fc00f70, 0xfc08eb10, +0xc08bf011, 0x00df0207, 0x0ff023fc, 0x6f011fc2, 0xf003dc00, 0x401fc08f, +0x03ed08fb, 0x0bc00af0, 0x00000660, 0x00000000, 0x0077a010, 0x81fe00db, +0x0dc00d70, 0x4c01d340, 0xc04d3003, 0x00d34407, 0x0d7082fc, 0xd30037c0, +0x30004c00, 0x0014d001, 0x034c20d7, 0x54c001f0, 0x00000e00, 0x00000000, +0x00399912, 0x01b400e1, 0x104004b2, 0x8400e140, 0x410a1003, 0x06eb0003, +0x0e1002f4, 0xf1003b40, 0x14038000, 0x0038500e, 0x028400e1, 0x494032d0, +0x00000620, 0x00000000, 0x00790004, 0x063401e9, 0x69401a50, 0xc401e100, +0x401a1007, 0x05e1004b, 0x1e5006b4, 0xe1047b40, 0x54040400, 0x04484010, +0x0784010d, 0x0c4018d0, 0x00000400, 0x00000000, 0x20332016, 0x0b3409c1, +0x30400c51, 0x4412c100, 0x480c1005, 0x00c90083, 0x0c102674, 0xc1007346, +0x50030400, 0x0c70400d, 0x074400c9, 0x494088d0, 0x00000c20, 0x00000000, +0x0017a817, 0x09f4037b, 0x1dc00750, 0xcd847348, 0xc2053401, 0x00530017, +0x0571017c, 0x53409fc0, 0x70014c02, 0x005cc605, 0x054d227f, 0x5cc037f0, +0x00000620, 0x00000000, 0x00058012, 0x407c001f, 0x03c001b0, 0x7ca01f00, +0xc401f220, 0x001f2003, 0x01f0007c, 0x0f0047c0, 0xb000bc10, 0x0007d003, +0x287c0317, 0x4bc101f0, 0x00000c00, 0x00000000, 0x00250810, 0x023c109f, +0x24c00830, 0x70029340, 0xc8090402, 0x05830434, 0x5930824c, 0x930027c0, +0x34067c05, 0x0026c229, 0x064c0093, 0x40800934, 0x00000c20, 0x00000000, +0x38262001, 0x0270209d, 0x24440911, 0x74009100, 0x42091002, 0xa1911024, +0x29500254, 0x910a2340, 0x10064400, 0x50245009, 0x6a440391, 0x04408910, +0x00000800, 0x00000000, 0x0464a01c, 0x0274009d, 0x24540910, 0x74009160, +0x40085202, 0x00910024, 0x09100244, 0x91002740, 0x1022e400, 0x8022400b, +0x02441881, 0x61400818, 0x00000200, 0x00000000, 0x00202814, 0x5234008d, +0x20414810, 0x34148105, 0x51485002, 0x28814230, 0x18500a14, 0x81203740, +0x10228408, 0x0220408e, 0x22050881, 0x41508810, 0x00000080, 0x00000000, +0x0006b01d, 0x107e141f, 0x04c24130, 0x7c040301, 0xc0417028, 0x02130584, +0x0130504c, 0x134007c0, 0x30586c16, 0x0d82c163, 0x084c1613, 0x75c16130, +0x00000ac0, 0x00000000, 0x0023a919, 0x82fc00ff, 0x2fd40bf4, 0xbc14ff30, +0xc54bb007, 0x869f0127, 0x0bf002fc, 0x9f002bc0, 0xf1126c04, 0x012fc249, +0x12fc04bb, 0x66c04bf0, 0x00000e60, 0x00000000, 0x802fa018, 0x027c049f, +0x25c14930, 0x4c00b300, 0xcb2d3022, 0x00b31024, 0x0af0027c, 0xa3002cc2, +0x3002cc00, 0x0526c089, 0x024c409f, 0x60c00970, 0x00000e00, 0x00000000, +0x0007181c, 0x087c021f, 0x0c482110, 0x5d161108, 0x44411000, 0x1f130280, +0x01d00074, 0x111016d0, 0x10000404, 0x01844000, 0x0045001d, 0x7140a2d0, +0x00000c20, 0x00000000, 0x00210012, 0x0eb402ad, 0xeb401a94, 0x04428900, +0x40089212, 0x04890462, 0x08d05a34, 0x81002442, 0x10420430, 0x0428404a, +0x52a416ad, 0x40414a50, 0x00000e80, 0x00000000, 0x00252018, 0x02d40095, +0x2e408b90, 0x14048948, 0x44499082, 0x80910026, 0x49d01274, 0x91002440, +0x18034400, 0x083c400b, 0x12e400bd, 0x61400bd0, 0x00000620, 0x00000000, +0x0027a805, 0x0a74029d, 0xa7c029b0, 0x4c029b00, 0xd009a002, 0x009b4026, +0x18f0067c, 0x9340a440, 0x30024c20, 0x0064c009, 0x066c0d9f, 0x14c12970, +0x00000e20, 0x00000000, 0x00258012, 0x0e78009f, 0x25c01872, 0x5c209748, +0xc0087412, 0x009f0035, 0x09f1067c, 0x9f1227c0, 0xf4023d00, 0x00a5d009, +0x065c108f, 0x53c008f0, 0x00000600, 0x00000000, 0x00450810, 0x00bc423f, +0x08c00338, 0x4c001300, 0xc0013000, 0x00134040, 0x11f0004c, 0x034044c0, +0xb0004c00, 0x0306c000, 0x804c8013, 0x50c061f2, 0x00000420, 0x00000000, +0x009ca014, 0x0174005d, 0x15500510, 0x44847100, 0x40051001, 0x005b0014, +0x67d10144, 0x71109846, 0x142d4604, 0x00944205, 0x2d449051, 0x52c005d2, +0x00000200, 0x00000000, 0x0032a014, 0x033400cd, 0x30500c10, 0x0484c950, +0x400c5403, 0x21c10830, 0x1cd00304, 0xc1083042, 0x942f0580, 0x40f2420c, +0x070403c1, 0x52402cd8, 0x00000a00, 0x00000000, 0x04388801, 0x00b4002d, +0x41500210, 0x84002940, 0x404ed003, 0x03e91038, 0x0ed10384, 0xe1103840, +0x90038408, 0x0058480a, 0xc38600f1, 0x16400ed0, 0x00000200, 0x00000000, +0x00781011, 0x073c01ef, 0x38c01c10, 0xcd01eb00, 0xd03f741f, 0x01f30878, +0x1ef0178d, 0xa30078d0, 0xb00f8405, 0x007ec01e, 0x078d01e3, 0x56d01af0, +0x00000040, 0x00000000, 0x0015a810, 0x007c001f, 0x07d001c4, 0x7c000700, +0xc16d305b, 0x00df00b7, 0x05f0237c, 0xdf0033c0, 0x70037c20, 0x0017c20d, +0x037c00cd, 0x43c029f0, 0x00000660, 0x00000000, 0x005d2000, 0x87cc01ef, +0x7cc21f30, 0x8d21f328, 0xc01fb007, 0x01b300fc, 0x1f3007cc, 0xff005fc0, +0x7507fc01, 0x007ec01f, 0x86cc8177, 0x00c03730, 0x00000e00, 0x00000000, +0x00191815, 0x0084082f, 0x08408208, 0x8400a100, 0x400e9003, 0x00af0238, +0x06b103ac, 0xef003b44, 0x1063840a, 0x021e508a, 0x02d510e1, 0x544006b0, +0x00000620, 0x00000000, 0x00090000, 0x030510ed, 0x3a400c10, 0xc400a102, +0x410c1063, 0x00a10030, 0x0a1003a4, 0xcd043b40, 0x50031400, 0x0038500c, +0x63a4087d, 0x00400310, 0x00000400, 0x00000000, 0x00112004, 0x00040205, +0x02422014, 0x04018100, 0x401c140f, 0x009d0130, 0x10100324, 0xc501f340, +0x10030400, 0x0112400c, 0x06340259, 0x10400090, 0x00000c20, 0x00000000, +0x0025a815, 0x074402dd, 0x76c09d30, 0xcd32c330, 0xd01fb083, 0x00d340b4, +0x551003ec, 0x7d0077c0, 0x700ddc00, 0x00a4c075, 0x856c099f, 0x54d00d34, +0x00000620, 0x00000000, 0x00370001, 0x047c101f, 0x45c011f0, 0x7c101f00, +0xc80df043, 0x00df0037, 0x25f1037c, 0xcf2037c4, 0xf0415c82, 0x02b5d041, +0x015c0097, 0x07c02d70, 0x00000c00, 0x00000000, 0x022f0884, 0x038c00eb, +0x32c00e30, 0xcc00f300, 0xc00fb403, 0x0193003f, 0x4338030c, 0x7f003fc0, +0xb0008e01, 0x00a8c02f, 0x01c908b2, 0x00801e30, 0x00000c22, 0x00000000, +0x20122085, 0x00440011, 0x05480154, 0x41001500, 0x420d1003, 0x01b10037, +0x31500344, 0xdd08e640, 0x10004401, 0x00b44131, 0x0144009b, 0x06c01910, +0x00000802, 0x00000000, 0x0014a201, 0x034000d9, 0x36580d11, 0x04a0d110, +0x400d1883, 0x04910833, 0x25100344, 0x550d3748, 0x90934444, 0x00244204, +0x42170055, 0x05406d10, 0x00000200, 0x00000000, 0x00102810, 0x00044001, +0x01400050, 0x04208500, 0x400c1803, 0x00810053, 0x04500305, 0xcd003340, +0x10010400, 0x00004000, 0x021500cd, 0x43400416, 0x00000080, 0x00000000, +0x0006b000, 0x034d00db, 0x36c00d30, 0xcd008320, 0xc00fb003, 0x00d30037, +0x0110034c, 0x5f8037c8, 0xb0034400, 0x0824d00d, 0x015c0017, 0x01c00d30, +0x00000ac0, 0x00000000, 0x001fa805, 0x00fc003f, 0x0fc003f4, 0xfc00bf60, +0xc00ff003, 0x00ff003f, 0x03f003fc, 0xff002ec0, 0xf401fd00, 0x000fc203, +0x00ec002b, 0x16c002f0, 0x00000e60, 0x00000000, 0x003fa003, 0x33dc0cf7, +0x0ce80330, 0xf450f301, 0xc02ff040, 0xb4ef010f, 0x4f3010dc, 0xf3060cc0, +0x3000fc10, 0x013ac003, 0x03844cfb, 0x0cc00f30, 0x00000e00, 0x00000000, +0x203f0801, 0x3a4c0e41, 0x8440a110, 0x7416e180, 0x4a2fd149, 0x82fd2182, +0xef100844, 0xf1098441, 0x10387412, 0x02bc40e1, 0x03440ee1, 0x04402f50, +0x00000c20, 0x00000000, 0x0033a011, 0x030500c5, 0x20414014, 0x3400c107, +0x406cd010, 0x00cd0603, 0x0d100814, 0xc1002040, 0x10c03404, 0x00304104, +0x036400c9, 0x44402d10, 0x00000e80, 0x00000000, 0x08358803, 0x4244a051, +0x25410010, 0x7600d105, 0x400dd000, 0x00dd0017, 0x0d130044, 0xd0000542, +0x10013400, 0x18344904, 0xa36400d1, 0x0c400d50, 0x00000620, 0x00000000, +0x0037a802, 0x2c44a017, 0x14500d30, 0x7c00d040, 0xc00df20a, 0x00df1137, +0x0d10015c, 0xd34094e2, 0x340b7c00, 0x0034d009, 0x036d00db, 0x08d00d30, +0x00000e20, 0x00000000, 0x00358007, 0x42fe082f, 0x5ac09ff1, 0x7c00ff00, +0xc80ff017, 0x00ff303e, 0x0ff4657c, 0xff0096c0, 0xf1007c00, 0x143fe00f, +0x03d800ff, 0x1fc00df2, 0x00000600, 0x00000000, 0x00350802, 0x080c1097, +0xa4c02df1, 0x5c04db10, 0x840d3408, 0x08d30007, 0x4d30036c, 0xd740a4c0, +0x340b7c00, 0x0037c029, 0x035c20c7, 0x08c00df1, 0x00000420, 0x00000000, +0x003ca013, 0x0a4c0091, 0x24400dd2, 0x4423f300, 0x400f1014, 0x02e10817, +0x2e108304, 0xf1000441, 0x1281740a, 0x04bb480d, 0x630400f1, 0x4c507e30, +0x00000200, 0x00000000, 0x00368007, 0x01060285, 0x014000d1, 0x1400c500, +0x440c501c, 0x10c10003, 0x1c100224, 0xc1000440, 0x10003400, 0x00724000, +0x0f1400c5, 0x9c404c50, 0x00000a00, 0x00000000, 0x00788004, 0x45a51161, +0x49541cd0, 0xa409e500, 0x461e1105, 0x21e1004b, 0x1f100684, 0xe1106840, +0x1006b401, 0x017b4012, 0x07842de1, 0x10405e12, 0x00000200, 0x00000000, +0x00301012, 0x010e0487, 0x15c000f0, 0x1c10c740, 0xc00c300b, 0x00c34023, +0x0c34612c, 0xc100b0c0, 0x30083c00, 0x0032c08c, 0x031c04c7, 0x48c00c70, +0x00000040, 0x00000000, 0x043db802, 0x21dc007d, 0x1ec00ff0, 0xdc00fa02, +0xc48ff003, 0x00fd103f, 0x0ef001fc, 0xfb003fc1, 0xf003fc00, 0x013fc40f, +0x23fe24df, 0x0bc14f70, 0x00000660, 0x00000000, 0x0d37a015, 0x004c0117, +0x37c00134, 0x4d13df08, 0xc18d7002, 0x10de0036, 0x4df0024c, 0xd10814c8, +0xf0037c14, 0x0d33c001, 0x434c12d3, 0x54c10d72, 0x00000e00, 0x00000000, +0x01398812, 0x03c40071, 0x3b400e10, 0x8406fd00, 0x400ed003, 0x80e9003b, +0x4fd00284, 0xe1003041, 0xd002b482, 0x003b4006, 0x939402f1, 0x48404ed0, +0x00000620, 0x00000000, 0x00710003, 0x048401a5, 0x7b401210, 0x8401ed00, +0x405ed007, 0x09cd206b, 0x5ed00786, 0xe1007840, 0xd007b445, 0xc27f403a, +0x278405e1, 0x0e401ed0, 0x00000400, 0x00000000, 0x10332812, 0x070521c1, +0x73401d10, 0x0420cd02, 0x400dd00b, 0x00c900f7, 0x0cd00f04, 0xd1023050, +0xd0077400, 0x0033401d, 0x171400c0, 0x4a420cd0, 0x00000c20, 0x00000000, +0x0015a837, 0x1dcc0777, 0x9fc07730, 0xcc005f00, 0xc4057001, 0x005f015f, +0x07f015cc, 0x53005cc0, 0xf01dfc00, 0x001fc037, 0x488c0061, 0x5ed006f8, +0x00000620, 0x00000000, 0x00070012, 0x407c101f, 0x07c101f0, 0x7c801f04, +0xc001f000, 0x000b0407, 0x01f0405d, 0x1f4007c0, 0xf0407c00, 0x0007c101, +0x007c001f, 0x49c001f0, 0x00000c00, 0x00000000, 0x00270810, 0x8e3c409b, +0x24c00930, 0x7e009f10, 0xc2093042, 0x00930024, 0x39d0024c, 0x9b0024c0, +0x70024c05, 0x2020c009, 0x004c0093, 0x40c009f2, 0x00000c20, 0x00000000, +0x00262001, 0x0e740091, 0x24480910, 0x74829d00, 0x4009100a, 0x00910024, +0x39d28264, 0x910026c0, 0x10024503, 0x10a44009, 0x046c8091, 0x04400910, +0x00000800, 0x00000000, 0x0020a018, 0x42740099, 0x24440910, 0x34029d00, +0x4009100a, 0x00910024, 0x08d00365, 0x91002051, 0x108244c2, 0x41244009, +0x19450099, 0x60601950, 0x00000200, 0x00000000, 0x00a02010, 0x0a340281, +0x2141c810, 0x34808d17, 0x50881052, 0x00818720, 0x08d05224, 0x81672040, +0x18720400, 0x022051c8, 0x22240891, 0x40489810, 0x00000080, 0x00000000, +0x0006b01d, 0x007cc01b, 0x84d06130, 0x74141f11, 0xc1613410, 0x14134184, +0x41f0104c, 0x13018441, 0x34184c14, 0x9594c061, 0x584c5613, 0x74d16172, +0x00000ac0, 0x00000000, 0x00a7b819, 0x8afc62bf, 0x2ec84bf4, 0xf4149f01, +0xc0497152, 0x009f012f, 0x49f002dc, 0x9f092fc3, 0xf012fc14, 0x2127c84b, +0x12dd849f, 0x67c04870, 0x00000e60, 0x00000000, 0x01a7a018, 0x4afc06b3, +0x27c029f0, 0xdc10bf85, 0xc1293042, 0x04932127, 0x2bf0123c, 0xbf08270e, +0x30424c86, 0x01afc009, 0x168c109b, 0x60c12bf0, 0x00000e00, 0x00000000, +0x0107081c, 0x10342659, 0x8344a1d8, 0x55001d11, 0x40605408, 0x42110597, +0x019100e6, 0x19038640, 0x12004504, 0x004740a5, 0x544c0217, 0x704071d0, +0x00000c20, 0x00000000, 0x0322a010, 0x02340889, 0x230048d0, 0x04108d04, +0x40485052, 0x08a90223, 0x28c00ab4, 0x8d042340, 0x90420402, 0x91b74108, +0x0a243481, 0x40404cd0, 0x00000e80, 0x00000000, 0x0025a818, 0x02748099, +0x274009d0, 0x44209900, 0x4409102a, 0xa0990227, 0x09d046f4, 0x9d01a740, +0x94064400, 0x20074049, 0x50440095, 0x604801d0, 0x00000620, 0x00000000, +0x40278805, 0x027c0293, 0x274809d2, 0x5c209d01, 0xc0097002, 0x00994027, +0x09f88274, 0x9f0827e0, 0xb0024400, 0x0007c029, 0x00640093, 0x14d001d0, +0x00000e20, 0x00000000, 0x00258014, 0x027c0097, 0x67c209f0, 0x7c009f02, +0xc009f042, 0x00971023, 0x09b0026c, 0x9b1066c0, 0x70027c00, 0x8007c009, +0x007d009f, 0x53c001f0, 0x00000600, 0x00000000, 0x20010814, 0x907c0212, +0x87a00130, 0x7c400300, 0xc001f288, 0x00330004, 0x813108fc, 0x0f1007c0, +0xf0003c00, 0x0044d021, 0x88440013, 0x50c01072, 0x00000420, 0x00000000, +0x0014a014, 0x89f43071, 0x17e2051d, 0xf6007100, 0x4005d00d, 0x40530014, +0x1710015c, 0x7d0017c0, 0xd0817401, 0x000c4005, 0x08ec0051, 0x50400330, +0x00000208, 0x00000000, 0x8032a014, 0x833412c1, 0x33420c50, 0x7409c908, +0x400cc80f, 0x00c50030, 0x0c140334, 0xcd003340, 0xd8033401, 0x0034620c, +0x170400d9, 0x50400c51, 0x00000a00, 0x00000000, 0x00308005, 0x04b40021, +0x79444e52, 0xb480e111, 0x481ed043, 0x802d2338, 0x0e9004b4, 0x6d003908, +0xd003b410, 0x013040dc, 0x77e580c9, 0x14404e90, 0x00000200, 0x00000000, +0x01781015, 0x07bc01e3, 0x7b409e50, 0xb441eb45, 0xd11ef005, 0x81e74570, +0x163007bc, 0xad127b40, 0xf027bc01, 0x4378c05e, 0x278d01eb, 0x54d03c70, +0x00000040, 0x00000000, 0x1135b810, 0x0274001f, 0x37808da1, 0x7c005f00, +0xc0adf001, 0x00130837, 0x0d70005c, 0x1f02b7c0, 0xf0837c00, 0x13b7c10d, +0x1b5c5ed7, 0x43c16d70, 0x00000660, 0x00000000, 0x04bfa000, 0x85fe09bf, +0x7fc2cff0, 0xe981ff24, 0xc81ff007, 0x01ff107d, 0x1f7007fc, 0xfb087cc8, +0xe067cc21, 0x007cc01f, 0x07c409f9, 0x00c01ff0, 0x00000e00, 0x00000000, +0x02398815, 0x109c00ad, 0x3b440ec0, 0x8400ad10, 0x400ec000, 0x00292038, +0x0e1000b4, 0x65003c10, 0xd0238482, 0x0038424e, 0x5b868ce1, 0x54408ed2, +0x00000620, 0x00000000, 0x02390000, 0x01b600ad, 0x3b608ed1, 0x8400e900, +0x400ed043, 0x00ed0438, 0x06500324, 0xa1003c40, 0xda0b8400, 0x4232450c, +0x038400e1, 0x02400ed0, 0x00000400, 0x00000000, 0x00332804, 0x1a10018d, +0x33402cc0, 0x00000d80, 0x480cd000, 0x400d9830, 0x08100034, 0x05043042, +0xd1030400, 0x0032401c, 0x0f4500c1, 0x12440cd0, 0x00000c20, 0x00000000, +0x003da815, 0x0a74005f, 0x3fc0afd0, 0x4c001b00, 0xd00ff006, 0x00df007c, +0x0d70036c, 0xd1003c80, 0xc003cd00, 0x4032e01f, 0x8f4c00f3, 0x56900cf0, +0x00000620, 0x00000000, 0x20370001, 0x205c180f, 0x33c41df0, 0x1c00df00, +0xc00cf002, 0x001b0037, 0x04f0007c, 0xd70836c0, 0xf2077c00, 0x0035c00d, +0x1b6c00cf, 0x05c00df0, 0x00000c00, 0x00000000, 0x803f0880, 0x03ec097f, +0x3ec00ef0, 0xfc251330, 0xc00d3020, 0x40ff003c, 0x45f003fc, 0x5f003cc0, +0xf003cc02, 0x203fc00f, 0x07cc00f3, 0x00c00d30, 0x00000c22, 0x00000000, +0x00362081, 0x2744531d, 0x34600dd0, 0x74025108, 0x440db000, 0x20170034, +0x15d00074, 0x5f003440, 0xd0034403, 0x0035c00d, 0x034400d1, 0x04400db0, +0x00000802, 0x00000000, 0x0034a001, 0x0064005d, 0x34400dd2, 0x74021150, +0x400d5803, 0x00dd0035, 0x0dd00374, 0x8d003441, 0xd0035480, 0x0077400d, +0x234500d9, 0x04601d10, 0x00000200, 0x00000000, 0x00302010, 0x800480cd, +0x32400cd1, 0x36008180, 0x500c1400, 0x00050031, 0x04c00034, 0x85003050, +0xd0031000, 0x0075000c, 0x034400c0, 0x40501d90, 0x00000080, 0x00000000, +0x003eb000, 0x016e005d, 0x3cd00fd0, 0x7c601300, 0xc00f1003, 0x40df203d, +0x05f00374, 0x1d003cc4, 0xf003dc00, 0x0037c00f, 0x034c00eb, 0x00d00d30, +0x00000ac0, 0x00000000, 0x003fb805, 0x03fc00ff, 0x3dc00ff8, 0xfc403f00, +0xc00ff000, 0x0036003e, 0x07f000fc, 0x3f803fc4, 0xf003ec00, 0x503fc00f, +0x03fc00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x102fa203, 0x83bc0873, +0x1fc14f30, 0xce003328, 0xc06b3088, 0x02f70a3f, 0x0ff003cc, 0xf7080fc6, +0x7080ee04, 0x081fc02f, 0x03cc003f, 0x0cc043f0, 0x00000e00, 0x00000000, +0x06b70801, 0x03f42081, 0xaf482d14, 0x44520100, 0x406bb000, 0x24f1003f, +0x01d003c5, 0xd9422300, 0xb2007000, 0x0837424d, 0xc344401d, 0x0440a5d0, +0x00000c20, 0x00000000, 0x2123a011, 0x03344481, 0x93400c10, 0x040c0100, +0x4028d013, 0x04c51133, 0x1cd00304, 0xcd604340, 0x18d20402, 0x2013404c, +0x8334005d, 0x455014d0, 0x00000e80, 0x00000000, 0x0135a803, 0x03761091, +0x67601d11, 0x44205508, 0x4009d083, 0x00d10037, 0x11d18344, 0xdd206744, +0x90023400, 0x0017624d, 0x0160625c, 0x0c4005d0, 0x00000620, 0x00000000, +0x0027a802, 0x037c40d3, 0x53c01d30, 0xc5011301, 0xc089f0c3, 0x00d72037, +0x05f0034c, 0xd70847c0, 0x7006ec00, 0x2017812d, 0x0f7c005f, 0x09c48cf0, +0x00000e20, 0x00000000, 0x40398007, 0x83f403ff, 0x2fc805f0, 0x3c453b00, +0xc009b00f, 0x00ff0037, 0x0bf003f4, 0xf1002fc2, 0xf00f7800, 0x801e821f, +0x8bdd007f, 0x1fc20fe0, 0x00000600, 0x00000000, 0x00250802, 0x034c81df, +0x14d00d34, 0x4c014b00, 0xd0093083, 0x00d3c030, 0x0db0037c, 0xdb4007c0, +0xb0834c00, 0x0094c01d, 0x0f4c0a53, 0x08c10df0, 0x00000420, 0x00000000, +0x0034a013, 0x1b80019d, 0x24400d10, 0xc4005000, 0x01094003, 0x00f1003c, +0x1b5003fc, 0xf1002704, 0xb10fe88a, 0x0044881d, 0x876c6041, 0x4e803fd0, +0x00000200, 0x00000000, 0x0022a007, 0x932500cd, 0x72498810, 0x04200900, +0x033c1003, 0x00d10031, 0x1c180334, 0xc8101702, 0x9044048a, 0x01d3612c, +0x02140185, 0x1c4294d2, 0x00000a00, 0x00000000, 0x00788004, 0x27a409ad, +0x62401e10, 0x84016120, 0x401e5807, 0x21e10279, 0x1a402794, 0xe1c05b41, +0x91458401, 0x4459401f, 0x0fb50925, 0x124016d1, 0x00000200, 0x00000000, +0x00301012, 0x032c00cf, 0x12402832, 0x0c000b04, 0x42083083, 0x00c12031, +0x8cb00374, 0xcb400340, 0xb0010424, 0x0093c00c, 0x0b1c4057, 0x48c08cf0, +0x00000040, 0x00000000, 0x003db802, 0x0bdc00bf, 0x2dc08ff0, 0xfd407f80, +0xc80bf003, 0x00df0c36, 0x0bf04bfc, 0xee023fc0, 0xe003fd00, 0x221ad80e, +0x03ec007b, 0x0bc20ef0, 0x00000660, 0x00000000, 0x0827a015, 0x072d80cf, +0xb4c20b32, 0x4c035384, 0xc0593203, 0x38d32134, 0x05300b4d, 0xd7000fc0, +0xb0007c00, 0x2014c05d, 0x066c005f, 0x57c00d30, 0x00000e00, 0x00000000, +0x00398812, 0x1b8400ed, 0x39080e15, 0xc6306110, 0x408b1003, 0x04f10338, +0x4e1013fc, 0xe9003f08, 0xb503dc04, 0x801ac54f, 0x03ac007d, 0x4b406e32, +0x00000620, 0x00000000, 0x00790003, 0x97e401fd, 0x78001812, 0xa4416188, +0x401e14c3, 0x05e14870, 0xbe100384, 0xed084b0a, 0x10059409, 0x805840de, +0x0796016d, 0x0f401e94, 0x00000400, 0x00000000, 0x08332812, 0x030608cd, +0x30400c12, 0x24414148, 0x581c110f, 0x00d18030, 0x2c100374, 0xc9003744, +0x94071400, 0x2882433c, 0x0324014d, 0x4b401c10, 0x00000c20, 0x00000000, +0x0015a817, 0x01ec007f, 0xdcc02630, 0xac455324, 0xc055300d, 0x00530014, +0x3730014c, 0x57411fc0, 0x300ddc80, 0x805ce635, 0x09dc097f, 0x5fc437b0, +0x00000620, 0x00000000, 0x10070012, 0x007c921f, 0x87d001f1, 0x5d001f04, +0xc201f000, 0x801f1007, 0x01f0007c, 0x170047c4, 0xf02c7c00, 0x1207e081, +0x407d081f, 0x4bc091f1, 0x00000c00, 0x00000000, 0x00270810, 0x024d009f, +0x64c109f8, 0x7c01d300, 0xc0093042, 0x00930027, 0x49b0026c, 0x930027c1, +0x34227c00, 0x14274005, 0x024c019f, 0x43c059f0, 0x00000c20, 0x00000000, +0x00262001, 0x0264228d, 0xe05009d9, 0x74219111, 0x40291082, 0x809b0027, +0x29100255, 0x91502740, 0x100e7402, 0x00274001, 0x064c018d, 0x074019d0, +0x00000800, 0x00000000, 0x0024a018, 0x0264809d, 0x244009d0, 0x76049112, +0x41891002, 0x00912023, 0x09900264, 0x91c02740, 0x18033490, 0x022540a1, +0x06402895, 0x634009d8, 0x00000200, 0x00000000, 0x02202010, 0x0224089d, +0x205149d0, 0x76158105, 0x41481782, 0x08890123, 0x18102215, 0x81c22340, +0x10233400, 0x00234088, 0x2624288d, 0x436008d9, 0x00000080, 0x00000000, +0x0586b01d, 0x004e025f, 0x04d441d0, 0x7c040301, 0xc0413278, 0x021302c7, +0x41b0586c, 0x13058741, 0x30587e34, 0x2017c169, 0x084d165f, 0x77c141f2, +0x00000ac0, 0x00000000, 0x012fb819, 0x0a5e04be, 0x2bd00bf2, 0xfc149f40, +0xc34be006, 0x269f0267, 0x0bf0127c, 0x9f512fc4, 0xd012fc00, 0x003ec449, +0x129c04bf, 0x67c20ff0, 0x00000e60, 0x00000000, 0x0127a018, 0x424d22bf, +0x2cc109b0, 0x0c909304, 0xc90b3036, 0x149301a7, 0x4b300adc, 0xb30326c0, +0x3056ce04, 0x002ec01a, 0x02cc00bf, 0x63c04b30, 0x00000e00, 0x00000000, +0x0107081c, 0x0044841d, 0x04520310, 0x44001b20, 0xc0011100, 0x02111107, +0x21109078, 0x11008440, 0x10014c42, 0x2007c001, 0x8054951d, 0x734021b2, +0x00000c20, 0x00000000, 0x04a3a010, 0x0284048d, 0x20400a12, 0x3510c100, +0x4908d032, 0x208120a3, 0x28101a11, 0x8901a040, 0x10422442, 0x00204418, +0x0614018d, 0x43408810, 0x00000e80, 0x00000000, 0x0825a818, 0x0244409d, +0x24450b10, 0x24848900, 0x4819d002, 0x20910027, 0x29120274, 0xd940a450, +0x14020580, 0x00274019, 0x0654019d, 0x63404990, 0x00000620, 0x00000000, +0x0227a805, 0x824c059f, 0x24c00930, 0x6c009301, 0xc809f016, 0x00930027, +0x0834025c, 0x8b02e4c2, 0x32026400, 0x00a4c009, 0x025c239f, 0x17c20930, +0x00000e20, 0x00000000, 0x00658014, 0x027c049f, 0x27c40870, 0x5c00df34, +0xc0093526, 0x009f4025, 0x09f00265, 0x970065c0, 0xf0527c40, 0x0227c009, +0x0274009f, 0x53c009f0, 0x00000600, 0x00000000, 0x00050814, 0x00bc0813, +0x00d023f2, 0x7c001300, 0xc0113400, 0x00131003, 0x31f0000c, 0x134004d0, +0x30007c00, 0x0484c011, 0x244c0113, 0x50c021f0, 0x00000420, 0x00000000, +0x0014a014, 0x01744071, 0x9c4405d0, 0x74005100, 0xc0171201, 0x00513815, +0x27d0056c, 0x75001440, 0x5001dc02, 0x081ec427, 0x8dc5037b, 0x504007d0, +0x00000200, 0x00000000, 0x0032a014, 0x033410d1, 0x10480cd0, 0x3400c120, +0x400c0003, 0x00d54833, 0x0cd00704, 0x45003440, 0x100c3484, 0x02304281, +0x034000d1, 0x50400cd0, 0x00000a00, 0x00000000, 0x01788005, 0x04b400e1, +0x104002d0, 0xf400a100, 0x402a1023, 0x08e50031, 0x2fd84394, 0xa5033104, +0x5043f400, 0x004e04c2, 0x938442c9, 0x144014d0, 0x00000200, 0x00000000, +0x417c1015, 0x03b401e3, 0x78401ef0, 0xbc016300, 0x40123037, 0x01f7007b, +0x16d00785, 0x670338c0, 0x3007bc81, 0x0048c056, 0x8d8c4123, 0x54c41af0, +0x00000040, 0x00000000, 0x0275b810, 0x007c00df, 0x37c000f0, 0x7c661f40, +0xc005d20b, 0x01db10b5, 0x04f0036c, 0x9f0136c0, 0xb5011e00, 0x2003c411, +0x877c001f, 0x43c005f2, 0x00000660, 0x00000000, 0x007fa000, 0x87fc01ff, +0x7cc01f70, 0xcc03f700, 0xc897f027, 0x0df70cfc, 0x13b006cc, 0xf7607fc0, +0x3004dc29, 0x024cc8d3, 0x07cc0d73, 0x00c48e30, 0x00000e00, 0x00000000, +0x02398815, 0x00b600ad, 0x380c4212, 0xac00a146, 0x500ad033, 0x08e1023d, +0x021002bc, 0xa1103b40, 0x1002d400, 0x0b09c4c3, 0x0ac48c71, 0x5440c6b0, +0x00000620, 0x00000000, 0x00390000, 0x03b4086d, 0x38050e50, 0x84006500, +0x000cd003, 0x04cd0038, 0x86100280, 0xed443b40, 0x5043b020, 0x008e40e2, +0x21960ca1, 0x01448b10, 0x00000400, 0x00000000, 0x00772804, 0x0036800d, +0xf0407010, 0x24200100, 0x4000d003, 0x00c92030, 0x40100224, 0x8940b340, +0x5a0d0480, 0x00014001, 0x06544281, 0x10401490, 0x00000c20, 0x00000000, +0x013da815, 0x037c11df, 0xb0d01d70, 0xcc015700, 0xc001f003, 0x00ff003c, +0x3134024c, 0x9f00bfe1, 0x70045e40, 0x01c6c003, 0x8bdc28a3, 0x55d04534, +0x00000620, 0x00000000, 0x20370001, 0x007c44df, 0xa7c001f2, 0x7c885f40, +0xc00df203, 0x00d70033, 0x2170023c, 0xd7083740, 0xa4017e00, 0x4087c011, +0x6b6900df, 0x078205f2, 0x00000c00, 0x00000000, 0x003f0880, 0x037c01f3, +0x3cc00ef2, 0xce003306, 0xc00f3003, 0x00e3003d, 0x5630024c, 0x83143cc0, +0x32c18c02, 0x080c4803, 0x00cc003f, 0x03c00330, 0x00000c22, 0x00000000, +0x08362081, 0x007403d1, 0x344001d0, 0x44181100, 0x40015083, 0x00d50034, +0x31102650, 0xd1603781, 0x50015483, 0x0007c029, 0x8a402939, 0x074125b0, +0x00000802, 0x00000000, 0x0034a001, 0x83740a11, 0x34500dd0, 0x10000104, +0x411c1203, 0x20d10036, 0x01138344, 0x91503142, 0x10084480, 0x21854031, +0x0544005d, 0x07400510, 0x00000200, 0x00000000, 0x00302010, 0x00340001, +0x204000d2, 0x04200100, 0x40085003, 0x00c50032, 0x001a0314, 0x41803340, +0x50021420, 0x08034000, 0x80054049, 0x43400492, 0x00000088, 0x00000000, +0x0036b000, 0x037c0013, 0x34c00df0, 0xd4800340, 0xc0053083, 0x00d3003c, +0x05340344, 0x910039c0, 0x30034e80, 0x0005c401, 0x004c009f, 0x03c00132, +0x00000ac0, 0x00000000, 0x003fb805, 0x00fc003f, 0x3bd002f0, 0xfd203b00, +0xc403c003, 0x00ff003d, 0x03e203fc, 0xff003dc0, 0xf001fc00, 0x000bc003, +0x00fc00bf, 0x17c007f0, 0x00000e60, 0x00000000, 0x003fa003, 0x23fc4cff, +0x3ec043b8, 0xf884f301, 0xc44ff003, 0x2c730b0f, 0x0f3011fc, 0xf3260fc9, +0x3102fc00, 0x023cc48b, 0x038c0c33, 0x0cc08fb0, 0x00000e00, 0x00000000, +0x003b0801, 0x20740ed1, 0xbc41c9b0, 0x84bcf124, 0x49cf1203, 0x06450183, +0x6f104930, 0xf1088741, 0x10387408, 0x423d48ad, 0x0bc40201, 0x1c406f10, +0x00000c20, 0x00000000, 0x0032a011, 0x13140005, 0x30440414, 0x0400c983, +0x420c5203, 0x0c410303, 0x0c901934, 0xc1000140, 0x10c23414, 0x41344848, +0x23040801, 0x4c402d90, 0x00000e80, 0x00000000, 0x08358803, 0x2074b811, +0x3040ad10, 0x0400c900, 0x420d1003, 0x02552087, 0x0d908174, 0xd1201340, +0x10067480, 0x0035404c, 0x03460011, 0x1c4a0d90, 0x00000620, 0x00000000, +0x0037a800, 0x855c2095, 0x34c00110, 0x4d00db40, 0xc40d7203, 0x01d30237, +0x0db0097c, 0xd340b5c2, 0x30077440, 0x0834c009, 0x034d0253, 0x00c00db1, +0x00000e22, 0x00000000, 0x083d8087, 0x84fc0097, 0x3dd80bf0, 0xfc20f720, +0xc10f7003, 0x08ff007f, 0x0d7089fc, 0xff1007c0, 0xf101bc20, 0x0c3fc70b, +0x03fc093f, 0x1fc00f70, 0x00000602, 0x00000000, 0x00310802, 0x030c0017, +0x34c821f8, 0x7c40d322, 0xd00d3103, 0x22d380b0, 0x0d340b0c, 0xd340b4d0, +0xb4037c40, 0x0037c019, 0x034d02d3, 0x28c00d70, 0x00000420, 0x00000000, +0x003ca013, 0x22444011, 0x7c4409d2, 0xf441f340, 0x403f1003, 0x00db8034, +0x1f108344, 0xf3081440, 0xf2037412, 0x00fb4029, 0x33840091, 0x4c400e10, +0x00000200, 0x00000000, 0x0032a003, 0x0a042f45, 0x724001d0, 0x3648c900, +0x481c5003, 0x00052800, 0x0c100204, 0xc9080064, 0x94023411, 0x00f34828, +0x0b260091, 0x1c400c50, 0x00000a00, 0x00000000, 0x08788000, 0x048481e1, +0x7f4010d2, 0xb601e100, 0x411c1303, 0x21650058, 0x1e100784, 0xe1306861, +0xd407f401, 0x047f421e, 0x07a629e1, 0x74401f10, 0x00000200, 0x00000000, +0x00301016, 0x030c0007, 0x32c08cd0, 0x3800cb02, 0xc00c1023, 0x00c54020, +0x0c302b0d, 0xcb008080, 0xb4223c80, 0x0133c008, 0x032c0a83, 0x48c00c70, +0x00000040, 0x00000000, 0x0435b800, 0x017d009f, 0x30c08df0, 0x7810d704, +0xc18df073, 0x00db0037, 0x0df0836c, 0xdf0037c0, 0xf4033c18, 0x00b3c00d, +0x035c28df, 0x0bc0acf0, 0x00000660, 0x00000000, 0x0037a015, 0x00644057, +0xb6c4053a, 0x4d13cf04, 0xc95d304b, 0x005f0006, 0xedf0017c, 0xd71037c0, +0xf0024c91, 0x2034d21c, 0x734c0053, 0x44c00d30, 0x00000e00, 0x00000000, +0x133d8810, 0x80ac00c1, 0x3b400630, 0xc400ed01, 0x4a4e3093, 0x006d0018, +0x4ed001b4, 0xcd002bc3, 0x70038404, 0x00b0400e, 0x83840061, 0x4c404c10, +0x00000624, 0x00000000, 0x017900a3, 0x07040165, 0x7b401e91, 0x8401fd00, +0x445e9303, 0x01ed0068, 0x1e1007b4, 0xed007b40, 0xd006a401, 0x4178401f, +0x070401e1, 0x0440de14, 0x00000402, 0x00000000, 0x20332812, 0x436028c1, +0x33400c94, 0x0400cd08, 0x420c9003, 0x0bcd00f4, 0x0cd00734, 0xcd207740, +0x50072480, 0x0032401c, 0x03040ac1, 0xc84c0d10, 0x00000c20, 0x00000000, +0x0015a816, 0x01cc4177, 0x1fc44730, 0x44207d00, 0xd0053001, 0x017f095c, +0x063045fc, 0x7701dfc0, 0xf015ec80, 0x001cc017, 0x01cc0973, 0x5c500730, +0x00000620, 0x00000000, 0x00070012, 0x007c001f, 0x87c00170, 0x7c821f00, +0xc8217000, 0x101f0407, 0x21f0407c, 0x1f0405c0, 0xf0005d00, 0xa005c081, +0x083d001f, 0x4bd001f0, 0x00000c00, 0x00000000, 0x00270810, 0x024c089f, +0x66c20830, 0x7c009f02, 0xc049a102, 0x00830027, 0x0930020c, 0x930024c0, +0xf0026c20, 0x1027c009, 0x264c0093, 0x41c00934, 0x00000c20, 0x00000000, +0x18262000, 0x0e440091, 0xe4400915, 0x74129d00, 0x42399082, 0x00910827, +0x29120244, 0x911826d1, 0xd0027400, 0x10a74209, 0x1a4c0081, 0x04400931, +0x00000800, 0x00000000, 0x0020a018, 0x2244008d, 0x24400910, 0x34009d04, +0x4109d802, 0x00910027, 0x09900244, 0x94002440, 0xd0036401, 0x00234089, +0x02440099, 0x614009d0, 0x00000200, 0x00000000, 0x10a02010, 0x8a040281, +0x215bc818, 0x34408d00, 0x4208d00a, 0x3c812723, 0x09147205, 0x81072040, +0xd0723400, 0x00234148, 0x22041c89, 0x40408810, 0x00000080, 0x00000000, +0x0004301c, 0x014d001f, 0x04c26130, 0x74141f95, 0xc140b000, 0x06134187, +0x4130184c, 0x11418441, 0xf0186c14, 0x4517c041, 0x584c061b, 0x75c161b0, +0x00000ac0, 0x00000000, 0x08a7b039, 0x0bfc22bf, 0x26c04bf0, 0x7c949f05, +0xc149320a, 0x04bf012f, 0x497092fc, 0x9f112fc1, 0xf012fc34, 0x2527c54b, +0x125d04a7, 0x67c049f0, 0x00000e64, 0x00000000, 0x01a7a218, 0x42cc06bf, +0xafc029f2, 0x7412bf01, 0xc06bf01a, 0x049f0127, 0x2bf0127c, 0xbf0424c1, +0xf01a4c02, 0x04afc04b, 0x5acc269f, 0x63c02914, 0x00000e00, 0x00000000, +0x0186089c, 0x00440651, 0x874421d0, 0x1c461d21, 0x44411098, 0x021d0517, +0x00700074, 0x1d018444, 0xd0014504, 0x01874161, 0x1c44005d, 0x73400010, +0x00000c22, 0x00000000, 0x00a1a010, 0xc205228d, 0xa34068d0, 0x36008920, +0x4468d00a, 0x188d00a3, 0x09905a34, 0x99042054, 0xd01a0406, 0x01214088, +0x1244028d, 0x4b402810, 0x00000e80, 0x00000000, 0x00252818, 0x226468d1, +0x274429d0, 0x74209d00, 0x40091102, 0x429d0027, 0x09500a74, 0xdd082540, +0xd0024440, 0x00276409, 0x0045009d, 0x63400918, 0x00000620, 0x00000000, +0x0027a801, 0x0a4c009f, 0x27c019f0, 0x7c009f00, 0x4009f002, 0x019f10a7, +0x09f0067c, 0x9f2024c0, 0xf0024800, 0x8825e409, 0x0044028f, 0x17c00912, +0x00000c20, 0x00000000, 0x00218014, 0x065c4197, 0x27c049f0, 0x5c109f14, +0xc0097802, 0x099f0027, 0x09f2227c, 0x9f0266c9, 0xf0027c10, 0x1027c109, +0x407c409f, 0x5bc009f4, 0x00000400, 0x00000000, 0x00050814, 0x207c0a17, +0x04c001e0, 0x4c801308, 0xc4015100, 0x00134084, 0x0170006d, 0x1b0804c0, +0x30884d00, 0x0006d001, 0x006d0213, 0x50d201b0, 0x00000420, 0x00000000, +0x1014a014, 0x05f48075, 0x9f4405d0, 0x44027100, 0x40071801, 0x00510014, +0x36d20144, 0x6b0016e0, 0x10014412, 0x081c4027, 0x048c0071, 0x50400510, +0x00000000, 0x00000000, 0x1032a014, 0x033413dd, 0xf0420cd0, 0x6403c100, +0x401c5003, 0x00c19031, 0xbcd00304, 0xc5103040, 0x50033400, 0x0070402c, +0x0f0400c1, 0x50400c10, 0x00000a00, 0x00000000, 0x02788205, 0x01b64025, +0xe3404ed2, 0xa4038000, 0x443c1803, 0x05e10071, 0x0ec01394, 0xe9027e42, +0x5417b400, 0x00f14818, 0x430404c1, 0x10405e10, 0x00000200, 0x00000000, +0x033c1015, 0x84bc41a7, 0x78c37ef0, 0xa501e340, 0xd01a502f, 0x87c30179, +0x1e709fac, 0xb3007c40, 0x710ffc01, 0x4078c01e, 0x178897e3, 0x50c43e30, +0x00000040, 0x00000000, 0x0035b010, 0x007c001f, 0x37c40df2, 0x5c009f00, +0xc005f11b, 0x02df20b6, 0x0df00b6c, 0x9f05b7c0, 0xb0034c00, 0x0014c40d, +0x337c86df, 0x43c06d74, 0x00000660, 0x00000000, 0x007fa002, 0x06fc01f7, +0x7fc09f36, 0xec01f703, 0x021ff007, 0x87ff24ff, 0xdbf23bfe, 0x7f007cc2, +0xf08fcc81, 0x013cc017, 0x3f8c81ff, 0x08c03f30, 0x00000e00, 0x00000000, +0x00388815, 0x209c006b, 0x29e08e30, 0xa448a122, 0x404a9003, 0x40ed013b, +0x8a7103be, 0x2d013848, 0xd0038400, 0x0328400b, 0x238480ed, 0x44400e10, +0x00000620, 0x00000000, 0x00390008, 0x40b400a5, 0x03448e90, 0x04004111, +0x440a5023, 0x04ed083b, 0x409223b4, 0x6d0c3848, 0xd0238408, 0x823a4006, +0x13c400cd, 0x00400e15, 0x00000400, 0x00000000, 0x80332816, 0x0c141241, +0x05402c10, 0x04201100, 0x40011003, 0x09dd1073, 0x01502754, 0x0d203060, +0xd0070600, 0x8006401c, 0x034400dd, 0x18400c18, 0x00000c20, 0x00000000, +0x003da815, 0x0f7c0255, 0x1740afb0, 0xed805740, 0x4005f083, 0x20ffa17f, +0x05d103f4, 0xdd003cd2, 0xf01fc400, 0x0006d11d, 0x034d01fd, 0x54500f34, +0x00000620, 0x00000000, 0x28370009, 0x097c001f, 0x07c80df0, 0x7c005f00, +0xc005f083, 0x00df0037, 0x0171037c, 0x5f0033c0, 0xf0937d02, 0x0035cc21, +0x0b7c08df, 0x07c00df0, 0x00000c00, 0x00000000, 0x10370801, 0x47fc0037, +0x3cc00f70, 0x8c08f300, 0x880b3803, 0x80ff003c, 0x8f3003cc, 0x930c3cc0, +0x3003ccc0, 0x004fc00b, 0x0fcc00f3, 0x00c00e30, 0x00000c22, 0x00000000, +0x08362081, 0x0374021d, 0xb4600dd0, 0x7c73d30c, 0x41255003, 0x80dd2034, +0x3990036c, 0x1b0836c1, 0x10834400, 0x0cd5c241, 0x076c20d9, 0x04400db2, +0x00000802, 0x00000000, 0x00348000, 0x9a7400dd, 0x14440cd0, 0x44801900, +0x40055103, 0x00dd0035, 0x05900354, 0xcd003442, 0x14031400, 0x22074445, +0x0b4400d9, 0x04400d10, 0x00000a00, 0x00000000, 0x00302010, 0x003480cd, +0x01400cd0, 0x14000110, 0x44005003, 0xa0cd0031, 0x00960315, 0x01003040, +0x10031400, 0x00214000, 0x036400d9, 0x40400c90, 0x00000080, 0x00000000, +0x003eb000, 0x033c009f, 0x04500f70, 0xc5001b40, 0xd0091083, 0x80ff003d, +0x053803d4, 0xd1403c50, 0x3203dd00, 0x4007c001, 0x034c00f2, 0x00d00e30, +0x00000044, 0x00000000, 0x083f9004, 0x83fc00ff, 0x0ec00ff0, 0xec003f00, +0xc003f103, 0x00ff103e, 0x037803ec, 0x3f003fc4, 0xf083ec00, 0x000dc203, +0x03fc00f7, 0x17e80ff0, 0x00000e60, 0x00000000, 0x003fa003, 0x00fc1433, +0x1ac00f30, 0xfc08ff00, 0xc84f7123, 0x04f100bd, 0x4b70d0ec, 0x3b053d80, +0xf241dc80, 0x040ad40b, 0x03cc00f3, 0x0fc5c734, 0x00000e00, 0x00000000, +0x20330801, 0x10748651, 0x25480f11, 0x7400dd00, 0x44edd013, 0x1af103bc, +0xc8100855, 0x1100b450, 0xd0594424, 0x0094c00b, 0x030402eb, 0x07402511, +0x00000c20, 0x00000000, 0xa033a011, 0x60341001, 0x20040c12, 0x3604cd00, +0x410cd003, 0x00c19031, 0x04500004, 0x81003441, 0xd0010410, 0x05306888, +0x830402c1, 0x47404414, 0x00000e80, 0x00000000, 0x20358803, 0x80740851, +0x34580d10, 0x7400dd10, 0x400dd003, 0x00d18034, 0x09108514, 0x9540b440, +0xd1074400, 0x00364009, 0x034400d9, 0x0f408512, 0x00000620, 0x00000000, +0x4077a800, 0x03340093, 0x34c80c34, 0x7cb0df00, 0xc009f047, 0x00d34035, +0x19700c4c, 0x1b2030c0, 0xf0075d09, 0x9034c808, 0x034d00d3, 0x0bc23530, +0x00000e20, 0x00000000, 0x027d8087, 0x007c80ff, 0xadc80ff0, 0xf8a0fd20, +0xc00ed08b, 0x40ee003b, 0x2bf082dc, 0x3b027fc4, 0xd0037c01, 0x003dc00b, +0x83f800ff, 0x1fc015e0, 0x00000600, 0x00000000, 0x00350802, 0x236d0893, +0x27c00d34, 0x7e01d320, 0xc0093003, 0x00d31034, 0x45b0024c, 0x930cb4c0, +0x30127c01, 0x0033c009, 0x034d00d3, 0x08c06130, 0x00000420, 0x00000000, +0x21f4a013, 0x8dc401d1, 0xa6480f10, 0x7401db03, 0x420d1082, 0x00fb003c, +0x29108355, 0x910836c0, 0xb087742b, 0x0037424b, 0x03ee00f1, 0x4c502151, +0x00000200, 0x00000000, 0x01e6a007, 0x84040109, 0x13400c10, 0x3400c100, +0x500c1049, 0x00c90030, 0x09d00004, 0x11001060, 0x500d3482, 0x42234008, +0x036400d5, 0x1c422c10, 0x00000a00, 0x00000000, 0x02688006, 0x1c844149, +0x7b409e10, 0xf6016912, 0x491f1005, 0x4dc92278, 0x1a520694, 0x61427040, +0xd055b401, 0x027b409a, 0x07a001e5, 0x10009e40, 0x00000200, 0x00000000, +0x42201012, 0x800c060b, 0x23e80c10, 0x3400c320, 0xc88c3403, 0x04c98034, +0x2cf0034c, 0xc3023040, 0x70013c00, 0x0233c008, 0x030c04c6, 0x48c00430, +0x00000040, 0x00000000, 0x0a2db802, 0x90dc00b6, 0x3ec10ff0, 0xbc407f20, +0xc00eb003, 0x05ff003f, 0x89b0237c, 0xff023fd8, 0xb013fc08, 0x023fc20b, +0x63dc00fb, 0x0bc00ff4, 0x00000660, 0x00000000, 0x0037a015, 0x077c0093, +0x34ed2d30, 0x7c84d300, 0xc009f001, 0x18db00b7, 0x0d30804c, 0x532005c4, +0x30037c12, 0x2034c019, 0x034c04d3, 0x54c0093c, 0x00000e00, 0x00000000, +0x00398812, 0x14b400e1, 0x38400f12, 0xb490e100, 0x400ed003, 0x01c1053b, +0x0c1002ec, 0x61003bc4, 0x1003b400, 0x2038512a, 0x83d48af1, 0x48400e14, +0x00000620, 0x00000000, 0x00790083, 0x27f40361, 0x68404e10, 0xb401e120, +0x401ad007, 0x00e1427b, 0x1e100784, 0xe58c6d40, 0x90473401, 0x00f8404a, +0x178405e1, 0x0c451210, 0x00000400, 0x00000000, 0x0a372812, 0x893440c1, +0x74400c00, 0x34424140, 0x421cd187, 0x00c10033, 0x9c114324, 0xc5007240, +0x90073401, 0x00704008, 0x031400c9, 0x48506010, 0x00000c20, 0x00000000, +0x4055a817, 0x4dfc8173, 0x9cc00536, 0xfc485321, 0xc047f00d, 0x00530017, +0x073409cc, 0x570859f0, 0xa52dfc05, 0x40dcc805, 0x014c0053, 0x5cc03734, +0x00000620, 0x00000000, 0x00070012, 0x407c901f, 0x47c801f2, 0x7c001f08, +0xc001f22c, 0x00170807, 0x01f0807d, 0x1a4007c0, 0x70007c02, 0x0007c000, +0x007c0017, 0x4bc001f2, 0x00000c00, 0x00000000, 0x02270810, 0x067c4993, +0x24c009f0, 0x7c01db00, 0xc059f012, 0x80830023, 0x0938024d, 0xd30064c8, +0x20023c80, 0x0520d009, 0x020c8093, 0x40c00930, 0x00000c20, 0x00000000, +0x00a62001, 0x22742091, 0xa50009d8, 0x76019140, 0x4809d012, 0x009b0027, +0x2850026d, 0x93002474, 0x10027400, 0x00644019, 0x026c009b, 0x04402950, +0x00000800, 0x00000000, 0x00a4a018, 0x02748091, 0x244008d0, 0x56089904, +0x4009d802, 0x80910027, 0xa9120304, 0x91412440, 0x5482540a, 0x00344049, +0x02440081, 0x6040ac10, 0x00000200, 0x00000000, 0x4a202010, 0x22340881, +0x214828d0, 0x34008102, 0x61ccd002, 0x00890123, 0x89500224, 0x81003042, +0x50233408, 0x022040c8, 0xa2240489, 0x40508850, 0x00000080, 0x00000000, +0x0082b01d, 0x597c1613, 0x804001d0, 0x5c801b00, 0xc061d100, 0x1e1312c7, +0x6110784d, 0x13450441, 0x70585c02, 0x0580c031, 0x084c0b13, 0x74d16030, +0x00000ac0, 0x00000000, 0x212fb819, 0x92bc04ff, 0x2fc029f0, 0xfc149f01, +0xc04bf052, 0x019d0263, 0x4bf006fc, 0x97003bc0, 0xb012fc04, 0x112fc0ca, +0x1a7c099f, 0x67c04bf0, 0x00000e60, 0x00000000, 0x002fa019, 0x22bc6cb3, +0xacc10870, 0x7c029700, 0xc14bf002, 0x029301a4, 0x8bb00a4d, 0xb3012cc0, +0xf032fc00, 0x002cc183, 0x324c0c93, 0x60c14b30, 0x00000e00, 0x00000000, +0x4107089c, 0x18740e11, 0x06400130, 0x74001511, 0x40217250, 0x80111180, +0x8550006c, 0x13000450, 0xd0301c04, 0x14045071, 0x08144a11, 0x70506514, +0x00000c20, 0x00000000, 0x0423a010, 0x0a340081, 0x62610850, 0x34208501, +0x4149d012, 0x008180a0, 0x08100a44, 0x89022440, 0xd0123414, 0x02224008, +0x1a240089, 0x40410814, 0x00000e80, 0x00000000, 0x0021a818, 0x82760091, +0x22480991, 0x74009100, 0x08095062, 0x00810024, 0x89501264, 0x91002540, +0xd00b5614, 0x04264009, 0x02342099, 0x60404814, 0x00000620, 0x00000000, +0x0027a805, 0x227c0393, 0x64c00872, 0x7c049740, 0xc009f002, 0x80934024, +0x0930164c, 0x9b40a0c8, 0xe0a27c01, 0x0062d008, 0x026c809b, 0x14c02934, +0x00000e20, 0x00000000, 0x00258014, 0x823c029f, 0x65d00970, 0x7c00df11, +0xd0097003, 0x009f1027, 0x09f0027c, 0x970026c0, 0xf0065c80, 0xc165c001, +0x025c0097, 0x53c009f0, 0x00000600, 0x00000000, 0x00050814, 0x104c001f, +0x04c20132, 0x7c001308, 0xc8113000, 0x00131003, 0x01b0086c, 0x1b0244c0, +0x30004c10, 0x1006c401, 0x004c4003, 0x50d06130, 0x00000420, 0x00000000, +0x801ca014, 0x05c4805d, 0x1c400712, 0x74055100, 0x6076b00d, 0x0051401f, +0x375001fc, 0x7100dcc2, 0xb001dc83, 0x115c4013, 0x017c0051, 0x51501714, +0x00000200, 0x00000000, 0x0062a014, 0x072501cd, 0x12520c10, 0x34004100, +0x401c1048, 0x00c90033, 0xad900364, 0xc9807269, 0x50072601, 0x0870601c, +0x030400c1, 0x50403012, 0x00000a00, 0x00000000, 0x0c288005, 0x43e403ed, +0x2e404e12, 0xb4006100, 0x400a900e, 0x00e9023b, 0x0e542334, 0xa1001840, +0xdb0e9603, 0x5020554c, 0x13b001c1, 0x15490210, 0x00000200, 0x00000000, +0x00481015, 0x06ac81bd, 0x7a413e34, 0xf4016300, 0x48163017, 0x01c8417b, +0x1cb417ac, 0xfb207250, 0x7407ad01, 0x007ac00e, 0x178c04e3, 0x54d01f34, +0x00000040, 0x00000000, 0x202db810, 0x005800df, 0x21c00df0, 0x7c065f44, +0xc007f0af, 0x00d70137, 0x0df00b6c, 0x9f0017d0, 0xa0026c00, 0x00a7c81d, +0x2b6c02df, 0x42c00df4, 0x00000660, 0x00000000, 0x027fa002, 0x06fc41f3, +0x5dc01fb0, 0xcc037300, 0xc097300d, 0x13f320fd, 0x1fb00fec, 0xf3206dc0, +0xf006fc01, 0x03fec11f, 0x0fcc13f3, 0x00c01f30, 0x00000e00, 0x00000000, +0x02398815, 0x01f404e1, 0x28c48e11, 0xc4806110, 0x41873089, 0x00f1013c, +0x4e1013ed, 0xab002ac0, 0xd002b480, 0x022a400e, 0x03c440f1, 0x54400f52, +0x00000620, 0x00000000, 0x200d0208, 0x42b400e1, 0x79400c90, 0xa4006900, +0x4a86902b, 0x08e10839, 0x06128384, 0xa1003d44, 0xd002a400, 0x8120400f, +0x038400e1, 0x004006d0, 0x00000400, 0x00000000, 0x00372806, 0x0c3421c1, +0x24400c10, 0x04124940, 0x40041003, 0x00c10030, 0x7c102324, 0x8900f242, +0xd00e3408, 0x0224420c, 0x030400c1, 0x1040a4d4, 0x00000c20, 0x00000000, +0x0005a81d, 0x8f7c0fd3, 0x05c00fb0, 0xec005b00, 0xc009b01d, 0x00f3403d, +0x4d3403cc, 0xd304f5c0, 0xf00a3c01, 0x6076f00c, 0x03cd40f3, 0x54d010f8, +0x00000620, 0x00000000, 0x60070001, 0x037c809f, 0x35c00df2, 0x5c401702, +0xc00df409, 0x00df0033, 0x0c70137c, 0x9f0417cc, 0xf22a7c00, 0x2027c80d, +0x037c00df, 0x07c02364, 0x00000c00, 0x00000000, 0x002f0801, 0x428c03a3, +0x2ec20e30, 0xcc007306, 0xc857f003, 0x00e3003c, 0x2f3c03ec, 0xf3001cc0, +0x304efc00, 0x017ec00f, 0x03cc00e3, 0x00c10330, 0x00000c22, 0x00000000, +0x18262089, 0x1e446091, 0x34400d50, 0x44005110, 0xd004d02f, 0x00da0036, +0x1d100315, 0x9b025144, 0x50005c43, 0x0065401d, 0x035440df, 0x04509914, +0x00000802, 0x00000000, 0x0414a001, 0x07450091, 0x06400d10, 0x44040180, +0x4005d081, 0x00d10034, 0x0d580366, 0x1920b440, 0x50027423, 0x0034401d, +0x030400d5, 0x0440251b, 0x00000200, 0x00000000, 0x40102010, 0x03048081, +0x30400c50, 0x06000140, 0x4004d101, 0x00c90030, 0x0c520315, 0x89003140, +0x52021400, 0x28214a0c, 0x031420cd, 0x40480d1a, 0x00000080, 0x00000000, +0x0026b001, 0x02440093, 0x2ac20f12, 0x4c804300, 0x4005d002, 0x00f3003c, +0x005003ec, 0x930014c0, 0x71027c00, 0x003ec00d, 0x03cc00f7, 0x00c00534, +0x00000ac0, 0x00000000, 0x003bb805, 0x80fc80bf, 0x3fc00ff8, 0xfc607f08, +0xc007f002, 0x00ff083f, 0x0bb003fc, 0xb7001bc0, 0xf080dc00, 0x082f800e, +0x03fc40ff, 0x17c007f0, 0x00000e60, 0x00000000, 0xc080a300, 0x0833020e, +0x839820e8, 0x3b038ec0, 0xb020ec08, 0x820ec083, 0x20ec0838, 0x0ec083b0, +0xa8083b02, 0xc083b228, 0x083b020e, 0x03b020ec, 0x0000008c, 0x00000000, +0xa022a200, 0x0232808e, 0x23a808ea, 0x3a808ea0, 0xa808ea02, 0x008ea023, +0x08ea023a, 0x8ea023a8, 0xea023a80, 0xa023a808, 0x023a808e, 0x03a808ea, +0x00000a88, 0x00000000, 0x80420100, 0x04120104, 0x41201048, 0x12010480, +0x20104804, 0x01048041, 0x10480412, 0x04804120, 0x48051201, 0x80412018, +0x04120104, 0x01201048, 0x00000004, 0x00000000, 0x80002100, 0x401a0006, +0x01a2006a, 0x18108600, 0xa0006800, 0x80068001, 0x0068001a, 0x068001a0, +0x6a401a00, 0x8001a110, 0x001a0006, 0x01a00068, 0x00000004, 0x00000000, +0xa0128200, 0x013a884e, 0x138004e2, 0x3a884e20, 0xa804ea01, 0x004ea013, +0x04ea013a, 0x4e2013a8, 0xea203a80, 0xa013a804, 0x0138804e, 0x03a804ea, +0x00000a8c, 0x00000000, 0x0000a300, 0x00180006, 0x01800060, 0x18080602, +0x80006080, 0x00060001, 0x00600018, 0x06000180, 0x60001800, 0x00018080, +0x00180806, 0x01800060, 0x0000008c, 0x00000000, 0x2012a300, 0x00108044, +0x11000442, 0x10804420, 0x08044201, 0x00442011, 0x04420110, 0x44201108, +0x42001080, 0x20110804, 0x01108044, 0x01080442, 0x0000008c, 0x00000000, +0xa052a200, 0x04088142, 0x50881420, 0x0a8342a0, 0x88142a05, 0x0142a050, +0x1422050a, 0x42a050a8, 0x28070a81, 0xa050a814, 0x050a8142, 0x00a8142a, +0x00000a8c, 0x00000000, 0x80300100, 0x032800ca, 0x3aa00ca8, 0x2200ea80, +0xa00ca803, 0x00ca8032, 0x0ca8032a, 0xca8032a0, 0xa8032a00, 0x8032a00e, +0x030200ea, 0x02a00ca8, 0x00000004, 0x00000000, 0x00000100, 0x01080002, +0x00800020, 0x20000200, 0x80002080, 0x00020000, 0x00200008, 0x02080080, +0x20070800, 0x00008000, 0x00080002, 0x80800020, 0x00000004, 0x00000000, +0x4006a200, 0x40410010, 0x04100104, 0x61101040, 0x10010400, 0x00104004, +0x01040040, 0x10400410, 0x04404100, 0x40041101, 0x00410010, 0x80100104, +0x00000a8c, 0x00000000, 0xa002a300, 0x001a8006, 0x09a00066, 0x1a8026a0, +0xa8006a00, 0x8006a001, 0x006a001b, 0x06a001a8, 0x6e801a80, 0xa001a802, +0x001a8026, 0x01a8006a, 0x0000008c, 0x00000000, 0xc002a300, 0x01190006, +0x01b80060, 0x1b0006c0, 0xb0006000, 0x0006c001, 0x006c001b, 0x06c00190, +0x60001b00, 0xc001b000, 0x001b0006, 0x01b0006c, 0x0000008c, 0x00000000, +0x2842a300, 0x0430910c, 0x431810c2, 0x30910c20, 0x0810c004, 0xa10c2043, +0x10c20430, 0x0c004308, 0xc2443081, 0x20430a10, 0x0430810c, 0x030810c2, +0x00000a88, 0x00000000, 0x00000100, 0x0030000c, 0x030000c0, 0x30080c02, +0x00008000, 0x000c0003, 0x00c00030, 0x0c000300, 0xc0003000, 0x00030080, +0x0030080c, 0x030000c0, 0x00000208, 0x00000000, 0x80400100, 0x0c32010c, +0x432010ca, 0x32010c80, 0x2010c804, 0x010c8043, 0x10c80432, 0x0c804320, +0xca0c3201, 0x80432010, 0x0432010c, 0x032010c8, 0x00000004, 0x00000000, +0x2042a300, 0x0c1a8106, 0x4180106a, 0x1a810620, 0x88106204, 0x8106a041, +0x106a041a, 0x06a041a8, 0x6a0c1a81, 0xa041a810, 0x041a8106, 0x01a8106a, +0x00000a8c, 0x00000000, 0x0042a300, 0x04100104, 0x410010c0, 0x10010400, +0x00104084, 0x01040041, 0x10400410, 0x04004100, 0xc0041001, 0x00410210, +0x04100104, 0x01001040, 0x0000008c, 0x00000000, 0x2042a200, 0x04188126, +0x418010c2, 0x18812620, 0x88106204, 0x81062041, 0x10620418, 0x06204188, +0xc2049881, 0x20418810, 0x04188106, 0x01881062, 0x00000088, 0x00000000, +0xa006a200, 0x006a801a, 0x06800180, 0x6a801aa0, 0xa801a200, 0x801aa006, +0x01aa006a, 0x1a2006a8, 0x88006a80, 0xa006a801, 0x0068801a, 0x02a801aa, +0x00000a8c, 0x00000000, 0x80600000, 0x060a0182, 0x60a01828, 0x0a018280, +0xa0182806, 0x01828060, 0x18280608, 0x820060a0, 0x28060a01, 0x0060a018, +0x060a0182, 0x00a01828, 0x00000004, 0x00000000, 0x80400000, 0x04020120, +0x40201008, 0x02012080, 0x20100804, 0x01008040, 0x10080402, 0x00804020, +0x08048201, 0x80402010, 0x04020100, 0x00201008, 0x00000004, 0x00000000, +0xc062a300, 0x262b018a, 0x62b018ac, 0x2b098ac0, 0xb018a806, 0x018ac062, +0x18ac062b, 0x888062b0, 0xac262b01, 0xc062b098, 0x062b018a, 0x02b018ac, +0x00000a88, 0x00000000, 0xa062a300, 0x063a918e, 0x638818ee, 0x3a918ea0, +0xa818e706, 0x818ea063, 0x18ea0638, 0x8ca063a8, 0xe6463881, 0x2063a818, +0x063a818e, 0x03a818ea, 0x0000008c, 0x00000000, 0xc062a200, 0x063b018a, +0x63b018e8, 0x33818e40, 0xb018ec06, 0x018ec063, 0x18e40632, 0x8ee063b8, +0xe2863901, 0xc063b018, 0x063b018e, 0x03b018ec, 0x00000088, 0x00000000, +0xa062a200, 0x063a818e, 0x63a818e2, 0x32898ea2, 0xa818ea06, 0x018ea863, +0x18e20632, 0x8ea863a8, 0xca063a81, 0x2063a098, 0x863a898e, 0x03a818ea, +0x00000a88, 0x00000000, 0x80400000, 0x04120104, 0x412010c8, 0x12010480, +0x20104804, 0x01048041, 0x10480412, 0x04804120, 0x68041201, 0x80412010, +0x04120104, 0x01201048, 0x00000800, 0x00000000, 0x80622000, 0x061a0186, +0x61a0186a, 0x1a818680, 0xa0186886, 0x01868861, 0x1848061a, 0x86a061a8, +0x6a061a01, 0x8061a018, 0x061a0184, 0x01a01868, 0x00000008, 0x00000000, +0xa002a200, 0x003a800e, 0x038800ea, 0x38800ea0, 0xa800ea00, 0x800ea003, +0x00ea003a, 0x0e8003a0, 0xe8001280, 0xa0038800, 0x0038800e, 0x03a800e2, +0x00000a80, 0x00000000, 0x0042a200, 0x04180106, 0x41801060, 0x18010600, +0x80102004, 0x01060041, 0x10600418, 0x06004180, 0x60041801, 0x00418010, +0x04180106, 0x01801060, 0x00000280, 0x00000000, 0x2040a000, 0x04108104, +0x41081042, 0x10810420, 0x08100204, 0x81042041, 0x10420410, 0x04004100, +0x40041081, 0x20410810, 0x04108104, 0x01081042, 0x00000088, 0x00000000, +0xa042a200, 0x040a8102, 0x40a81020, 0x0a0102a0, 0xa8102a04, 0x8102a040, +0x102a040a, 0x02804080, 0x28040a81, 0xa040aa10, 0x040a8102, 0x00a8102a, +0x00000a88, 0x00000000, 0x80c28000, 0x0c2a030a, 0xc2a030a8, 0x2a010a80, +0xa030a80c, 0x030a80c2, 0x30a80c2a, 0x0a80c2a0, 0xa80c2a03, 0x00c2a030, +0x0c28030a, 0x02a030a8, 0x00000000, 0x00000000, 0x0012a200, 0x01080042, +0x10800420, 0x08004200, 0x80042001, 0x00420010, 0x04200108, 0x42081080, +0x20010800, 0x00108004, 0x01080042, 0x00800420, 0x00000000, 0x00000000, +0x4042a200, 0x04010100, 0x40101004, 0x01010040, 0x10100404, 0x01004040, +0x10040400, 0x00404010, 0x04040101, 0x40400010, 0x04010100, 0x00101004, +0x00000a88, 0x00000000, 0xa0022200, 0x001a8006, 0x01a8006e, 0x180006a0, +0xa8006800, 0xc0068001, 0x0062001b, 0x06a001a8, 0x6e001a80, 0xa001ac00, +0x001aa006, 0x01a8006a, 0x00000088, 0x00000000, 0x4002a300, 0x00190006, +0x019000e2, 0x0b000680, 0xb0002c00, 0xa006c001, 0x00640019, 0x068001b0, +0xe8001b00, 0xc001b000, 0x001b0006, 0x01b0006c, 0x00000000, 0x00000000, +0x20000000, 0x0030800c, 0x03080062, 0x30800c00, 0x0800c280, 0x800c2003, +0x00c20030, 0x0c000308, 0x62003080, 0x20030a00, 0x0010800c, 0x030800c2, +0x00000000, 0x00000000, 0x00000000, 0x00b0000c, 0x23000040, 0x30002c00, +0x0000c000, 0x000c0003, 0x00c00030, 0x0c000300, 0x4000b000, 0x0003001a, +0x0030000c, 0x030000c0, 0x00000000, 0x00000000, 0x80a00000, 0x0a30028c, +0xa32028c8, 0x32008c80, 0x2028c80a, 0x828c80a3, 0x28c80a32, 0x8c80a320, +0xca0a3202, 0x80a32028, 0x8a12028c, 0x032028c8, 0x00000000, 0x00000000, +0xa0c40000, 0x0c588316, 0xe5883168, 0x5a8316a0, 0xa8316a0c, 0x0316a0c5, +0x316a0c5a, 0x16a0c5a8, 0x6a0c5a83, 0x20c5a839, 0x0c5a8316, 0x01a8316a, +0x00000000, 0x00000000, 0x00000000, 0x00900004, 0x81000040, 0x10002400, +0x00004000, 0x00040001, 0x00400010, 0x04000100, 0x40009000, 0x00010002, +0x00100004, 0x01000040, 0x00000000, 0x00000000, 0x22100000, 0x21188846, +0x11888460, 0x18804626, 0x88846221, 0x08462211, 0x84622118, 0x46221188, +0x62211888, 0x22118804, 0x21188046, 0x01888462, 0x00000000, 0x00000000, +0x22000000, 0xa028800a, 0x02a880a8, 0x2a880aa2, 0xa880a220, 0x080aa202, +0x80a22028, 0x0a220288, 0xa8002888, 0x22028880, 0x202a880a, 0x02a880a2, +0x00000000, 0x00000000, 0x84100000, 0x41081042, 0x1081042a, 0x0a104204, +0xa1042841, 0x10420410, 0x0428410a, 0x428410a1, 0x20410a10, 0x8410a004, +0x410a1042, 0x00a10428, 0x00000000, 0x00000000, 0x80500000, 0x05020140, +0x50201408, 0x021940a0, 0x20140885, 0x01408050, 0x14080502, 0x40805020, +0x08050201, 0x80502014, 0x05020140, 0x00201408, 0x00000000, 0x00000000, +0xc0300000, 0x032b00ca, 0x32b00cac, 0x2b08ca80, 0xb00cac03, 0x00cac032, +0x0cac032b, 0xca8032b0, 0xac032b00, 0xc032b00c, 0x032b00ca, 0x02b00cac, +0x00000000, 0x00000000, 0xa0100000, 0x013a804e, 0x13a004ee, 0x3a804ea8, +0xaa04ea01, 0x804ea013, 0x04ea013a, 0x4e601388, 0xe6013880, 0xa013a804, +0x013a804e, 0x03a804e8, 0x00000000, 0x00000000, 0x18c40000, 0x8c484312, +0xc0863120, 0x48030218, 0x8631218c, 0x631218c4, 0x31218c48, 0x1218c486, +0x200c4863, 0x18c48230, 0x8c486302, 0x00863121, 0x00000000, 0x00000000, +0xfffc0000, 0xffcbfff2, 0xfcbfff2f, 0xcbfff2ff, 0xbfff2fff, 0xfff2fffc, +0xff2fffcb, 0xf2fffcbf, 0x2fffcbff, 0xfffcbfff, 0xffcbfff2, 0x00bfff2f, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xdb340000, 0xb3437ed0, +0x7c36cd0d, 0x4bedf0db, 0x36cd0db3, 0x6cd0db34, 0xcd0db343, 0xd0db3436, +0x0dfb436c, 0xdb34bedf, 0xb3436df0, 0x0036cd0d, 0x00000000, 0x00000000, +0x3ccc0000, 0xccc8ff32, 0xfc8f3323, 0xcbf3f23c, 0x8f3323cc, 0xf3323ccc, +0x3323ccc8, 0x323ccc8f, 0x23fcc8f3, 0x3cccbf3f, 0xccc8f3f2, 0x008f3323, +0x00000000, 0x00000000, 0x7edc0000, 0xedc9fb72, 0xdc9fb721, 0xc9e3727e, +0x9fb727ed, 0xfb727edc, 0xb727edc9, 0x727edc9f, 0x21edc9fb, 0x7edc9fb7, +0xedc9fb72, 0x009fb727, 0x00000000, 0x00000000, 0x40800000, 0x0839020e, +0x839020e4, 0x39020e40, 0x9020e608, 0x020e4083, 0x20e40839, 0x0e608390, +0xe0083902, 0x40839028, 0x0839020e, 0x038020e6, 0x00000000, 0x00000000, +0xa0200000, 0x023a808e, 0x23a808ea, 0x3a808ea0, 0xa808ea02, 0x808ea023, +0x08ea023a, 0x8ea023a8, 0xee023a80, 0xa023a808, 0x023a808e, 0x03a808ee, +0x00000000, 0x00000000, 0x80400000, 0x04120104, 0x41201048, 0x12010480, +0x20104804, 0x01048041, 0x00480412, 0x04804120, 0x48051201, 0x80412018, +0x04120104, 0x01201048, 0x00000000, 0x00000000, 0x08000000, 0x00180006, +0x01800060, 0x18118600, 0x80006000, 0x00060001, 0x10600018, 0x06000181, +0x60401800, 0x00018100, 0x00180006, 0x01890060, 0x00000000, 0x00000000, +0x22100000, 0x0138884e, 0x138804e2, 0x38884e20, 0x8804e001, 0x804e2013, +0x04e20138, 0x4e001388, 0xe2203880, 0x20138814, 0x2138804e, 0x038884e0, +0x00000000, 0x00000000, 0x00000000, 0x00180006, 0x01800060, 0x18080602, +0x80006000, 0x00060201, 0x80608018, 0x06000180, 0x60281800, 0x00018000, +0x00180006, 0x01800060, 0x00000000, 0x00000000, 0x20100000, 0x01108044, +0x11080442, 0x10806420, 0x08044001, 0x80442011, 0x06420110, 0x44001108, +0x42009080, 0x20110806, 0x03108044, 0x01080240, 0x00000000, 0x00000000, +0x20140000, 0x05488152, 0x54881522, 0x48815220, 0x88152205, 0x81522054, +0x15220548, 0x52205488, 0x20054881, 0x20548815, 0x05488152, 0x00801920, +0x00000000, 0x00000000, 0x00700310, 0x032800ca, 0x32800ca0, 0x0200ca00, +0x800ca003, 0x00ca0032, 0x0ca00328, 0xca003280, 0x80032800, 0x0032800c, +0x072800ca, 0x02801ca0, 0x00000000, 0x00000000, 0x00200310, 0x00000002, +0x00800020, 0x08002200, 0x80002000, 0x00020000, 0x02200008, 0x02000080, +0x82008800, 0x00008002, 0x02080002, 0x00800620, 0x0000000c, 0x00000000, +0x40020310, 0x00010000, 0x00100004, 0x01100040, 0x10000400, 0x00004000, +0x00040001, 0x00400011, 0x84400100, 0x40001100, 0x00010000, 0x00110004, +0x0000000c, 0x00000000, 0x00020310, 0x00128006, 0x01880062, 0x18800620, +0x88006000, 0x80062001, 0x00620018, 0x06000188, 0x62801880, 0x20018800, +0x00188006, 0x01980062, 0x0000000c, 0x00000000, 0x40028008, 0x00180006, +0x019a0064, 0x19000640, 0x90006400, 0x80064001, 0x00640019, 0x06400190, +0x60001900, 0x40019800, 0x00190006, 0x41800464, 0x00000000, 0x00000000, +0x2442a202, 0x0430110c, 0x430810c2, 0x30910c20, 0x0810c204, 0x810c2043, +0x10c20430, 0x0c204308, 0xc6443081, 0x20431810, 0x4430810c, 0x030910c2, +0x00000a88, 0x00000000, 0x00008002, 0x0030000c, 0x030000c0, 0x30080c02, +0x0000c000, 0x000c0203, 0x80c00030, 0x0c000300, 0xc0203000, 0x00030000, +0x0030000c, 0x030000c0, 0x00000000, 0x00000000, 0x00402002, 0x0430010c, +0x430010c0, 0x30010c00, 0x0010c004, 0x010c0043, 0x10c00430, 0x0c004300, +0xc00d3001, 0x00430010, 0x0430010c, 0x030830c0, 0x00000000, 0x00000000, +0x2042a202, 0x04188106, 0x41801062, 0x18810620, 0x88106204, 0x01062041, +0x10620418, 0x06204188, 0xe20c1881, 0x20418010, 0x04188106, 0x01883062, +0x00000a88, 0x00000000, 0x00408002, 0x04100104, 0x41001040, 0x10010400, +0x00104004, 0x01040041, 0x10400410, 0x04004100, 0x60041001, 0x00410010, +0x04100104, 0x03001040, 0x00000000, 0x00000000, 0x2042800a, 0x04188106, +0x41801062, 0x18810620, 0x88106204, 0x01062041, 0x10620418, 0x06204188, +0x42041881, 0x20418010, 0x04188106, 0x03081062, 0x00000000, 0x00000000, +0x2002a202, 0x8028800a, 0x028800a2, 0x28800a20, 0x8800a200, 0x800a2002, +0x00a20028, 0x0a200288, 0x80002880, 0x20028000, 0x0028800a, 0x420000a2, +0x00000a88, 0x00000000, 0x00600012, 0x06080182, 0x60801820, 0x08018200, +0x80182006, 0x01820060, 0x18200608, 0x82006080, 0x20060801, 0x00608018, +0x06080182, 0x00801820, 0x00000000, 0x00000000, 0x80408212, 0x04028100, +0x40201008, 0x02010088, 0x20100804, 0x01008040, 0x10080402, 0x00804020, +0x0a040201, 0x80402010, 0x04020100, 0x00201008, 0x00000000, 0x00000000, +0xc062a202, 0x062a018a, 0x62b018ac, 0x2b098ac0, 0xb018ac06, 0x018ac062, +0x98ac062b, 0x8ac062b0, 0xac262b01, 0xc062b098, 0x062b018a, 0x02b098ac, +0x00000a88, 0x00000000, 0x24628200, 0x0638918e, 0x638018e2, 0x38918e20, +0x8818e206, 0x018e2063, 0x18e20638, 0x8e206388, 0xe2463881, 0x20638818, +0x4638818e, 0x039918e2, 0x00000000, 0x00000000, 0x40628002, 0x0639018e, +0x639018e4, 0x39018e60, 0x9018e406, 0x018e4063, 0x18e40639, 0x8e406390, +0xe8063901, 0x40639018, 0x0638018e, 0x03a018e4, 0x00000000, 0x00000000, +0xa062a20a, 0x063a818e, 0x63a818ea, 0x3a898ee2, 0xa818ea06, 0x818ea263, +0x98ea063a, 0x8ea063a8, 0xe6263a81, 0xa063a818, 0x063a018e, 0x438818ea, +0x00000a88, 0x00000000, 0x80480002, 0x04120124, 0x41201048, 0x12012480, +0x20104804, 0x01048041, 0x10480412, 0x04804120, 0x48049201, 0x80412010, +0x04920104, 0x03201248, 0x00000000, 0x00000000, 0x00608002, 0x06180186, +0x61801860, 0x18018600, 0x80186006, 0x01860061, 0x18600618, 0x86006180, +0x40061801, 0x00618018, 0x06180186, 0x01881860, 0x00000000, 0x00000000, +0x2006a000, 0x0078801e, 0x078801e2, 0x78801e00, 0x8801e200, 0x801e2007, +0x01e20078, 0x1e200788, 0xe2007880, 0x20078801, 0x0078801e, 0x038801e2, +0x00000a88, 0x00000000, 0x004a8002, 0x04180126, 0x41801060, 0x18012600, +0x80106004, 0x21060841, 0x10600418, 0x06004180, 0x60049801, 0x00418010, +0x04980106, 0x01801260, 0x00000000, 0x00000000, 0x20428002, 0x04108104, +0x41081042, 0x10810400, 0x08104204, 0x81042041, 0x10420410, 0x04204108, +0x42041081, 0x20410810, 0x04108104, 0x01081042, 0x00000000, 0x00000000, +0x2042a202, 0x04088102, 0x40881022, 0x08810200, 0x88102204, 0x81022040, +0x10220408, 0x02204088, 0x20040881, 0x20408810, 0x04088102, 0x00801022, +0x00000a88, 0x00000000, 0x00c00002, 0x0c28030a, 0xc28030a0, 0x28030a00, +0x8030a00c, 0x030a00c2, 0x30a00c28, 0x0a00c280, 0xa0040203, 0x00c28030, +0x0c28030a, 0x028030a0, 0x00000000, 0x00000000, 0x00100002, 0x01082042, +0x10800420, 0x08004200, 0x80042001, 0x00420010, 0x04200108, 0x42001080, +0x22010800, 0x00108004, 0x01088042, 0x00800420, 0x00000000, 0x00000000, +0x4042a202, 0x04010100, 0x40101004, 0x01010040, 0x10100404, 0x01004040, +0x10040401, 0x00404010, 0x04040101, 0x40401010, 0x04000100, 0x00101004, +0x00000a88, 0x00000000, 0x20028002, 0x00188006, 0x01880062, 0x18800620, +0x88006200, 0x80062001, 0x00620018, 0x06200188, 0x62001880, 0x20018800, +0x0018c006, 0x01980062, 0x00000000, 0x00000000, 0x40008002, 0x80198006, +0x09900066, 0x19002640, 0x90006400, 0x00064009, 0x02640019, 0x06400190, +0x40009900, 0x40019000, 0x00190006, 0x03800064, 0x00000000, 0x00000000, +0x2006a202, 0x0070801c, 0x070801c2, 0x70801c20, 0x0801c200, 0x801c2007, +0x01c20070, 0x1c200708, 0x46007080, 0x20070801, 0x0070801c, 0x018801c2, +0x00000a88, 0x00000000, 0x0080200a, 0x0830020c, 0x830020c0, 0x30028c00, +0x0020c008, 0x020c00e3, 0x28c00830, 0x0c008300, 0x400a3002, 0x00830020, +0x0830020c, 0x010020c0, 0x00000000, 0x00000000, 0x00a00002, 0x0a30828c, +0xab0028c2, 0x3003ac00, 0x0028c00a, 0x028c00ab, 0x2ac00a30, 0x8c00a300, +0x4006b002, 0x00a30028, 0x0a30028c, 0x430828c0, 0x00000200, 0x00000000, +0x20c2a002, 0x0c120306, 0xc1883060, 0x18838620, 0x8830620c, 0x830620c1, +0x38620c18, 0x0620c188, 0x620e1883, 0x20c18830, 0x0c188306, 0x01883062, +0x00000a88, 0x00000000, 0x00020002, 0x00100004, 0x01000040, 0x10000400, +0x00004000, 0x00040021, 0x00400010, 0x04000100, 0x40001000, 0x00010000, +0x00100004, 0x01000040, 0x00000000, 0x00000000, 0x22128002, 0x21100846, +0x11888460, 0x18804620, 0x88846221, 0x88462011, 0x04622118, 0x46221188, +0x62011888, 0x22118884, 0x21188846, 0x01888462, 0x00000000, 0x00000000, +0x2002a202, 0x2028200a, 0x028880a0, 0x28800a22, 0x8880a220, 0x880a2202, +0xa0a22028, 0x0a220288, 0xa0202888, 0x22028880, 0x0028880a, 0x028000a2, +0x00000a88, 0x00000000, 0x04100002, 0x41081042, 0x10810420, 0x08004204, +0x81042041, 0x10420410, 0x04204108, 0x42041080, 0x20410810, 0x04108004, +0x41081042, 0x00810420, 0x00000008, 0x00000000, 0x80508000, 0x05020140, +0x50201408, 0x02014080, 0x20140805, 0x01408050, 0x14080502, 0x40805020, +0x0a050201, 0x80502014, 0x05020140, 0x00201408, 0x00000000, 0x00000000, +0xc0b2a002, 0x0b2b02ca, 0xb2b02cac, 0x2b02cac0, 0xb02cac0b, 0x02cac0b2, +0x2cac0b2b, 0xcac0b2b0, 0xac0b2b02, 0xc0b2b02c, 0x0b2b02ca, 0x02b02cac, +0x00000a88, 0x00000000, 0x20120002, 0x0138804e, 0x138804e2, 0x38804e20, +0x8804e201, 0x804e2013, 0x04e20038, 0x4e20138a, 0xe2003880, 0x20138804, +0x0138804e, 0x039804e2, 0x00000000, 0x00000000, 0x10c40000, 0x8c484312, +0xc0863121, 0x48030218, 0x8631218c, 0x631218c0, 0x30218c48, 0x1218c482, +0x200c0863, 0x18c48231, 0x0c486312, 0x00803121, 0x00000000, 0x00000000, +0xfffc0000, 0xffcbfff2, 0xfcbfff2f, 0xcbfff2ff, 0xbfff2fff, 0xfff2fffc, +0xff2fffcb, 0xf2fffcbf, 0x2fffcbff, 0xfffcbfff, 0xffcbfff2, 0x00bfff2f, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xdfb40000, 0xb3437ed0, +0x7c36cd0d, 0x4bfff0db, 0x36cd0db3, 0x6cd0db7c, 0xdf0db343, 0xd0db34be, +0x0db7c36c, 0xdb34becd, 0xfb436cd0, 0x0037ed0d, 0x00000000, 0x00000000, +0x3fcc0000, 0xccc8ff32, 0xfc8f3323, 0xcbfff23c, 0x8f3323cc, 0xf3323cfc, +0x3f23ccc8, 0x323cccbf, 0x23cfc8f3, 0x3cccbf33, 0xfcc8f332, 0x008ff323, +0x00000000, 0x00000000, 0x7edc0000, 0xedc9fb72, 0xdc9fb727, 0xc9fb727e, +0x9fb727ed, 0xfb727edc, 0xb727edc9, 0x727edc9f, 0x218dc9fb, 0x7edc9fb7, +0xedc9fb72, 0x0087b727, 0x00000000, 0x00000000, 0x21c00000, 0x24d18020, +0x42100002, 0x02850061, 0x0a004034, 0x85002140, 0x51060433, 0x08c34208, +0x82143395, 0xc14008d0, 0x14218508, 0x00085002, 0x00000000, 0x00000000, +0xa0004000, 0x00528820, 0x0828900a, 0x410060a0, 0x08004804, 0x802c2001, +0x000a0040, 0x20208118, 0x44005080, 0x20000002, 0x00128020, 0x00080202, +0x00000120, 0x00000000, 0x61000000, 0x40109504, 0x1138940e, 0x128c5061, +0x08c6ce31, 0x845c2312, 0xc4061180, 0x54231828, 0x46118084, 0x23180044, +0x10b08454, 0x00284002, 0x00000000, 0x00000000, 0x20900800, 0x01308144, +0x1038340e, 0xd0006060, 0x08960e01, 0x8068201e, 0x04060133, 0x64201828, +0x0201b380, 0x20100006, 0x01318064, 0x00280602, 0x00000000, 0x00000000, +0x01124000, 0x25131454, 0x13004048, 0x10044401, 0x00e44031, 0x04400110, +0x84003133, 0x40031000, 0x00110004, 0x410020c4, 0x10800400, 0x00004000, +0x00000900, 0x00000000, 0x04400010, 0x20300100, 0x4020000c, 0x20010c00, +0x00108400, 0x01000042, 0x10000400, 0x00004000, 0x80040201, 0x80402010, +0x04000108, 0x00001000, 0x00000000, 0x00000000, 0x81110010, 0x11900474, +0x1832044c, 0x102c7c01, 0x00c44831, 0x04600319, 0xc4001100, 0x50031002, +0x40118104, 0x43103044, 0x11800454, 0x40004540, 0x00000800, 0x00000000, +0x80100000, 0x05130244, 0x1030844c, 0x80004000, 0x00a60801, 0x00400018, +0x040001b3, 0x40001000, 0x00018000, 0x00103004, 0x01800054, 0x40000540, +0x00000000, 0x00000000, 0x2a102010, 0x11e2844c, 0x53088486, 0x00855821, +0x08048281, 0x85702b53, 0xd7ceb533, 0x4823580a, 0x02153185, 0x615a08d7, +0x15008540, 0x40085402, 0x00000080, 0x00000000, 0xa2100010, 0x01028064, +0x5538844a, 0x90806c20, 0x18064201, 0x80602053, 0x04020190, 0x6420d818, +0x42019280, 0xa05b0804, 0x01808064, 0x00080642, 0x00000000, 0x00000000, +0xc1000010, 0x2411841c, 0x0620804c, 0xa08c0c21, 0x28d28a20, 0x84002305, +0xc04210a0, 0x28230828, 0x0210708c, 0x630b0840, 0x10408408, 0x40084002, +0x00000000, 0x00000000, 0xa0080800, 0x20a3801c, 0x021883c2, 0x13801c22, +0x08018a04, 0x80102803, 0x01ce0033, 0x14e00b38, 0x82002380, 0xa0020800, +0x0033801c, 0x43380082, 0x00000020, 0x00000000, 0x61000010, 0x14d08000, +0x0028408e, 0x63840ca1, 0x08c00610, 0x8400234b, 0xc00e3053, 0x0c234108, +0x8e1063a4, 0x210028c2, 0x10608400, 0x40084282, 0x00000000, 0x00000000, +0x80480000, 0x00200908, 0x0030118a, 0xb2831ca0, 0x00808800, 0x81040003, +0x10460410, 0x0c000100, 0x02040081, 0x00011010, 0x04120100, 0x00001040, +0x00000000, 0x00000000, 0x41480000, 0x14122004, 0x0d304042, 0x838c0421, +0x20c24410, 0x8408030b, 0x02c40040, 0x0c000f00, 0x4210508c, 0x030c2042, +0x10410400, 0x00004280, 0x00000000, 0x00000000, 0x40070800, 0x00120824, +0x093000cc, 0x13800806, 0x20110004, 0x00280809, 0x01402020, 0x28000500, +0x8c002300, 0x000a2001, 0x00200020, 0x00000100, 0x00000420, 0x00000000, +0x0808000c, 0x00000001, 0x0b000000, 0x02000008, 0x00000004, 0x0000080b, +0x00000000, 0x000c0b00, 0x00000000, 0x080b0000, 0x00000000, 0x33000000, +0x00000000, 0x00000000, 0x0308040c, 0x00000501, 0x09090c0a, 0x00010107, +0x08000800, 0x01010709, 0x00080402, 0x010b0908, 0x08040201, 0x03090800, +0x04020103, 0x31080008, 0x00000010, 0x00000000, 0x385be000, 0xc4030910, +0xffe52fe7, 0x01019a03, 0x980c0264, 0x00020001, 0x90019003, 0x01000001, +0x00000300, 0x00000004, 0x0c030001, 0x00019c00, 0x00000f80, 0x00000000, +0x70c80000, 0x6401322e, 0x00774532, 0x00000000, 0x00000208, 0x00000000, +0x00000001, 0x01000002, 0x00080000, 0x00000008, 0x00010000, 0x00000800, +0x00000000, 0x00000000, 0x42280000, 0x29138111, 0x02802a14, 0x44000000, +0x01000000, 0x00000000, 0x00010044, 0x00000000, 0x01004400, 0x00000100, +0x00440000, 0x00010001, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0xfc000000, 0x03ff7eff, 0x00000000, 0xf76cff7c, 0x00000002, 0xeefffc00, +0x000003fd, 0x7cfc0000, 0x00037ff6, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0x7c000000, 0x03e77beb, 0x00000000, 0xfef7fffc, +0x00000003, 0xf7fef400, 0x000003ff, 0x7efc0000, 0x0003fffe, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xf8000000, 0x03f3fbf3, +0x00000000, 0xedf2fdf8, 0x00000003, 0xf6f9f400, 0x000002ff, 0xfcfc0000, +0x0002fef7, 0x00000000, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0x3c000000, 0x03df7fcf, 0x00000000, 0xfffddefc, 0x00000003, 0xfffefc00, +0x000003ff, 0xfffc0000, 0x0003ffff, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0xfc000000, 0x03dfdeff, 0x00000000, 0xffff7ffc, +0x00000003, 0xfe7f3c00, 0x000003ff, 0xfffc0000, 0x0003ffff, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xdc000000, 0x03ffffff, +0x00000000, 0xffffffbc, 0x00000003, 0x7ffebc00, 0x000003ff, 0xbf7c0000, +0x0003feff, 0x00000000, 0x00000000, 0x00000000, 0xa3400000, 0x14528804, +0x41085006, 0xc0840421, 0x08514214, 0x85002140, 0x514234a2, 0x00210108, +0x06148085, 0xe14328d0, 0x14e08528, 0x00085002, 0x00000000, 0x00000000, +0xe2400000, 0x84028000, 0x4228000e, 0x40800820, 0x00004201, 0x80402011, +0x00828102, 0x60200208, 0x8a004080, 0x20091804, 0x00708060, 0x00080402, +0x00000000, 0x00000000, 0xa0000000, 0x5953a824, 0x1918440e, 0x00854423, +0x00444211, 0x8440211a, 0x44420191, 0x00234908, 0x42101084, 0x23032840, +0x10108404, 0x00084002, 0x00000000, 0x00000000, 0x62100000, 0x41838164, +0x1808040a, 0x80807420, 0x0004c201, 0x80702018, 0x040221a2, 0x40205908, +0x06015080, 0xe01b0807, 0x01208048, 0x00080702, 0x00000000, 0x00000000, +0x42400000, 0x55102060, 0x41004400, 0x10042801, 0x00408810, 0x04640102, +0x44083180, 0x68c11000, 0x00100004, 0x011020c4, 0x11100450, 0x01004440, +0x00000000, 0x00000000, 0x80000010, 0x00132108, 0x02301088, 0x20010400, +0x10100804, 0x01080041, 0x10000410, 0x00004000, 0x00040001, 0x80401010, +0x04200100, 0x40001080, 0x00000000, 0x00000000, 0xc0100010, 0x19d20164, +0x11306440, 0x90046c03, 0x20464411, 0x04640112, 0x864811b0, 0x64031100, +0x40115004, 0x43192046, 0x11100464, 0x41004540, 0x00000000, 0x00000000, +0x80100010, 0x01130060, 0x18000444, 0x00005c00, 0x00048801, 0x0050001a, +0x84480190, 0x68c01900, 0x40011000, 0x001d2004, 0x01800044, 0x41000400, +0x00000000, 0x00000000, 0xe0100010, 0x55418944, 0x50284442, 0x10856021, +0x08548615, 0x85606153, 0x54821500, 0x5c215108, 0x42152385, 0x615008d4, +0x15208560, 0x40085442, 0x00000000, 0x00000000, 0xe0100010, 0x01a28248, +0x111804ca, 0x50804420, 0x08054a01, 0x8040a013, 0x05422100, 0x6c201308, +0xca019080, 0x60100805, 0x01b08064, 0x400806c2, 0x00000000, 0x00000000, +0xa3000010, 0x08108014, 0x0628e0ce, 0x508c0021, 0x08428610, 0x84046107, +0x41821040, 0x0c230b28, 0x46107084, 0x83000841, 0x18708424, 0x400842c2, +0x00000000, 0x00000000, 0xe0480810, 0x0001a008, 0x0b1810ca, 0xa0801820, +0x38024e00, 0x8014600a, 0x020280b3, 0x04e00d28, 0x86000380, 0xa00b3800, +0x00138038, 0x4338000e, 0x00000020, 0x00000000, 0x20000010, 0x08928814, +0x0528414a, 0xb0840c21, 0x28400210, 0x84002108, 0x420e1081, 0x24210108, +0x02100084, 0x410128c0, 0x10b08404, 0x40084042, 0x00000000, 0x00000000, +0x08400000, 0x04030928, 0x4a303004, 0x70011400, 0x3010c004, 0x01000044, +0x104a2443, 0x0c004600, 0x8004a001, 0xa04d1013, 0x04100114, 0x00001000, +0x00000000, 0x00000000, 0x02040000, 0x10930404, 0x0130d348, 0x100c2c01, +0x30434010, 0x04200100, 0x42c62002, 0x04000100, 0x40109004, 0x43092040, +0x10300404, 0x01004140, 0x00000000, 0x00000000, 0x02090800, 0x00402000, +0x01002048, 0x30000000, 0x00028000, 0x00100000, 0x01002001, 0x28000100, +0x40004000, 0x20032000, 0x00b0000c, 0x01000140, 0x00000420, 0x00000000, +0x08080000, 0x04000401, 0x0b000801, 0x00040008, 0x00000004, 0x0802000b, +0x00010801, 0x02000b00, 0x01080108, 0x000b0000, 0x08010802, 0x03000001, +0x00000000, 0x00000000, 0x03083c3c, 0x00000901, 0x09090809, 0x00010107, +0x08000800, 0x01010309, 0x00080000, 0x01030908, 0x08000001, 0x03090800, +0x00000101, 0xf1080008, 0x000000f0, 0x00000000, 0xa1240000, 0x1402b696, +0x02482655, 0x02019a98, 0x00000000, 0x00030000, 0x08019c03, 0x02000000, +0x00000200, 0x00000190, 0x0c000000, 0x00000c00, 0x00000000, 0x00000000, +0x1a980000, 0x00017333, 0x0229620b, 0x00000100, 0x00880008, 0x00000000, +0x08000001, 0x00000002, 0x00080000, 0x00000000, 0x08010001, 0x00000800, +0x00000000, 0x00000000, 0x84100000, 0x41132183, 0x00218040, 0x44000000, +0x00000100, 0x00000000, 0x00020044, 0x00000000, 0x01004400, 0x00000100, +0x00440000, 0x00010003, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0xfc000000, 0x03ffffff, 0x00000000, 0x7fff7ffc, 0x00000003, 0xf7fefc00, +0x000003ff, 0xdefc0000, 0x0001fff7, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0xfc000000, 0x03ffffff, 0x00000000, 0x7fff7ffc, +0x00000001, 0xfffffc00, 0x0000007f, 0xfffc0000, 0x0002fffe, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xfc000000, 0x03ffffff, +0x00000000, 0xfffdfefc, 0x00000003, 0xfdfffc00, 0x000003ff, 0xfffc0000, +0x0003ffff, 0x00000000, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0xfc000000, 0x03ef7f7f, 0x00000000, 0x9ffffffc, 0x00000003, 0xfffffc00, +0x000003fc, 0xfffc0000, 0x0003ffff, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0xfc000000, 0x03ffffff, 0x00000000, 0xfdddfdfc, +0x00000003, 0xffff7c00, 0x000003fd, 0xfffc0000, 0x0003ffff, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xfc000000, 0x03ffbfff, +0x00000000, 0xfffefffc, 0x00000003, 0x7ffffc00, 0x000003ff, 0xff7c0000, +0x0003feff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x8004000c, 0x00000040, 0x71c2000c, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x8004000c, 0x00004040, 0x01c2000c, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x8000000c, 0xefcf0000, 0x8001000c, 0xc0000000, 0x7002000c, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x8001000c, 0xa0000000, 0x8005000c, 0x00000000, 0x8000000c, +0xe9d60000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; diff -urN linux-2.4.21-rc1.orig/sound/pci/rme9652/hammerfall_mem.c linux/sound/pci/rme9652/hammerfall_mem.c --- linux-2.4.21-rc1.orig/sound/pci/rme9652/hammerfall_mem.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme9652/hammerfall_mem.c 2003-02-25 06:35:44.000000000 -0700 @@ -0,0 +1,271 @@ +/* + ALSA memory allocation module for the RME Digi9652 + + Copyright(c) 1999 IEM - Winfried Ritsch + Copyright (C) 1999 Paul Barton-Davis + + This module is only needed if you compiled the hammerfall driver with + the PREALLOCATE_MEMORY option. It allocates the memory need to + run the board and holds it until the module is unloaded. Because + we need 2 contiguous 1.6MB regions for the board, it can be + a problem getting them once the system memory has become fairly + fragmented. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + hammerfall_mem.c,v 1.8 2003/02/25 13:35:44 perex Exp + + + Tue Oct 17 2000 Jaroslav Kysela + * space is allocated only for physical devices + * added support for 2.4 kernels (pci_alloc_consistent) + +*/ + +#include +#include +#include +#include +#include +#include + +#define HAMMERFALL_CARDS 8 +#define HAMMERFALL_CHANNEL_BUFFER_SAMPLES (16*1024) +#define HAMMERFALL_CHANNEL_BUFFER_BYTES (4*HAMMERFALL_CHANNEL_BUFFER_SAMPLES) + +/* export */ + +static int enable[8] = {1,1,1,1,1,1,1,1}; +MODULE_PARM(enable, "1-" __MODULE_STRING(HAMMERFALL_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable cards to allocate buffers for."); + +MODULE_AUTHOR("Winfried Ritsch, Paul Barton-Davis "); +MODULE_DESCRIPTION("Memory allocator for RME Hammerfall"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); + +/* Since we don't know at this point if we're allocating memory for a + Hammerfall or a Hammerfall/Light, assume the worst and allocate + space for the maximum number of channels. + + The extra channel is allocated because we need a 64kB-aligned + buffer in the actual interface driver code (see rme9652.c or hdsp.c + for details) +*/ + +#define TOTAL_SIZE (26+1)*(HAMMERFALL_CHANNEL_BUFFER_BYTES) +#define NBUFS 2*HAMMERFALL_CARDS + +#define HAMMERFALL_BUF_ALLOCATED 0x1 +#define HAMMERFALL_BUF_USED 0x2 + +typedef struct hammerfall_buf_stru hammerfall_buf_t; + +struct hammerfall_buf_stru { + struct pci_dev *pci; + void *buf; + dma_addr_t addr; + char flags; +}; + +static hammerfall_buf_t hammerfall_buffers[NBUFS]; + +/* These are here so that we have absolutely no dependencies + on any other modules. Dependencies can (1) cause us to + lose in the rush for 2x1.6MB chunks of contiguous memory + and (2) make driver debugging difficult because unloading + and reloading the snd module causes us to have to do the + same for this one. Since we can rarely if ever allocate + memory after starting things running, that would be very + undesirable. +*/ + +static void *hammerfall_malloc_pages(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dmaaddr) +{ + void *res; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + res = (void *) pci_alloc_consistent(pci, size, dmaaddr); +#else + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = (void *)__get_free_pages(GFP_KERNEL, pg); + if (res != NULL) + *dmaaddr = virt_to_bus(res); +#endif + if (res != NULL) { + struct page *page = virt_to_page(res); + struct page *last_page = page + (size + PAGE_SIZE - 1) / PAGE_SIZE; + while (page < last_page) + set_bit(PG_reserved, &(page++)->flags); + } + return res; +} + +static void hammerfall_free_pages(struct pci_dev *pci, unsigned long size, + void *ptr, dma_addr_t dmaaddr) +{ + struct page *page, *last_page; + + if (ptr == NULL) + return; + page = virt_to_page(ptr); + last_page = virt_to_page(ptr) + (size + PAGE_SIZE - 1) / PAGE_SIZE; + while (page < last_page) + clear_bit(PG_reserved, &(page++)->flags); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + pci_free_consistent(pci, size, ptr, dmaaddr); +#else + { + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if (bus_to_virt(dmaaddr) != ptr) { + printk(KERN_ERR "hammerfall_free_pages: dmaaddr != ptr\n"); + return; + } + free_pages((unsigned long)ptr, pg); + } +#endif +} + +void *snd_hammerfall_get_buffer (struct pci_dev *pcidev, dma_addr_t *dmaaddr) +{ + int i; + hammerfall_buf_t *rbuf; + + for (i = 0; i < NBUFS; i++) { + rbuf = &hammerfall_buffers[i]; + if (rbuf->flags == HAMMERFALL_BUF_ALLOCATED) { + rbuf->flags |= HAMMERFALL_BUF_USED; + rbuf->pci = pcidev; + *dmaaddr = rbuf->addr; + return rbuf->buf; + } + } + + return NULL; +} + +void snd_hammerfall_free_buffer (struct pci_dev *pcidev, void *addr) +{ + int i; + hammerfall_buf_t *rbuf; + + for (i = 0; i < NBUFS; i++) { + rbuf = &hammerfall_buffers[i]; + if (rbuf->buf == addr && rbuf->pci == pcidev) { + rbuf->flags &= ~HAMMERFALL_BUF_USED; + return; + } + } + + printk ("Hammerfall memory allocator: unknown buffer address or PCI device ID"); +} + +static void hammerfall_free_buffers (void) + +{ + int i; + hammerfall_buf_t *rbuf; + + for (i = 0; i < NBUFS; i++) { + + /* We rely on general module code to prevent + us from being unloaded with buffers in use. + + However, not quite. Do not release memory + if it is still marked as in use. This might + be unnecessary. + */ + + rbuf = &hammerfall_buffers[i]; + + if (rbuf->flags == HAMMERFALL_BUF_ALLOCATED) { + hammerfall_free_pages (rbuf->pci, TOTAL_SIZE, rbuf->buf, rbuf->addr); + rbuf->buf = NULL; + rbuf->flags = 0; + } + } +} + +static int __init alsa_hammerfall_mem_init(void) +{ + int i; + struct pci_dev *pci; + hammerfall_buf_t *rbuf; + + /* make sure our buffer records are clean */ + + for (i = 0; i < NBUFS; i++) { + rbuf = &hammerfall_buffers[i]; + rbuf->pci = NULL; + rbuf->buf = NULL; + rbuf->flags = 0; + } + + /* ensure sane values for the number of buffers */ + + /* Remember: 2 buffers per card, one for capture, one for + playback. + */ + + i = 0; /* card number */ + rbuf = hammerfall_buffers; + pci_for_each_dev(pci) { + int k; + + /* check for Hammerfall and Hammerfall DSP cards */ + + if (pci->vendor != 0x10ee || (pci->device != 0x3fc4 && pci->device != 0x3fc5)) + continue; + + if (!enable[i]) + continue; + + for (k = 0; k < 2; ++k) { + rbuf->buf = hammerfall_malloc_pages(pci, TOTAL_SIZE, &rbuf->addr); + if (rbuf->buf == NULL) { + hammerfall_free_buffers(); + printk(KERN_ERR "Hammerfall memory allocator: no memory available for card %d buffer %d\n", i, k + 1); + return -ENOMEM; + } + rbuf->flags = HAMMERFALL_BUF_ALLOCATED; + rbuf++; + } + i++; + } + + if (i == 0) + printk(KERN_ERR "Hammerfall memory allocator: " + "no Hammerfall cards found...\n"); + else + printk(KERN_ERR "Hammerfall memory allocator: " + "buffers allocated for %d cards\n", i); + + return 0; +} + +static void __exit alsa_hammerfall_mem_exit(void) +{ + hammerfall_free_buffers(); +} + +module_init(alsa_hammerfall_mem_init) +module_exit(alsa_hammerfall_mem_exit) + +EXPORT_SYMBOL(snd_hammerfall_get_buffer); +EXPORT_SYMBOL(snd_hammerfall_free_buffer); diff -urN linux-2.4.21-rc1.orig/sound/pci/rme9652/hdsp.c linux/sound/pci/rme9652/hdsp.c --- linux-2.4.21-rc1.orig/sound/pci/rme9652/hdsp.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme9652/hdsp.c 2003-03-04 09:47:35.000000000 -0700 @@ -0,0 +1,3184 @@ +/* + * ALSA driver for RME Hammerfall DSP audio interface(s) + * + * Copyright (c) 2002 Paul Davis + * Marcus Andersson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include +#include +#include + +#include "multiface_firmware.dat" +#include "digiface_firmware.dat" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */ +static int line_outs_monitor[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0}; /* Send all inputs/playback to line outs */ +static int force_firmware[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0}; /* Force firmware reload */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for RME Hammerfall DSP interface."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for RME Hammerfall DSP interface."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable/disable specific Hammerfall DSP soundcards."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(precise_ptr, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(precise_ptr, "Enable precise pointer (doesn't work reliably)."); +MODULE_PARM_SYNTAX(precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(line_outs_monitor,"1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(line_outs_monitor, "Send all input and playback streams to line outs by default."); +MODULE_PARM_SYNTAX(line_outs_monitor, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(force_firmware,"1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(force_firmware, "Force a reload of the I/O box firmware"); +MODULE_PARM_SYNTAX(force_firmware, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_AUTHOR("Paul Davis "); +MODULE_DESCRIPTION("RME Hammerfall DSP"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Hammerfall-DSP}}"); + +typedef enum { + Digiface, + Multiface +} HDSP_Type; + +#define HDSP_MAX_CHANNELS 26 +#define DIGIFACE_SS_CHANNELS 26 +#define DIGIFACE_DS_CHANNELS 14 +#define MULTIFACE_SS_CHANNELS 18 +#define MULTIFACE_DS_CHANNELS 14 + +/* Write registers. These are defined as byte-offsets from the iobase value. + */ +#define HDSP_resetPointer 0 +#define HDSP_outputBufferAddress 32 +#define HDSP_inputBufferAddress 36 +#define HDSP_controlRegister 64 +#define HDSP_interruptConfirmation 96 +#define HDSP_outputEnable 128 +#define HDSP_jtagReg 256 +#define HDSP_midiDataOut0 352 +#define HDSP_midiDataOut1 356 +#define HDSP_fifoData 368 +#define HDSP_inputEnable 384 + +/* Read registers. These are defined as byte-offsets from the iobase value + */ + +#define HDSP_statusRegister 0 +#define HDSP_timecode 128 +#define HDSP_status2Register 192 +#define HDSP_midiDataOut0 352 +#define HDSP_midiDataOut1 356 +#define HDSP_midiDataIn0 360 +#define HDSP_midiDataIn1 364 +#define HDSP_midiStatusOut0 384 +#define HDSP_midiStatusOut1 388 +#define HDSP_midiStatusIn0 392 +#define HDSP_midiStatusIn1 396 +#define HDSP_fifoStatus 400 + +/* the meters are regular i/o-mapped registers, but offset + considerably from the rest. the peak registers are reset + when read; the least-significant 4 bits are full-scale counters; + the actual peak value is in the most-significant 24 bits. +*/ + +#define HDSP_playbackPeakLevel 4096 /* 26 * 32 bit values */ +#define HDSP_inputPeakLevel 4224 /* 26 * 32 bit values */ +#define HDSP_outputPeakLevel 4100 /* 26 * 32 bit values */ +#define HDSP_playbackRmsLevel 4612 /* 26 * 64 bit values */ +#define HDSP_inputRmsLevel 4884 /* 26 * 64 bit values */ + +#define HDSP_IO_EXTENT 5192 + +/* jtag register bits */ + +#define HDSP_TMS 0x01 +#define HDSP_TCK 0x02 +#define HDSP_TDI 0x04 +#define HDSP_JTAG 0x08 +#define HDSP_PWDN 0x10 +#define HDSP_PROGRAM 0x020 +#define HDSP_CONFIG_MODE_0 0x040 +#define HDSP_CONFIG_MODE_1 0x080 +#define HDSP_VERSION_BIT 0x100 +#define HDSP_BIGENDIAN_MODE 0x200 +#define HDSP_RD_MULTIPLE 0x400 + +#define HDSP_S_PROGRAM (HDSP_PROGRAM|HDSP_CONFIG_MODE_0) +#define HDSP_S_LOAD (HDSP_PROGRAM|HDSP_CONFIG_MODE_1) + +/* Control Register bits */ + +#define HDSP_Start (1<<0) // start engine +#define HDSP_Latency0 (1<<1) // buffer size = 2^n where n is defined by Latency{2,1,0} +#define HDSP_Latency1 (1<<2) // [ see above ] +#define HDSP_Latency2 (1<<3) // ] see above ] +#define HDSP_ClockModeMaster (1<<4) // 1=Master, 0=Slave/Autosync +#define HDSP_AudioInterruptEnable (1<<5) // what do you think ? +#define HDSP_Frequency0 (1<<6) // 0=44.1kHz/88.2kHz 1=48kHz/96kHz +#define HDSP_Frequency1 (1<<7) // 0=32kHz/64kHz +#define HDSP_DoubleSpeed (1<<8) // 0=normal speed, 1=double speed +#define HDSP_SPDIFProfessional (1<<9) // 0=consumer, 1=professional +#define HDSP_SPDIFEmphasis (1<<10) // 0=none, 1=on +#define HDSP_SPDIFNonAudio (1<<11) // 0=off, 1=on +#define HDSP_SPDIFOpticalOut (1<<12) // 1=use 1st ADAT connector for SPDIF, 0=do not +#define HDSP_SyncRef2 (1<<13) +#define HDSP_SPDIFInputSelect0 (1<<14) +#define HDSP_SPDIFInputSelect1 (1<<15) +#define HDSP_SyncRef0 (1<<16) +#define HDSP_SyncRef1 (1<<17) +#define HDSP_Midi0InterruptEnable (1<<22) +#define HDSP_Midi1InterruptEnable (1<<23) +#define HDSP_LineOut (1<<24) + +#define HDSP_LatencyMask (HDSP_Latency0|HDSP_Latency1|HDSP_Latency2) +#define HDSP_FrequencyMask (HDSP_Frequency0|HDSP_Frequency1|HDSP_DoubleSpeed) + +#define HDSP_SPDIFInputMask (HDSP_SPDIFInputSelect0|HDSP_SPDIFInputSelect1) +#define HDSP_SPDIFInputADAT1 0 +#define HDSP_SPDIFInputCoaxial (HDSP_SPDIFInputSelect1) +#define HDSP_SPDIFInputCDROM (HDSP_SPDIFInputSelect0|HDSP_SPDIFInputSelect1) + +#define HDSP_SyncRefMask (HDSP_SyncRef0|HDSP_SyncRef1|HDSP_SyncRef2) +#define HDSP_SyncRef_ADAT1 0 +#define HDSP_SyncRef_ADAT2 (HDSP_SyncRef0) +#define HDSP_SyncRef_ADAT3 (HDSP_SyncRef1) +#define HDSP_SyncRef_SPDIF (HDSP_SyncRef0|HDSP_SyncRef1) +#define HDSP_SyncRef_WORD (HDSP_SyncRef2) +#define HDSP_SyncRef_ADAT_SYNC (HDSP_SyncRef0|HDSP_SyncRef2) + +/* Preferred sync source choices - used by "sync_pref" control switch */ + +#define HDSP_SYNC_FROM_SELF 0 +#define HDSP_SYNC_FROM_WORD 1 +#define HDSP_SYNC_FROM_ADAT_SYNC 2 +#define HDSP_SYNC_FROM_SPDIF 3 +#define HDSP_SYNC_FROM_ADAT1 4 +#define HDSP_SYNC_FROM_ADAT2 5 +#define HDSP_SYNC_FROM_ADAT3 6 + +/* Possible sources of S/PDIF input */ + +#define HDSP_SPDIFIN_OPTICAL 0 /* optical (ADAT1) */ +#define HDSP_SPDIFIN_COAXIAL 1 /* coaxial (RCA) */ +#define HDSP_SPDIFIN_INTERN 2 /* internal (CDROM) */ + +#define HDSP_Frequency32KHz HDSP_Frequency0 +#define HDSP_Frequency44_1KHz HDSP_Frequency1 +#define HDSP_Frequency48KHz (HDSP_Frequency1|HDSP_Frequency0) +#define HDSP_Frequency64KHz (HDSP_DoubleSpeed|HDSP_Frequency0) +#define HDSP_Frequency88_2KHz (HDSP_DoubleSpeed|HDSP_Frequency1) +#define HDSP_Frequency96KHz (HDSP_DoubleSpeed|HDSP_Frequency1|HDSP_Frequency0) + +#define hdsp_encode_latency(x) (((x)<<1) & HDSP_LatencyMask) +#define hdsp_decode_latency(x) (((x) & HDSP_LatencyMask)>>1) + +#define hdsp_encode_spdif_in(x) (((x)&0x3)<<14) +#define hdsp_decode_spdif_in(x) (((x)>>14)&0x3) + +/* Status Register bits */ + +#define HDSP_audioIRQPending (1<<0) +#define HDSP_Lock2 (1<<1) +#define HDSP_Lock1 (1<<2) +#define HDSP_Lock0 (1<<3) +#define HDSP_SPDIFSync (1<<4) +#define HDSP_TimecodeLock (1<<5) +#define HDSP_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ +#define HDSP_Sync2 (1<<16) +#define HDSP_Sync1 (1<<17) +#define HDSP_Sync0 (1<<18) +#define HDSP_DoubleSpeedStatus (1<<19) +#define HDSP_ConfigError (1<<20) +#define HDSP_DllError (1<<21) +#define HDSP_spdifFrequency0 (1<<22) +#define HDSP_spdifFrequency1 (1<<23) +#define HDSP_spdifFrequency2 (1<<24) +#define HDSP_SPDIFErrorFlag (1<<25) +#define HDSP_BufferID (1<<26) +#define HDSP_TimecodeSync (1<<27) +#define HDSP_CIN (1<<28) +#define HDSP_midi0IRQPending (1<<30) /* notice the gap at bit 29 */ +#define HDSP_midi1IRQPending (1<<31) + +#define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2) + +#define HDSP_spdifFrequency32KHz (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2) +#define HDSP_spdifFrequency44_1KHz (HDSP_spdifFrequency2|HDSP_spdifFrequency1) +#define HDSP_spdifFrequency48KHz (HDSP_spdifFrequency0|HDSP_spdifFrequency2) + +#define HDSP_spdifFrequency64KHz 0 +#define HDSP_spdifFrequency88_2KHz (HDSP_spdifFrequency2) +#define HDSP_spdifFrequency96KHz (HDSP_spdifFrequency0|HDSP_spdifFrequency1) + +/* Status2 Register bits */ + +#define HDSP_version0 (1<<0) +#define HDSP_version1 (1<<1) +#define HDSP_version2 (1<<2) +#define HDSP_wc_lock (1<<3) +#define HDSP_wc_sync (1<<4) +#define HDSP_inp_freq0 (1<<5) +#define HDSP_inp_freq1 (1<<6) +#define HDSP_inp_freq2 (1<<7) +#define HDSP_SelSyncRef0 (1<<8) +#define HDSP_SelSyncRef1 (1<<9) +#define HDSP_SelSyncRef2 (1<<10) + +#define HDSP_wc_valid (HDSP_wc_lock|HDSP_wc_sync) + +#define HDSP_systemFrequencyMask (HDSP_inp_freq0|HDSP_inp_freq1|HDSP_inp_freq2) +#define HDSP_systemFrequency32 (HDSP_inp_freq0) +#define HDSP_systemFrequency44_1 (HDSP_inp_freq1) +#define HDSP_systemFrequency48 (HDSP_inp_freq0|HDSP_inp_freq1) +#define HDSP_systemFrequency64 (HDSP_inp_freq2) +#define HDSP_systemFrequency88_2 (HDSP_inp_freq0|HDSP_inp_freq2) +#define HDSP_systemFrequency96 (HDSP_inp_freq1|HDSP_inp_freq2) + +#define HDSP_SelSyncRefMask (HDSP_SelSyncRef0|HDSP_SelSyncRef1|HDSP_SelSyncRef2) +#define HDSP_SelSyncRef_ADAT1 0 +#define HDSP_SelSyncRef_ADAT2 (HDSP_SelSyncRef0) +#define HDSP_SelSyncRef_ADAT3 (HDSP_SelSyncRef1) +#define HDSP_SelSyncRef_SPDIF (HDSP_SelSyncRef0|HDSP_SelSyncRef1) +#define HDSP_SelSyncRef_WORD (HDSP_SelSyncRef2) +#define HDSP_SelSyncRef_ADAT_SYNC (HDSP_SelSyncRef0|HDSP_SelSyncRef2) + +/* FIFO wait times, defined in terms of loops on readl() */ + +#define HDSP_LONG_WAIT 40000 +#define HDSP_SHORT_WAIT 100 + +/* Computing addresses for adjusting gains */ + +#define INPUT_TO_OUTPUT_KEY(in,out) ((64 * (out)) + (in)) +#define PLAYBACK_TO_OUTPUT_KEY(chn,out) ((64 * (out)) + 32 + (chn)) +#define UNITY_GAIN 32768 +#define MINUS_INFINITY_GAIN 0 + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 +#endif + +/* the size of a substream (1 mono data stream) */ + +#define HDSP_CHANNEL_BUFFER_SAMPLES (16*1024) +#define HDSP_CHANNEL_BUFFER_BYTES (4*HDSP_CHANNEL_BUFFER_SAMPLES) + +/* the size of the area we need to allocate for DMA transfers. the + size is the same regardless of the number of channels - the + Multiface still uses the same memory area. + + Note that we allocate 1 more channel than is apparently needed + because the h/w seems to write 1 byte beyond the end of the last + page. Sigh. +*/ + +#define HDSP_DMA_AREA_BYTES ((HDSP_MAX_CHANNELS+1) * HDSP_CHANNEL_BUFFER_BYTES) +#define HDSP_DMA_AREA_KILOBYTES (HDSP_DMA_AREA_BYTES/1024) + +#define HDSP_MATRIX_MIXER_SIZE 2048 + +typedef struct _hdsp hdsp_t; +typedef struct _hdsp_midi hdsp_midi_t; + +struct _hdsp_midi { + hdsp_t *hdsp; + int id; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; + char istimer; /* timer in use */ + struct timer_list timer; + spinlock_t lock; +}; + +struct _hdsp { + spinlock_t lock; + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback_substream; + hdsp_midi_t midi[2]; + int precise_ptr; + u32 control_register; /* cached value */ + u32 creg_spdif; + u32 creg_spdif_stream; + char *card_name; /* digiface/multiface */ + HDSP_Type type; /* ditto, but for code use */ + size_t period_bytes; /* guess what this is */ + unsigned char ds_channels; + unsigned char ss_channels; /* different for multiface/digiface */ + void *capture_buffer_unaligned; /* original buffer addresses */ + void *playback_buffer_unaligned; /* original buffer addresses */ + unsigned char *capture_buffer; /* suitably aligned address */ + unsigned char *playback_buffer; /* suitably aligned address */ + dma_addr_t capture_buffer_addr; + dma_addr_t playback_buffer_addr; + pid_t capture_pid; + pid_t playback_pid; + int running; + int passthru; /* non-zero if doing pass-thru */ + int last_spdif_sample_rate; /* so that we can catch externally ... */ + int last_adat_sample_rate; /* ... induced rate changes */ + char *channel_map; + int dev; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + snd_card_t *card; + snd_pcm_t *pcm; + struct pci_dev *pci; + snd_kcontrol_t *spdif_ctl; + unsigned short mixer_matrix[HDSP_MATRIX_MIXER_SIZE]; +}; + +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refer to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_df_ss[HDSP_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25 +}; + +static char channel_map_mf_ss[HDSP_MAX_CHANNELS] = { /* Multiface */ + /* ADAT 0 */ + 0, 1, 2, 3, 4, 5, 6, 7, + /* ADAT 2 */ + 16, 17, 18, 19, 20, 21, 22, 23, + /* SPDIF */ + 24, 25, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_ds[HDSP_MAX_CHANNELS] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, + /* channels 12 and 13 are S/PDIF */ + 24, 25, + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#define HDSP_PREALLOCATE_MEMORY /* via module snd-hdsp_mem */ + +#ifdef HDSP_PREALLOCATE_MEMORY +extern void *snd_hammerfall_get_buffer(struct pci_dev *, dma_addr_t *dmaaddr); +extern void snd_hammerfall_free_buffer(struct pci_dev *, void *ptr); +#endif + +static struct pci_device_id snd_hdsp_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_XILINX, + .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, /* RME Hammerfall-DSP */ + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, snd_hdsp_ids); + +static inline void hdsp_write(hdsp_t *hdsp, int reg, int val) +{ + writel(val, hdsp->iobase + reg); +} + +static inline unsigned int hdsp_read(hdsp_t *hdsp, int reg) +{ + return readl (hdsp->iobase + reg); +} + +static inline unsigned long long hdsp_read64 (hdsp_t *hdsp, int reg) +{ + unsigned long long val; + val = hdsp_read(hdsp, reg); + val = (val<<32)|hdsp_read(hdsp, reg + 4); + + return val; +} + +static inline int hdsp_check_for_iobox (hdsp_t *hdsp) +{ + if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) { + snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n"); + return -EIO; + } + return 0; +} + +static inline int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout) +{ + int i; + + /* the fifoStatus registers reports on how many words + are available in the command FIFO. + */ + + for (i = 0; i < timeout; i++) + if ((int)(hdsp_read (hdsp, HDSP_fifoStatus) & 0xff) <= count) + return 0; + + snd_printk ("wait for FIFO status <= %d failed after %d iterations\n", + count, timeout); + return -1; +} + +static inline int hdsp_read_gain (hdsp_t *hdsp, unsigned int addr) +{ + if (addr >= HDSP_MATRIX_MIXER_SIZE) { + return 0; + } + return hdsp->mixer_matrix[addr]; +} + +static inline int hdsp_write_gain(hdsp_t *hdsp, unsigned int addr, unsigned short data) +{ + unsigned int ad; + + if (addr >= HDSP_MATRIX_MIXER_SIZE) + return -1; + + ad = data + addr * 65536; + + if (hdsp_fifo_wait(hdsp, 127, HDSP_LONG_WAIT)) { + return -1; + } + hdsp_write (hdsp, HDSP_fifoData, ad); + hdsp->mixer_matrix[addr] = data; + + return 0; +} + +static inline int snd_hdsp_use_is_exclusive(hdsp_t *hdsp) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&hdsp->lock, flags); + if ((hdsp->playback_pid != hdsp->capture_pid) && + (hdsp->playback_pid >= 0) && (hdsp->capture_pid >= 0)) { + ret = 0; + } + spin_unlock_irqrestore(&hdsp->lock, flags); + return ret; +} + +static inline int hdsp_system_sample_rate (hdsp_t *hdsp) +{ + unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); + unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; + + switch (rate_bits) { + case HDSP_systemFrequency32: return 32000; + case HDSP_systemFrequency44_1: return 44100; + case HDSP_systemFrequency48: return 48000; + case HDSP_systemFrequency64: return 64000; + case HDSP_systemFrequency88_2: return 88200; + case HDSP_systemFrequency96: return 96000; + default: return 0; + } +} + +static inline int hdsp_spdif_sample_rate(hdsp_t *hdsp) +{ + unsigned int status = hdsp_read(hdsp, HDSP_statusRegister); + unsigned int rate_bits = (status & HDSP_spdifFrequencyMask); + + if (status & HDSP_SPDIFErrorFlag) { + return 0; + } + + switch (rate_bits) { + case HDSP_spdifFrequency32KHz: return 32000; + case HDSP_spdifFrequency44_1KHz: return 44100; + case HDSP_spdifFrequency48KHz: return 48000; + case HDSP_spdifFrequency64KHz: return 64000; + case HDSP_spdifFrequency88_2KHz: return 88200; + case HDSP_spdifFrequency96KHz: return 96000; + default: + snd_printk ("unknown frequency status; bits = 0x%x, status = 0x%x", rate_bits, status); + return 0; + } +} + +static inline void hdsp_compute_period_size(hdsp_t *hdsp) +{ + hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8)); +} + +static snd_pcm_uframes_t hdsp_hw_pointer(hdsp_t *hdsp) +{ + int position; + + position = hdsp_read(hdsp, HDSP_statusRegister); + + if (!hdsp->precise_ptr) { + return (position & HDSP_BufferID) ? (hdsp->period_bytes / 4) : 0; + } + + position &= HDSP_BufferPositionMask; + position /= 4; + position -= 32; + position &= (HDSP_CHANNEL_BUFFER_SAMPLES-1); + return position; +} + +static inline void hdsp_reset_hw_pointer(hdsp_t *hdsp) +{ +#if 0 + /* reset the hw pointer to zero. We do this by writing to 8 + registers, each of which is a 32bit wide register, and set + them all to zero. + */ + + for (i = 0; i < 8; ++i) { + hdsp_write(hdsp, i, 0); + udelay(10); + } +#endif +} + +static inline void hdsp_start_audio(hdsp_t *s) +{ + s->control_register |= (HDSP_AudioInterruptEnable | HDSP_Start); + hdsp_write(s, HDSP_controlRegister, s->control_register); +} + +static inline void hdsp_stop_audio(hdsp_t *s) +{ + s->control_register &= ~(HDSP_Start | HDSP_AudioInterruptEnable); + hdsp_write(s, HDSP_controlRegister, s->control_register); +} + +static inline void hdsp_silence_playback(hdsp_t *hdsp) +{ + memset(hdsp->playback_buffer, 0, HDSP_DMA_AREA_BYTES); +} + +static int hdsp_set_interrupt_interval(hdsp_t *s, unsigned int frames) +{ + int n; + + spin_lock_irq(&s->lock); + + frames >>= 7; + n = 0; + while (frames) { + n++; + frames >>= 1; + } + + s->control_register &= ~HDSP_LatencyMask; + s->control_register |= hdsp_encode_latency(n); + + hdsp_write(s, HDSP_controlRegister, s->control_register); + + hdsp_compute_period_size(s); + + spin_unlock_irq(&s->lock); + + return 0; +} + +static int hdsp_set_rate(hdsp_t *hdsp, int rate) +{ + int reject_if_open = 0; + int current_rate; + + if (!(hdsp->control_register & HDSP_ClockModeMaster)) { + snd_printk ("device is not running as a clock master: cannot set sample rate.\n"); + return -1; + } + + /* Changing from a "single speed" to a "double speed" rate is + not allowed if any substreams are open. This is because + such a change causes a shift in the location of + the DMA buffers and a reduction in the number of available + buffers. + + Note that a similar but essentially insoluble problem + exists for externally-driven rate changes. All we can do + is to flag rate changes in the read/write routines. + */ + + spin_lock_irq(&hdsp->lock); + current_rate = hdsp_system_sample_rate(hdsp); + + switch (rate) { + case 32000: + if (current_rate > 48000) { + reject_if_open = 1; + } + rate = HDSP_Frequency32KHz; + break; + case 44100: + if (current_rate > 48000) { + reject_if_open = 1; + } + rate = HDSP_Frequency44_1KHz; + break; + case 48000: + if (current_rate > 48000) { + reject_if_open = 1; + } + rate = HDSP_Frequency48KHz; + break; + case 64000: + if (current_rate < 48000) { + reject_if_open = 1; + } + rate = HDSP_Frequency64KHz; + break; + case 88200: + if (current_rate < 48000) { + reject_if_open = 1; + } + rate = HDSP_Frequency88_2KHz; + break; + case 96000: + if (current_rate < 48000) { + reject_if_open = 1; + } + rate = HDSP_Frequency96KHz; + break; + default: + spin_unlock_irq(&hdsp->lock); + return -EINVAL; + } + + if (reject_if_open && (hdsp->capture_pid >= 0 || hdsp->playback_pid >= 0)) { + snd_printk ("cannot change between single- and double-speed mode (capture PID = %d, playback PID = %d)\n", + hdsp->capture_pid, + hdsp->playback_pid); + spin_unlock_irq(&hdsp->lock); + return -EBUSY; + } + + hdsp->control_register &= ~HDSP_FrequencyMask; + hdsp->control_register |= rate; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + + if (rate > 48000) { + hdsp->channel_map = channel_map_ds; + } else { + switch (hdsp->type) { + case Multiface: + hdsp->channel_map = channel_map_mf_ss; + break; + case Digiface: + hdsp->channel_map = channel_map_df_ss; + break; + } + } + + spin_unlock_irq(&hdsp->lock); + return 0; +} + +static void hdsp_set_thru(hdsp_t *hdsp, int channel, int enable) +{ + + hdsp->passthru = 0; + + if (channel < 0) { + + int i; + + /* set thru for all channels */ + + if (enable) { + for (i = 0; i < 26; i++) { + hdsp_write_gain (hdsp, INPUT_TO_OUTPUT_KEY(i,i), UNITY_GAIN); + } + } else { + for (i = 0; i < 26; i++) { + hdsp_write_gain (hdsp, INPUT_TO_OUTPUT_KEY(i,i), MINUS_INFINITY_GAIN); + } + } + + } else { + int mapped_channel; + + snd_assert(channel < HDSP_MAX_CHANNELS, return); + + mapped_channel = hdsp->channel_map[channel]; + + snd_assert(mapped_channel > -1, return); + + if (enable) { + hdsp_write_gain (hdsp, INPUT_TO_OUTPUT_KEY(mapped_channel,mapped_channel), UNITY_GAIN); + } else { + hdsp_write_gain (hdsp, INPUT_TO_OUTPUT_KEY(mapped_channel,mapped_channel), MINUS_INFINITY_GAIN); + } + } +} + +static int hdsp_set_passthru(hdsp_t *hdsp, int onoff) +{ + if (onoff) { + hdsp_set_thru(hdsp, -1, 1); + hdsp_reset_hw_pointer(hdsp); + hdsp_silence_playback(hdsp); + + /* we don't want interrupts, so do a + custom version of hdsp_start_audio(). + */ + + hdsp->control_register |= (HDSP_Start|HDSP_AudioInterruptEnable|hdsp_encode_latency(7)); + + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + hdsp->passthru = 1; + } else { + hdsp_set_thru(hdsp, -1, 0); + hdsp_stop_audio(hdsp); + hdsp->passthru = 0; + } + + return 0; +} + +/*---------------------------------------------------------------------------- + MIDI + ----------------------------------------------------------------------------*/ + +static inline unsigned char snd_hdsp_midi_read_byte (hdsp_t *hdsp, int id) +{ + /* the hardware already does the relevant bit-mask with 0xff */ + if (id) { + return hdsp_read(hdsp, HDSP_midiDataIn1); + } else { + return hdsp_read(hdsp, HDSP_midiDataIn0); + } +} + +static inline void snd_hdsp_midi_write_byte (hdsp_t *hdsp, int id, int val) +{ + /* the hardware already does the relevant bit-mask with 0xff */ + if (id) { + return hdsp_write(hdsp, HDSP_midiDataOut1, val); + } else { + return hdsp_write(hdsp, HDSP_midiDataOut0, val); + } +} + +static inline int snd_hdsp_midi_input_available (hdsp_t *hdsp, int id) +{ + if (id) { + return (hdsp_read(hdsp, HDSP_midiStatusIn1) & 0xff); + } else { + return (hdsp_read(hdsp, HDSP_midiStatusIn0) & 0xff); + } +} + +static inline int snd_hdsp_midi_output_possible (hdsp_t *hdsp, int id) +{ + int fifo_bytes_used; + + if (id) { + fifo_bytes_used = hdsp_read(hdsp, HDSP_midiStatusOut1) & 0xff; + } else { + fifo_bytes_used = hdsp_read(hdsp, HDSP_midiStatusOut0) & 0xff; + } + + if (fifo_bytes_used < 128) { + return 128 - fifo_bytes_used; + } else { + return 0; + } +} + +static inline void snd_hdsp_flush_midi_input (hdsp_t *hdsp, int id) +{ + while (snd_hdsp_midi_input_available (hdsp, id)) { + snd_hdsp_midi_read_byte (hdsp, id); + } +} + +static int snd_hdsp_midi_output_write (hdsp_midi_t *hmidi) +{ + unsigned long flags; + int n_pending; + int to_write; + int i; + unsigned char buf[128]; + + /* Output is not interrupt driven */ + + spin_lock_irqsave (&hmidi->lock, flags); + + if (hmidi->output) { + if (!snd_rawmidi_transmit_empty (hmidi->output)) { + if ((n_pending = snd_hdsp_midi_output_possible (hmidi->hdsp, hmidi->id)) > 0) { + if (n_pending > (int)sizeof (buf)) + n_pending = sizeof (buf); + + if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) { + for (i = 0; i < to_write; ++i) + snd_hdsp_midi_write_byte (hmidi->hdsp, hmidi->id, buf[i]); + } + } + } + } + + spin_unlock_irqrestore (&hmidi->lock, flags); + return 0; +} + +static int snd_hdsp_midi_input_read (hdsp_midi_t *hmidi) +{ + unsigned char buf[128]; /* this buffer is designed to match the MIDI input FIFO size */ + unsigned long flags; + int n_pending; + int i; + + spin_lock_irqsave (&hmidi->lock, flags); + + if ((n_pending = snd_hdsp_midi_input_available (hmidi->hdsp, hmidi->id)) > 0) { + if (hmidi->input) { + if (n_pending > (int)sizeof (buf)) { + n_pending = sizeof (buf); + } + for (i = 0; i < n_pending; ++i) { + buf[i] = snd_hdsp_midi_read_byte (hmidi->hdsp, hmidi->id); + } + if (n_pending) { + snd_rawmidi_receive (hmidi->input, buf, n_pending); + } + } else { + /* flush the MIDI input FIFO */ + while (--n_pending) { + snd_hdsp_midi_read_byte (hmidi->hdsp, hmidi->id); + } + } + } + spin_unlock_irqrestore (&hmidi->lock, flags); + return snd_hdsp_midi_output_write (hmidi); +} + +static void snd_hdsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + hdsp_t *hdsp; + hdsp_midi_t *hmidi; + unsigned long flags; + + hmidi = (hdsp_midi_t *) substream->rmidi->private_data; + hdsp = hmidi->hdsp; + spin_lock_irqsave (&hdsp->lock, flags); + if (up) { + snd_hdsp_flush_midi_input (hdsp, hmidi->id); + if (hmidi->id) + hdsp->control_register |= HDSP_Midi1InterruptEnable; + else + hdsp->control_register |= HDSP_Midi0InterruptEnable; + } else { + if (hmidi->id) + hdsp->control_register &= ~HDSP_Midi1InterruptEnable; + else + hdsp->control_register &= ~HDSP_Midi0InterruptEnable; + } + + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + spin_unlock_irqrestore (&hdsp->lock, flags); +} + +static void snd_hdsp_midi_output_timer(unsigned long data) +{ + hdsp_midi_t *hmidi = (hdsp_midi_t *) data; + unsigned long flags; + + snd_hdsp_midi_output_write(hmidi); + spin_lock_irqsave (&hmidi->lock, flags); + + /* this does not bump hmidi->istimer, because the + kernel automatically removed the timer when it + expired, and we are now adding it back, thus + leaving istimer wherever it was set before. + */ + + if (hmidi->istimer) { + hmidi->timer.expires = 1 + jiffies; + add_timer(&hmidi->timer); + } + + spin_unlock_irqrestore (&hmidi->lock, flags); +} + +static void snd_hdsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + hdsp_midi_t *hmidi; + unsigned long flags; + + hmidi = (hdsp_midi_t *) substream->rmidi->private_data; + spin_lock_irqsave (&hmidi->lock, flags); + if (up) { + if (!hmidi->istimer) { + init_timer(&hmidi->timer); + hmidi->timer.function = snd_hdsp_midi_output_timer; + hmidi->timer.data = (unsigned long) hmidi; + hmidi->timer.expires = 1 + jiffies; + add_timer(&hmidi->timer); + hmidi->istimer++; + } + } else { + if (hmidi->istimer && --hmidi->istimer <= 0) { + del_timer (&hmidi->timer); + } + } + spin_unlock_irqrestore (&hmidi->lock, flags); + if (up) + snd_hdsp_midi_output_write(hmidi); +} + +static int snd_hdsp_midi_input_open(snd_rawmidi_substream_t * substream) +{ + hdsp_midi_t *hmidi; + unsigned long flags; + + hmidi = (hdsp_midi_t *) substream->rmidi->private_data; + spin_lock_irqsave (&hmidi->lock, flags); + snd_hdsp_flush_midi_input (hmidi->hdsp, hmidi->id); + hmidi->input = substream; + spin_unlock_irqrestore (&hmidi->lock, flags); + + return 0; +} + +static int snd_hdsp_midi_output_open(snd_rawmidi_substream_t * substream) +{ + hdsp_midi_t *hmidi; + unsigned long flags; + + hmidi = (hdsp_midi_t *) substream->rmidi->private_data; + spin_lock_irqsave (&hmidi->lock, flags); + hmidi->output = substream; + spin_unlock_irqrestore (&hmidi->lock, flags); + + return 0; +} + +static int snd_hdsp_midi_input_close(snd_rawmidi_substream_t * substream) +{ + hdsp_midi_t *hmidi; + unsigned long flags; + + snd_hdsp_midi_input_trigger (substream, 0); + + hmidi = (hdsp_midi_t *) substream->rmidi->private_data; + spin_lock_irqsave (&hmidi->lock, flags); + hmidi->input = NULL; + spin_unlock_irqrestore (&hmidi->lock, flags); + + return 0; +} + +static int snd_hdsp_midi_output_close(snd_rawmidi_substream_t * substream) +{ + hdsp_midi_t *hmidi; + unsigned long flags; + + snd_hdsp_midi_output_trigger (substream, 0); + + hmidi = (hdsp_midi_t *) substream->rmidi->private_data; + spin_lock_irqsave (&hmidi->lock, flags); + hmidi->output = NULL; + spin_unlock_irqrestore (&hmidi->lock, flags); + + return 0; +} + +snd_rawmidi_ops_t snd_hdsp_midi_output = +{ + .open = snd_hdsp_midi_output_open, + .close = snd_hdsp_midi_output_close, + .trigger = snd_hdsp_midi_output_trigger, +}; + +snd_rawmidi_ops_t snd_hdsp_midi_input = +{ + .open = snd_hdsp_midi_input_open, + .close = snd_hdsp_midi_input_close, + .trigger = snd_hdsp_midi_input_trigger, +}; + +static int __devinit snd_hdsp_create_midi (snd_card_t *card, hdsp_t *hdsp, int id) +{ + char buf[32]; + + hdsp->midi[id].id = id; + hdsp->midi[id].rmidi = NULL; + hdsp->midi[id].input = NULL; + hdsp->midi[id].output = NULL; + hdsp->midi[id].hdsp = hdsp; + hdsp->midi[id].istimer = 0; + spin_lock_init (&hdsp->midi[id].lock); + + sprintf (buf, "%s MIDI %d", card->shortname, id+1); + if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0) { + return -1; + } + + sprintf (hdsp->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1); + hdsp->midi[id].rmidi->private_data = &hdsp->midi[id]; + + snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdsp_midi_output); + snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdsp_midi_input); + + hdsp->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} + +/*----------------------------------------------------------------------------- + Control Interface + ----------------------------------------------------------------------------*/ + +static u32 snd_hdsp_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? HDSP_SPDIFProfessional : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? HDSP_SPDIFNonAudio : 0; + if (val & HDSP_SPDIFProfessional) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? HDSP_SPDIFEmphasis : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? HDSP_SPDIFEmphasis : 0; + return val; +} + +static void snd_hdsp_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & HDSP_SPDIFProfessional) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & HDSP_SPDIFNonAudio) ? IEC958_AES0_NONAUDIO : 0); + if (val & HDSP_SPDIFProfessional) + aes->status[0] |= (val & HDSP_SPDIFEmphasis) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & HDSP_SPDIFEmphasis) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_hdsp_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_hdsp_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + + snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif); + return 0; +} + +static int snd_hdsp_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&hdsp->lock, flags); + change = val != hdsp->creg_spdif; + hdsp->creg_spdif = val; + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +static int snd_hdsp_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_hdsp_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + + snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif_stream); + return 0; +} + +static int snd_hdsp_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&hdsp->lock, flags); + change = val != hdsp->creg_spdif_stream; + hdsp->creg_spdif_stream = val; + hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis); + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register |= val); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +static int snd_hdsp_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_hdsp_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +#define HDSP_SPDIF_IN(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_spdif_in, \ + .get = snd_hdsp_get_spdif_in, .put = snd_hdsp_put_spdif_in } + +static unsigned int hdsp_spdif_in(hdsp_t *hdsp) +{ + return hdsp_decode_spdif_in(hdsp->control_register & HDSP_SPDIFInputMask); +} + +static int hdsp_set_spdif_input(hdsp_t *hdsp, int in) +{ + hdsp->control_register &= ~HDSP_SPDIFInputMask; + hdsp->control_register |= hdsp_encode_spdif_in(in); + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +static int snd_hdsp_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"ADAT1", "Coaxial", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_hdsp_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.enumerated.item[0] = hdsp_spdif_in(hdsp); + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&hdsp->lock, flags); + change = val != hdsp_spdif_in(hdsp); + if (change) + hdsp_set_spdif_input(hdsp, val); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +#define HDSP_SPDIF_OUT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_spdif_out, \ + .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out } + +static int hdsp_spdif_out(hdsp_t *hdsp) +{ + return (hdsp->control_register & HDSP_SPDIFOpticalOut) ? 1 : 0; +} + +static int hdsp_set_spdif_output(hdsp_t *hdsp, int out) +{ + if (out) { + hdsp->control_register |= HDSP_SPDIFOpticalOut; + } else { + hdsp->control_register &= ~HDSP_SPDIFOpticalOut; + } + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +static int snd_hdsp_info_spdif_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdsp_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp); + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&hdsp->lock, flags); + change = (int)val != hdsp_spdif_out(hdsp); + hdsp_set_spdif_output(hdsp, val); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +#define HDSP_SYNC_PREF(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_sync_pref, \ + .get = snd_hdsp_get_sync_pref, .put = snd_hdsp_put_sync_pref } + +static int hdsp_sync_pref(hdsp_t *hdsp) +{ + /* Notice that this looks at the requested sync source, + not the one actually in use. + */ + + if (hdsp->control_register & HDSP_ClockModeMaster) { + return HDSP_SYNC_FROM_SELF; + } + + switch (hdsp->control_register & HDSP_SyncRefMask) { + case HDSP_SyncRef_ADAT1: + return HDSP_SYNC_FROM_ADAT1; + case HDSP_SyncRef_ADAT2: + return HDSP_SYNC_FROM_ADAT2; + case HDSP_SyncRef_ADAT3: + return HDSP_SYNC_FROM_ADAT3; + case HDSP_SyncRef_SPDIF: + return HDSP_SYNC_FROM_SPDIF; + case HDSP_SyncRef_WORD: + return HDSP_SYNC_FROM_WORD; + case HDSP_SyncRef_ADAT_SYNC: + return HDSP_SYNC_FROM_ADAT_SYNC; + default: + return HDSP_SYNC_FROM_SELF; + } + return 0; +} + +static int hdsp_set_sync_pref(hdsp_t *hdsp, int pref) +{ + hdsp->control_register &= ~HDSP_SyncRefMask; + switch (pref) { + case HDSP_SYNC_FROM_ADAT1: + hdsp->control_register &= ~HDSP_ClockModeMaster; + hdsp->control_register &= ~HDSP_SyncRefMask; /* clear SyncRef bits */ + break; + case HDSP_SYNC_FROM_ADAT2: + hdsp->control_register &= ~HDSP_ClockModeMaster; + hdsp->control_register |= HDSP_SyncRef_ADAT2; + break; + case HDSP_SYNC_FROM_ADAT3: + hdsp->control_register &= ~HDSP_ClockModeMaster; + hdsp->control_register |= HDSP_SyncRef_ADAT3; + break; + case HDSP_SYNC_FROM_SPDIF: + hdsp->control_register &= ~HDSP_ClockModeMaster; + hdsp->control_register |= HDSP_SyncRef_SPDIF; + break; + case HDSP_SYNC_FROM_WORD: + hdsp->control_register &= ~HDSP_ClockModeMaster; + hdsp->control_register |= HDSP_SyncRef_WORD; + break; + case HDSP_SYNC_FROM_ADAT_SYNC: + hdsp->control_register &= ~HDSP_ClockModeMaster; + hdsp->control_register |= HDSP_SyncRef_ADAT_SYNC; + break; + case HDSP_SYNC_FROM_SELF: + hdsp->control_register |= HDSP_ClockModeMaster; + break; + default: + return -1; + } + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +static int snd_hdsp_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = {"Internal", "Word", "ADAT Sync", "IEC958", "ADAT1", "ADAT2", "ADAT3" }; + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = (hdsp->type == Digiface) ? 7 : 6; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_hdsp_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.enumerated.item[0] = hdsp_sync_pref(hdsp); + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, max; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + max = hdsp->ss_channels == (hdsp->type == Digiface) ? 7 : 6; + val = ucontrol->value.enumerated.item[0] % max; + spin_lock_irqsave(&hdsp->lock, flags); + change = (int)val != hdsp_sync_pref(hdsp); + hdsp_set_sync_pref(hdsp, val); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +#define HDSP_PASSTHRU(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_passthru, \ + .put = snd_hdsp_put_passthru, \ + .get = snd_hdsp_get_passthru } + +static int snd_hdsp_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdsp_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.integer.value[0] = hdsp->passthru; + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + int err = 0; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&hdsp->lock, flags); + change = (ucontrol->value.integer.value[0] != hdsp->passthru); + if (change) + err = hdsp_set_passthru(hdsp, val); + spin_unlock_irqrestore(&hdsp->lock, flags); + return err ? err : change; +} + +#define HDSP_LINE_OUT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_line_out, \ + .get = snd_hdsp_get_line_out, .put = snd_hdsp_put_line_out } + +static int hdsp_line_out(hdsp_t *hdsp) +{ + return (hdsp->control_register & HDSP_LineOut) ? 1 : 0; +} + +static int hdsp_set_line_output(hdsp_t *hdsp, int out) +{ + if (out) { + hdsp->control_register |= HDSP_LineOut; + } else { + hdsp->control_register &= ~HDSP_LineOut; + } + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +static int snd_hdsp_info_line_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdsp_get_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.integer.value[0] = hdsp_line_out(hdsp); + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&hdsp->lock, flags); + change = (int)val != hdsp_line_out(hdsp); + hdsp_set_line_output(hdsp, val); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +#define HDSP_MIXER(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_mixer, \ + .get = snd_hdsp_get_mixer, .put = snd_hdsp_put_mixer } + +static int snd_hdsp_info_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65536; + uinfo->value.integer.step = 1; + return 0; +} + +static int snd_hdsp_get_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int source; + int destination; + int addr; + + source = ucontrol->value.integer.value[0]; + destination = ucontrol->value.integer.value[1]; + + if (source > 25) { + addr = PLAYBACK_TO_OUTPUT_KEY(source-26,destination); + } else { + addr = INPUT_TO_OUTPUT_KEY(source, destination); + } + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.integer.value[0] = hdsp_read_gain (hdsp, addr); + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + int source; + int destination; + int gain; + int addr; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + + source = ucontrol->value.integer.value[0]; + destination = ucontrol->value.integer.value[1]; + + if (source > 25) { + addr = PLAYBACK_TO_OUTPUT_KEY(source-26, destination); + } else { + addr = INPUT_TO_OUTPUT_KEY(source, destination); + } + + gain = ucontrol->value.integer.value[2]; + + spin_lock_irqsave(&hdsp->lock, flags); + change = gain != hdsp_read_gain(hdsp, addr); + if (change) + hdsp_write_gain(hdsp, addr, gain); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +/* The simple mixer control(s) provide gain control for the + basic 1:1 mappings of playback streams to output + streams. +*/ + +#define HDSP_PLAYBACK_MIXER \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdsp_info_playback_mixer, \ + .get = snd_hdsp_get_playback_mixer, .put = snd_hdsp_put_playback_mixer } + +static int snd_hdsp_info_playback_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65536; + uinfo->value.integer.step = 1; + return 0; +} + +static int snd_hdsp_get_playback_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr; + int channel; + int mapped_channel; + + channel = ucontrol->id.index - 1; + + snd_assert(channel >= 0 || channel < HDSP_MAX_CHANNELS, return -EINVAL); + + if ((mapped_channel = hdsp->channel_map[channel]) < 0) { + return -EINVAL; + } + + addr = PLAYBACK_TO_OUTPUT_KEY(mapped_channel, mapped_channel); + + spin_lock_irqsave(&hdsp->lock, flags); + ucontrol->value.integer.value[0] = hdsp_read_gain (hdsp, addr); + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static int snd_hdsp_put_playback_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + int addr; + int channel; + int mapped_channel; + int gain; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + + channel = ucontrol->id.index - 1; + + snd_assert(channel >= 0 || channel < HDSP_MAX_CHANNELS, return -EINVAL); + + if ((mapped_channel = hdsp->channel_map[channel]) < 0) { + return -EINVAL; + } + + addr = PLAYBACK_TO_OUTPUT_KEY(mapped_channel, mapped_channel); + gain = ucontrol->value.integer.value[0]; + + + spin_lock_irqsave(&hdsp->lock, flags); + change = gain != hdsp_read_gain(hdsp, addr); + if (change) + hdsp_write_gain(hdsp, addr, gain); + spin_unlock_irqrestore(&hdsp->lock, flags); + return change; +} + +#define HDSP_PEAK_PLAYBACK \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdsp_info_peak_playback, \ + .get = snd_hdsp_get_peak_playback \ +} + +static int snd_hdsp_info_peak_playback(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + return 0; +} + +static int snd_hdsp_get_peak_playback(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned int peakval = hdsp_read (hdsp, HDSP_playbackPeakLevel + (4 * (ucontrol->id.index-1))); + ucontrol->value.integer.value[0] = peakval & 0xffffff00; /* peak */ + ucontrol->value.integer.value[1] = peakval & 0xf; /* overs */ + return 0; +} + +#define HDSP_PEAK_INPUT \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdsp_info_peak_input, \ + .get = snd_hdsp_get_peak_input \ +} + +static int snd_hdsp_info_peak_input(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + return 0; +} + +static int snd_hdsp_get_peak_input(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned int peakval = hdsp_read (hdsp, HDSP_inputPeakLevel + (4 * (ucontrol->id.index-1))); + ucontrol->value.integer.value[0] = peakval & 0xffffff00; /* peak */ + ucontrol->value.integer.value[1] = peakval & 0xf; /* overs */ + return 0; +} + +#define HDSP_PEAK_OUTPUT \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdsp_info_peak_output, \ + .get = snd_hdsp_get_peak_output \ +} + +static int snd_hdsp_info_peak_output(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + return 0; +} + +static int snd_hdsp_get_peak_output(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + unsigned int peakval = hdsp_read (hdsp, HDSP_outputPeakLevel + (4 * (ucontrol->id.index-1))); + ucontrol->value.integer.value[0] = peakval & 0xffffff00; /* peak */ + ucontrol->value.integer.value[1] = peakval & 0xf; /* overs */ + return 0; +} + +#define HDSP_RMS_INPUT \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdsp_info_rms_input, \ + .get = snd_hdsp_get_rms_input \ +} + +static int snd_hdsp_info_rms_input(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER64; + uinfo->count = 1; + return 0; +} + +static int snd_hdsp_get_rms_input(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + ucontrol->value.integer64.value[0] = hdsp_read64 (hdsp, HDSP_inputRmsLevel + (8 * (ucontrol->id.index-1))); + return 0; +} + +#define HDSP_RMS_PLAYBACK \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdsp_info_rms_playback, \ + .get = snd_hdsp_get_rms_playback \ +} + +static int snd_hdsp_info_rms_playback(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER64; + uinfo->count = 1; + return 0; +} + +static int snd_hdsp_get_rms_playback(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + ucontrol->value.integer64.value[0] = hdsp_read64 (hdsp, HDSP_playbackRmsLevel + (8 * (ucontrol->id.index-1))); + return 0; +} + +static snd_kcontrol_new_t snd_hdsp_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_hdsp_control_spdif_info, + .get = snd_hdsp_control_spdif_get, + .put = snd_hdsp_control_spdif_put, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_hdsp_control_spdif_stream_info, + .get = snd_hdsp_control_spdif_stream_get, + .put = snd_hdsp_control_spdif_stream_put, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_hdsp_control_spdif_mask_info, + .get = snd_hdsp_control_spdif_mask_get, + .private_value = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_hdsp_control_spdif_mask_info, + .get = snd_hdsp_control_spdif_mask_get, + .private_value = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS, +}, +HDSP_MIXER("Mixer", 0), +HDSP_SPDIF_IN("IEC958 Input Connector", 0), +HDSP_SPDIF_OUT("IEC958 Output also on ADAT1", 0), +HDSP_SYNC_PREF("Preferred Sync Source", 0), +HDSP_PASSTHRU("Passthru", 0), +HDSP_LINE_OUT("Line Out", 0), +}; + +#define HDSP_CONTROLS (sizeof(snd_hdsp_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_hdsp_playback_mixer = HDSP_PLAYBACK_MIXER; +static snd_kcontrol_new_t snd_hdsp_input_peak = HDSP_PEAK_INPUT; +static snd_kcontrol_new_t snd_hdsp_output_peak = HDSP_PEAK_OUTPUT; +static snd_kcontrol_new_t snd_hdsp_playback_peak = HDSP_PEAK_PLAYBACK; +static snd_kcontrol_new_t snd_hdsp_input_rms = HDSP_RMS_INPUT; +static snd_kcontrol_new_t snd_hdsp_playback_rms = HDSP_RMS_PLAYBACK; + +int snd_hdsp_create_controls(snd_card_t *card, hdsp_t *hdsp) +{ + unsigned int idx, limit; + int err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < HDSP_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + hdsp->spdif_ctl = kctl; + } + + if (hdsp->type == Digiface) { + limit = DIGIFACE_SS_CHANNELS; + } else { + limit = MULTIFACE_SS_CHANNELS; + } + + /* The index values are one greater than the channel ID so that alsamixer + will display them correctly. We want to use the index for fast lookup + of the relevant channel, but if we use it at all, most ALSA software + does the wrong thing with it ... + */ + + snd_hdsp_playback_mixer.name = "Chn"; + snd_hdsp_input_peak.name = "Input Peak"; + snd_hdsp_output_peak.name = "Output Peak"; + snd_hdsp_playback_peak.name = "Playback Peak"; + snd_hdsp_playback_rms.name = "Playback RMS"; + snd_hdsp_input_rms.name = "Input RMS"; + + for (idx = 0; idx < limit; ++idx) { + snd_hdsp_playback_mixer.index = idx+1; + if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_playback_mixer, hdsp)))) { + return err; + } + snd_hdsp_input_peak.index = idx+1; + if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_input_peak, hdsp)))) { + return err; + } + snd_hdsp_output_peak.index = idx+1; + if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_output_peak, hdsp)))) { + return err; + } + snd_hdsp_playback_peak.index = idx+1; + if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_playback_peak, hdsp)))) { + return err; + } + snd_hdsp_playback_rms.index = idx+1; + if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_playback_rms, hdsp)))) { + return err; + } + snd_hdsp_input_rms.index = idx+1; + if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_input_rms, hdsp)))) { + return err; + } + } + + return 0; +} + +/*------------------------------------------------------------ + /proc interface + ------------------------------------------------------------*/ + +static void +snd_hdsp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + hdsp_t *hdsp = (hdsp_t *) entry->private_data; + unsigned int status; + unsigned int status2; + char *requested_sync_ref; + int x; + + if (hdsp_check_for_iobox (hdsp)) { + return; + } + + status = hdsp_read(hdsp, HDSP_statusRegister); + status2 = hdsp_read(hdsp, HDSP_status2Register); + + snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1); + snd_iprintf(buffer, "Buffers: capture %p playback %p\n", + hdsp->capture_buffer, hdsp->playback_buffer); + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + hdsp->irq, hdsp->port, hdsp->iobase); + snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register); + snd_iprintf(buffer, "Status register: 0x%x\n", status); + snd_iprintf(buffer, "Status2 register: 0x%x\n", status2); + snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff); + + snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0)); + snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); + snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1)); + snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1)); + + snd_iprintf(buffer, "\n"); + + x = 1 << (6 + hdsp_decode_latency(hdsp->control_register & HDSP_LatencyMask)); + + snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdsp->period_bytes); + snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", hdsp_hw_pointer(hdsp)); + snd_iprintf(buffer, "Passthru: %s\n", hdsp->passthru ? "yes" : "no"); + snd_iprintf(buffer, "Line out: %s\n", (hdsp->control_register & HDSP_LineOut) ? "on" : "off"); + + snd_iprintf(buffer, "Firmware version: %d\n", (status2&HDSP_version0)|(status2&HDSP_version1)<<1|(status2&HDSP_version2)<<2); + + switch (hdsp_sync_pref (hdsp)) { + case HDSP_SYNC_FROM_WORD: + requested_sync_ref = "Word"; + break; + case HDSP_SYNC_FROM_ADAT_SYNC: + requested_sync_ref = "ADAT Sync"; + break; + case HDSP_SYNC_FROM_SPDIF: + requested_sync_ref = "SPDIF"; + break; + case HDSP_SYNC_FROM_ADAT1: + requested_sync_ref = "ADAT1"; + break; + case HDSP_SYNC_FROM_ADAT2: + requested_sync_ref = "ADAT2"; + break; + case HDSP_SYNC_FROM_ADAT3: + requested_sync_ref = "ADAT3"; + break; + case HDSP_SYNC_FROM_SELF: + default: + requested_sync_ref = "Master"; + break; + } + + if ((hdsp->control_register & HDSP_ClockModeMaster)) { + snd_iprintf (buffer, "Sync reference: %s/Master (chosen)\n", requested_sync_ref); + } else if (hdsp_system_sample_rate(hdsp) == 0) { + snd_iprintf (buffer, "Sync reference: %s/Master (forced)\n", requested_sync_ref); + } else { + switch (status2 & HDSP_SelSyncRefMask) { + case HDSP_SelSyncRef_ADAT1: + snd_iprintf (buffer, "Sync reference: %s/ADAT1\n", requested_sync_ref); + break; + case HDSP_SelSyncRef_ADAT2: + snd_iprintf (buffer, "Sync reference: %s/ADAT2\n", requested_sync_ref); + break; + case HDSP_SelSyncRef_ADAT3: + snd_iprintf (buffer, "Sync reference: %s/ADAT3\n", requested_sync_ref); + break; + case HDSP_SelSyncRef_SPDIF: + snd_iprintf (buffer, "Sync reference: %s/SPDIF\n", requested_sync_ref); + break; + case HDSP_SelSyncRef_WORD: + snd_iprintf (buffer, "Sync reference: %s/WORD\n", requested_sync_ref); + break; + case HDSP_SelSyncRef_ADAT_SYNC: + snd_iprintf (buffer, "Sync reference: %s/ADAT Sync\n", requested_sync_ref); + break; + default: + snd_iprintf (buffer, "Sync reference: %s/Master (fallback)\n", requested_sync_ref); + break; + } + } + snd_iprintf (buffer, "Sample rate: %d\n", hdsp_system_sample_rate(hdsp)); + + snd_iprintf(buffer, "\n"); + + switch ((hdsp->control_register & HDSP_SPDIFInputMask) >> 14) { + case HDSP_SPDIFIN_OPTICAL: + snd_iprintf(buffer, "IEC958 input: ADAT1\n"); + break; + case HDSP_SPDIFIN_COAXIAL: + snd_iprintf(buffer, "IEC958 input: Coaxial\n"); + break; + case HDSP_SPDIFIN_INTERN: + snd_iprintf(buffer, "IEC958 input: Internal\n"); + break; + default: + snd_iprintf(buffer, "IEC958 input: ???\n"); + break; + } + + if (hdsp->control_register & HDSP_SPDIFOpticalOut) { + snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n"); + } else { + snd_iprintf(buffer, "IEC958 output: Coaxial only\n"); + } + + if (hdsp->control_register & HDSP_SPDIFProfessional) { + snd_iprintf(buffer, "IEC958 quality: Professional\n"); + } else { + snd_iprintf(buffer, "IEC958 quality: Consumer\n"); + } + + if (hdsp->control_register & HDSP_SPDIFEmphasis) { + snd_iprintf(buffer, "IEC958 emphasis: on\n"); + } else { + snd_iprintf(buffer, "IEC958 emphasis: off\n"); + } + + if (hdsp->control_register & HDSP_SPDIFNonAudio) { + snd_iprintf(buffer, "IEC958 NonAudio: on\n"); + } else { + snd_iprintf(buffer, "IEC958 NonAudio: off\n"); + } + + snd_iprintf(buffer, "\n"); + + if ((x = hdsp_spdif_sample_rate (hdsp)) != 0) { + snd_iprintf (buffer, "IEC958 sample rate: %d\n", x); + } else { + snd_iprintf (buffer, "IEC958 sample rate: Error flag set\n"); + } + + snd_iprintf(buffer, "\n"); + + /* Sync Check */ + x = status & HDSP_Sync0; + if (status & HDSP_Lock0) { + snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT1: No Lock\n"); + } + + x = status & HDSP_Sync1; + if (status & HDSP_Lock1) { + snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT2: No Lock\n"); + } + + if (hdsp->type == Digiface) { + x = status & HDSP_Sync2; + if (status & HDSP_Lock2) { + snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT3: No Lock\n"); + } + } + + snd_iprintf(buffer, "\n"); + +#if 0 + for (x = 0; x < 26; x++) { + unsigned int val = hdsp_read (hdsp, HDSP_inputPeakLevel + (4 * x)); + snd_iprintf (buffer, "%d: input peak = %d overs = %d\n", x, val&0xffffff00, val&0xf); + } +#endif +} + +static void __devinit snd_hdsp_proc_init(hdsp_t *hdsp) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(hdsp->card, "hdsp", &entry)) + snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read); +} + +static void snd_hdsp_free_buffers(hdsp_t *hdsp) +{ + if (hdsp->capture_buffer_unaligned) { +#ifndef HDSP_PREALLOCATE_MEMORY + snd_free_pci_pages(hdsp->pci, + HDSP_DMA_AREA_BYTES, + hdsp->capture_buffer_unaligned, + hdsp->capture_buffer_addr); +#else + snd_hammerfall_free_buffer(hdsp->pci, hdsp->capture_buffer_unaligned); +#endif + } + + if (hdsp->playback_buffer_unaligned) { +#ifndef HDSP_PREALLOCATE_MEMORY + snd_free_pci_pages(hdsp->pci, + HDSP_DMA_AREA_BYTES, + hdsp->playback_buffer_unaligned, + hdsp->playback_buffer_addr); +#else + snd_hammerfall_free_buffer(hdsp->pci, hdsp->playback_buffer_unaligned); +#endif + } +} + +static int __devinit snd_hdsp_initialize_memory(hdsp_t *hdsp) +{ + void *pb, *cb; + dma_addr_t pb_addr, cb_addr; + unsigned long pb_bus, cb_bus; + +#ifndef HDSP_PREALLOCATE_MEMORY + cb = snd_malloc_pci_pages(hdsp->pci, HDSP_DMA_AREA_BYTES, &cb_addr); + pb = snd_malloc_pci_pages(hdsp->pci, HDSP_DMA_AREA_BYTES, &pb_addr); +#else + cb = snd_hammerfall_get_buffer(hdsp->pci, &cb_addr); + pb = snd_hammerfall_get_buffer(hdsp->pci, &pb_addr); +#endif + + if (cb == 0 || pb == 0) { + if (cb) { +#ifdef HDSP_PREALLOCATE_MEMORY + snd_hammerfall_free_buffer(hdsp->pci, cb); +#else + snd_free_pci_pages(hdsp->pci, HDSP_DMA_AREA_BYTES, cb, cb_addr); +#endif + } + if (pb) { +#ifdef HDSP_PREALLOCATE_MEMORY + snd_hammerfall_free_buffer(hdsp->pci, pb); +#else + snd_free_pci_pages(hdsp->pci, HDSP_DMA_AREA_BYTES, pb, pb_addr); +#endif + } + + printk(KERN_ERR "%s: no buffers available\n", hdsp->card_name); + return -ENOMEM; + } + + /* save raw addresses for use when freeing memory later */ + + hdsp->capture_buffer_unaligned = cb; + hdsp->playback_buffer_unaligned = pb; + hdsp->capture_buffer_addr = cb_addr; + hdsp->playback_buffer_addr = pb_addr; + + /* Align to bus-space 64K boundary */ + + cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl; + pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl; + + /* Tell the card where it is */ + + hdsp_write(hdsp, HDSP_inputBufferAddress, cb_bus); + hdsp_write(hdsp, HDSP_outputBufferAddress, pb_bus); + + hdsp->capture_buffer = cb + (cb_bus - cb_addr); + hdsp->playback_buffer = pb + (pb_bus - pb_addr); + + return 0; +} + +static void snd_hdsp_set_defaults(hdsp_t *hdsp) +{ + unsigned int i; + + /* ASSUMPTION: hdsp->lock is either held, or + there is no need to hold it (e.g. during module + initalization). + */ + + /* set defaults: + + SPDIF Input via Coax + Master clock mode + maximum latency (7 => 2^7 = 8192 samples, 64Kbyte buffer, + which implies 2 4096 sample, 32Kbyte periods). + Enable line out. + */ + + hdsp->control_register = HDSP_ClockModeMaster | + HDSP_SPDIFInputCoaxial | + hdsp_encode_latency(7) | + HDSP_LineOut; + + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + hdsp_reset_hw_pointer(hdsp); + hdsp_compute_period_size(hdsp); + + /* silence everything */ + + for (i = 0; i < HDSP_MATRIX_MIXER_SIZE; ++i) { + hdsp->mixer_matrix[i] = MINUS_INFINITY_GAIN; + } + + for (i = 0; i < 2048; i++) + hdsp_write_gain (hdsp, i, MINUS_INFINITY_GAIN); + + if (line_outs_monitor[hdsp->dev]) { + + snd_printk ("sending all inputs and playback streams to line outs.\n"); + + /* route all inputs to the line outs for easy monitoring. send + odd numbered channels to right, even to left. + */ + + for (i = 0; i < HDSP_MAX_CHANNELS; i++) { + if (i & 1) { + hdsp_write_gain (hdsp, INPUT_TO_OUTPUT_KEY (i, 26), UNITY_GAIN); + hdsp_write_gain (hdsp, PLAYBACK_TO_OUTPUT_KEY (i, 26), UNITY_GAIN); + } else { + hdsp_write_gain (hdsp, INPUT_TO_OUTPUT_KEY (i, 27), UNITY_GAIN); + hdsp_write_gain (hdsp, PLAYBACK_TO_OUTPUT_KEY (i, 27), UNITY_GAIN); + } + } + } + + hdsp->passthru = 0; + + /* set a default rate so that the channel map is set up. + */ + + hdsp_set_rate(hdsp, 48000); +} + +void snd_hdsp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + hdsp_t *hdsp = (hdsp_t *) dev_id; + unsigned int status; + int audio; + int midi0; + int midi1; + unsigned int midi0status; + unsigned int midi1status; + + status = hdsp_read(hdsp, HDSP_statusRegister); + + audio = status & HDSP_audioIRQPending; + midi0 = status & HDSP_midi0IRQPending; + midi1 = status & HDSP_midi1IRQPending; + + if (!audio && !midi0 && !midi1) { + return; + } + + hdsp_write(hdsp, HDSP_interruptConfirmation, 0); + + midi0status = hdsp_read (hdsp, HDSP_midiStatusIn0) & 0xff; + midi1status = hdsp_read (hdsp, HDSP_midiStatusIn1) & 0xff; + + if (audio) { + if (hdsp->capture_substream) { + snd_pcm_period_elapsed(hdsp->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + } + + if (hdsp->playback_substream) { + snd_pcm_period_elapsed(hdsp->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); + } + } + + /* note: snd_hdsp_midi_input_read() calls output_write() because + output is not interrupt-driven ... + */ + + if (midi0status) + snd_hdsp_midi_input_read (&hdsp->midi[0]); + if (midi1status) + snd_hdsp_midi_input_read (&hdsp->midi[1]); +} + +static snd_pcm_uframes_t snd_hdsp_hw_pointer(snd_pcm_substream_t *substream) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + return hdsp_hw_pointer(hdsp); +} + +static char *hdsp_channel_buffer_location(hdsp_t *hdsp, + int stream, + int channel) + +{ + int mapped_channel; + + snd_assert(channel >= 0 || channel < HDSP_MAX_CHANNELS, return NULL); + + if ((mapped_channel = hdsp->channel_map[channel]) < 0) { + return NULL; + } + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return hdsp->capture_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES); + } else { + return hdsp->playback_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES); + } +} + +static int snd_hdsp_playback_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); + snd_assert(channel_buf != NULL, return -EIO); + if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + return -EFAULT; + return count; +} + +static int snd_hdsp_capture_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); + snd_assert(channel_buf != NULL, return -EIO); + if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + return -EFAULT; + return count; +} + +static int snd_hdsp_hw_silence(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); + snd_assert(channel_buf != NULL, return -EIO); + memset(channel_buf + pos * 4, 0, count * 4); + return count; +} + +static int snd_hdsp_reset(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = hdsp->capture_substream; + else + other = hdsp->playback_substream; + if (hdsp->running) + runtime->status->hw_ptr = hdsp_hw_pointer(hdsp); + else + runtime->status->hw_ptr = 0; + if (other) { + snd_pcm_substream_t *s = substream; + snd_pcm_runtime_t *oruntime = other->runtime; + do { + s = s->link_next; + if (s == other) { + oruntime->status->hw_ptr = runtime->status->hw_ptr; + break; + } + } while (s != substream); + } + return 0; +} + +static int snd_hdsp_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + int err; + pid_t this_pid; + pid_t other_pid; + + if (hdsp_check_for_iobox (hdsp)) { + return -EIO; + } + + spin_lock_irq(&hdsp->lock); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) { + hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis); + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register |= hdsp->creg_spdif_stream); + this_pid = hdsp->playback_pid; + other_pid = hdsp->capture_pid; + } else { + this_pid = hdsp->capture_pid; + other_pid = hdsp->playback_pid; + } + + if ((other_pid > 0) && (this_pid != other_pid)) { + + /* The other stream is open, and not by the same + task as this one. Make sure that the parameters + that matter are the same. + */ + + if ((int)params_rate(params) != hdsp_system_sample_rate(hdsp)) { + spin_unlock_irq(&hdsp->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return -EBUSY; + } + + if (params_period_size(params) != hdsp->period_bytes / 4) { + spin_unlock_irq(&hdsp->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return -EBUSY; + } + + /* We're fine. */ + + spin_unlock_irq(&hdsp->lock); + return 0; + + } else { + spin_unlock_irq(&hdsp->lock); + } + + /* how to make sure that the rate matches an externally-set one ? + */ + + if ((err = hdsp_set_rate(hdsp, params_rate(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return err; + } + + if ((err = hdsp_set_interrupt_interval(hdsp, params_period_size(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return err; + } + + return 0; +} + +static int snd_hdsp_channel_info(snd_pcm_substream_t *substream, + snd_pcm_channel_info_t *info) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + int mapped_channel; + + snd_assert(info->channel < HDSP_MAX_CHANNELS, return -EINVAL); + + if ((mapped_channel = hdsp->channel_map[info->channel]) < 0) { + return -EINVAL; + } + + info->offset = mapped_channel * HDSP_CHANNEL_BUFFER_BYTES; + info->first = 0; + info->step = 32; + return 0; +} + +static int snd_hdsp_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + { + return snd_hdsp_reset(substream); + } + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + { + snd_pcm_channel_info_t *info = arg; + return snd_hdsp_channel_info(substream, info); + } + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_hdsp_trigger(snd_pcm_substream_t *substream, int cmd) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + int running; + + if (hdsp_check_for_iobox (hdsp)) { + return -EIO; + } + + spin_lock(&hdsp->lock); + running = hdsp->running; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + running |= 1 << substream->stream; + break; + case SNDRV_PCM_TRIGGER_STOP: + running &= ~(1 << substream->stream); + break; + default: + snd_BUG(); + spin_unlock(&hdsp->lock); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = hdsp->capture_substream; + else + other = hdsp->playback_substream; + + if (other) { + snd_pcm_substream_t *s = substream; + do { + s = s->link_next; + if (s == other) { + snd_pcm_trigger_done(s, substream); + if (cmd == SNDRV_PCM_TRIGGER_START) + running |= 1 << s->stream; + else + running &= ~(1 << s->stream); + goto _ok; + } + } while (s != substream); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) + hdsp_silence_playback(hdsp); + } else { + if (running && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + hdsp_silence_playback(hdsp); + } + } else { + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + hdsp_silence_playback(hdsp); + } + _ok: + snd_pcm_trigger_done(substream, substream); + if (!hdsp->running && running) + hdsp_start_audio(hdsp); + else if (hdsp->running && !running) + hdsp_stop_audio(hdsp); + hdsp->running = running; + spin_unlock(&hdsp->lock); + + return 0; +} + +static int snd_hdsp_prepare(snd_pcm_substream_t *substream) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + int result = 0; + + if (hdsp_check_for_iobox (hdsp)) { + return -EIO; + } + + spin_lock_irq(&hdsp->lock); + if (!hdsp->running) + hdsp_reset_hw_pointer(hdsp); + spin_unlock_irq(&hdsp->lock); + return result; +} + +static snd_pcm_hardware_t snd_hdsp_playback_subinfo = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_DOUBLE), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 14, + .channels_max = HDSP_MAX_CHANNELS, + .buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS, + .period_bytes_min = (64 * 4) *10, + .period_bytes_max = (8192 * 4) * HDSP_MAX_CHANNELS, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_hdsp_capture_subinfo = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 14, + .channels_max = HDSP_MAX_CHANNELS, + .buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS, + .period_bytes_min = (64 * 4) * 10, + .period_bytes_max = (8192 * 4) * HDSP_MAX_CHANNELS, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; + +#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { + .count = PERIOD_SIZES, + .list = period_sizes, + .mask = 0 +}; + +static int snd_hdsp_hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + hdsp_t *hdsp = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + unsigned int list[2] = { hdsp->ds_channels, hdsp->ss_channels }; + return snd_interval_list(c, 2, list, 0); +} + +static int snd_hdsp_hw_rule_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + hdsp_t *hdsp = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > 48000) { + snd_interval_t t = { + .min = hdsp->ds_channels, + .max = hdsp->ds_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 64000) { + snd_interval_t t = { + .min = hdsp->ss_channels, + .max = hdsp->ss_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } + return 0; +} + +static int snd_hdsp_hw_rule_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + hdsp_t *hdsp = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (c->min >= hdsp->ss_channels) { + snd_interval_t t = { + .min = 32000, + .max = 48000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdsp->ds_channels) { + snd_interval_t t = { + .min = 64000, + .max = 96000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } + return 0; +} + +static int snd_hdsp_playback_open(snd_pcm_substream_t *substream) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (hdsp_check_for_iobox (hdsp)) { + return -EIO; + } + + spin_lock_irqsave(&hdsp->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_hdsp_playback_subinfo; + runtime->dma_area = hdsp->playback_buffer; + runtime->dma_bytes = HDSP_DMA_AREA_BYTES; + + if (hdsp->capture_substream == NULL) { + hdsp_stop_audio(hdsp); + hdsp_set_thru(hdsp, -1, 0); + } + + hdsp->playback_pid = current->pid; + hdsp->playback_substream = substream; + + spin_unlock_irqrestore(&hdsp->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdsp_hw_rule_channels, hdsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdsp_hw_rule_channels_rate, hdsp, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_hdsp_hw_rule_rate_channels, hdsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + hdsp->creg_spdif_stream = hdsp->creg_spdif; + hdsp->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id); + return 0; +} + +static int snd_hdsp_playback_release(snd_pcm_substream_t *substream) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + + hdsp->playback_pid = -1; + hdsp->playback_substream = NULL; + + spin_unlock_irqrestore(&hdsp->lock, flags); + + hdsp->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id); + return 0; +} + + +static int snd_hdsp_capture_open(snd_pcm_substream_t *substream) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (hdsp_check_for_iobox (hdsp)) { + return -EIO; + } + + spin_lock_irqsave(&hdsp->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_hdsp_capture_subinfo; + runtime->dma_area = hdsp->capture_buffer; + runtime->dma_bytes = HDSP_DMA_AREA_BYTES; + + if (hdsp->playback_substream == NULL) { + hdsp_stop_audio(hdsp); + hdsp_set_thru(hdsp, -1, 0); + } + + hdsp->capture_pid = current->pid; + hdsp->capture_substream = substream; + + spin_unlock_irqrestore(&hdsp->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdsp_hw_rule_channels, hdsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdsp_hw_rule_channels_rate, hdsp, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_hdsp_hw_rule_rate_channels, hdsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + return 0; +} + +static int snd_hdsp_capture_release(snd_pcm_substream_t *substream) +{ + hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&hdsp->lock, flags); + + hdsp->capture_pid = -1; + hdsp->capture_substream = NULL; + + spin_unlock_irqrestore(&hdsp->lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_hdsp_playback_ops = { + .open = snd_hdsp_playback_open, + .close = snd_hdsp_playback_release, + .ioctl = snd_hdsp_ioctl, + .hw_params = snd_hdsp_hw_params, + .prepare = snd_hdsp_prepare, + .trigger = snd_hdsp_trigger, + .pointer = snd_hdsp_hw_pointer, + .copy = snd_hdsp_playback_copy, + .silence = snd_hdsp_hw_silence, +}; + +static snd_pcm_ops_t snd_hdsp_capture_ops = { + .open = snd_hdsp_capture_open, + .close = snd_hdsp_capture_release, + .ioctl = snd_hdsp_ioctl, + .hw_params = snd_hdsp_hw_params, + .prepare = snd_hdsp_prepare, + .trigger = snd_hdsp_trigger, + .pointer = snd_hdsp_hw_pointer, + .copy = snd_hdsp_capture_copy, +}; + +static int __devinit snd_hdsp_create_pcm(snd_card_t *card, + hdsp_t *hdsp) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(card, hdsp->card_name, 0, 1, 1, &pcm)) < 0) + return err; + + hdsp->pcm = pcm; + pcm->private_data = hdsp; + strcpy(pcm->name, hdsp->card_name); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + return 0; +} + +static int __devinit snd_hdsp_initialize_firmware (hdsp_t *hdsp) +{ + int i; + u32 *firmware_ptr; + + if (hdsp_check_for_iobox (hdsp)) { + return -EIO; + } + + if (hdsp_fifo_wait (hdsp, 0, 100)) { + return -EIO; + } + + /* enable all channels */ + + for (i = 0; i < HDSP_MAX_CHANNELS; ++i) { + hdsp_write (hdsp, HDSP_inputEnable + (4 * i), 1); + hdsp_write (hdsp, HDSP_outputEnable + (4 * i), 1); + } + + if (force_firmware[hdsp->dev] || (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) { + + snd_printk ("loading firmware\n"); + + hdsp_write (hdsp, HDSP_jtagReg, HDSP_PROGRAM); + hdsp_write (hdsp, HDSP_fifoData, 0); + if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0) { + snd_printk ("timeout waiting for firmware setup\n"); + return -EIO; + } + + hdsp_write (hdsp, HDSP_jtagReg, HDSP_S_LOAD); + hdsp_write (hdsp, HDSP_fifoData, 0); + + if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) { + hdsp->type = Multiface; + hdsp_write (hdsp, HDSP_jtagReg, HDSP_VERSION_BIT); + hdsp_write (hdsp, HDSP_jtagReg, HDSP_S_LOAD); + hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT); + } else { + hdsp->type = Digiface; + } + + hdsp_write (hdsp, HDSP_jtagReg, HDSP_S_PROGRAM); + hdsp_write (hdsp, HDSP_fifoData, 0); + + if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) { + snd_printk ("timeout waiting for download preparation\n"); + return -EIO; + } + + hdsp_write (hdsp, HDSP_jtagReg, HDSP_S_LOAD); + + if (hdsp->type == Digiface) { + firmware_ptr = (u32 *) digiface_firmware; + } else { + firmware_ptr = (u32 *) multiface_firmware; + } + + for (i = 0; i < 24413; ++i) { + hdsp_write(hdsp, HDSP_fifoData, firmware_ptr[i]); + if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) { + snd_printk ("timeout during firmware loading\n"); + return -EIO; + } + } + + if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) { + snd_printk ("timeout at end of firmware loading\n"); + return -EIO; + } + + hdsp_write (hdsp, HDSP_jtagReg, 0); + snd_printk ("finished firmware loading\n"); + mdelay(3000); + + } else { + + /* firmware already loaded, but we need to know what type + of I/O box is connected. + */ + + if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1) { + hdsp->type = Multiface; + } else { + hdsp->type = Digiface; + } + } + + if (hdsp->type == Digiface) { + snd_printk ("I/O Box is a Digiface\n"); + hdsp->card_name = "RME Hammerfall DSP (Digiface)"; + hdsp->ss_channels = DIGIFACE_SS_CHANNELS; + hdsp->ds_channels = DIGIFACE_DS_CHANNELS; + } else { + snd_printk ("I/O Box is a Multiface\n"); + hdsp->card_name = "RME Hammerfall DSP (Multiface)"; + hdsp->ss_channels = MULTIFACE_SS_CHANNELS; + hdsp->ds_channels = MULTIFACE_DS_CHANNELS; + } + + snd_hdsp_flush_midi_input (hdsp, 0); + snd_hdsp_flush_midi_input (hdsp, 1); + +#ifdef SNDRV_BIG_ENDIAN + hdsp_write(hdsp, HDSP_jtagReg, HDSP_BIGENDIAN_MODE); +#endif + + return 0; +} + +static int __devinit snd_hdsp_create(snd_card_t *card, + hdsp_t *hdsp, + int precise_ptr) +{ + struct pci_dev *pci = hdsp->pci; + int err; + unsigned short rev; + + hdsp->irq = -1; + hdsp->midi[0].rmidi = 0; + hdsp->midi[1].rmidi = 0; + hdsp->midi[0].input = 0; + hdsp->midi[1].input = 0; + hdsp->midi[0].output = 0; + hdsp->midi[1].output = 0; + spin_lock_init(&hdsp->midi[0].lock); + spin_lock_init(&hdsp->midi[1].lock); + hdsp->iobase = 0; + hdsp->res_port = 0; + + hdsp->card = card; + + spin_lock_init(&hdsp->lock); + + pci_read_config_word(hdsp->pci, PCI_CLASS_REVISION, &rev); + strcpy(card->driver, "H-DSP"); + strcpy(card->mixername, "Xilinx FPGA"); + + switch (rev & 0xff) { + case 0xa: + case 0xb: + case 0x64: + /* hdsp_initialize_firmware() will reset this */ + hdsp->card_name = "RME Hammerfall DSP"; + break; + + default: + return -ENODEV; + } + + if ((err = pci_enable_device(pci)) < 0) + return err; + + pci_set_master(hdsp->pci); + + hdsp->port = pci_resource_start(pci, 0); + + if ((hdsp->res_port = request_mem_region(hdsp->port, HDSP_IO_EXTENT, "hdsp")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1); + return -EBUSY; + } + + if ((hdsp->iobase = (unsigned long) ioremap_nocache(hdsp->port, HDSP_IO_EXTENT)) == 0) { + snd_printk("unable to remap region 0x%lx-0x%lx\n", hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_hdsp_interrupt, SA_INTERRUPT|SA_SHIRQ, "hdsp", (void *)hdsp)) { + snd_printk("unable to use IRQ %d\n", pci->irq); + return -EBUSY; + } + + hdsp->irq = pci->irq; + hdsp->precise_ptr = precise_ptr; + + if ((err = snd_hdsp_initialize_memory(hdsp)) < 0) { + return err; + } + + if ((err = snd_hdsp_initialize_firmware(hdsp)) < 0) { + return err; + } + + if ((err = snd_hdsp_create_pcm(card, hdsp)) < 0) { + return err; + } + + if ((err = snd_hdsp_create_midi(card, hdsp, 0)) < 0) { + return err; + } + + if ((err = snd_hdsp_create_midi(card, hdsp, 1)) < 0) { + return err; + } + + if ((err = snd_hdsp_create_controls(card, hdsp)) < 0) { + return err; + } + + snd_hdsp_proc_init(hdsp); + + hdsp->last_spdif_sample_rate = -1; + hdsp->last_adat_sample_rate = -1; + hdsp->playback_pid = -1; + hdsp->capture_pid = -1; + hdsp->capture_substream = NULL; + hdsp->playback_substream = NULL; + + snd_hdsp_set_defaults(hdsp); + + return 0; +} + +static int snd_hdsp_free(hdsp_t *hdsp) +{ + if (hdsp->res_port) { + /* stop the audio, and cancel all interrupts */ + hdsp->control_register &= ~(HDSP_Start|HDSP_AudioInterruptEnable|HDSP_Midi0InterruptEnable|HDSP_Midi1InterruptEnable); + hdsp_write (hdsp, HDSP_controlRegister, hdsp->control_register); + } + + if (hdsp->irq >= 0) + free_irq(hdsp->irq, (void *)hdsp); + + snd_hdsp_free_buffers(hdsp); + + if (hdsp->iobase) + iounmap((void *) hdsp->iobase); + + if (hdsp->res_port) { + release_resource(hdsp->res_port); + kfree_nocheck(hdsp->res_port); + } + + return 0; +} + +static void snd_hdsp_card_free(snd_card_t *card) +{ + hdsp_t *hdsp = (hdsp_t *) card->private_data; + + if (hdsp) + snd_hdsp_free(hdsp); +} + +static int __devinit snd_hdsp_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + hdsp_t *hdsp; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + if (!(card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(hdsp_t)))) + return -ENOMEM; + + hdsp = (hdsp_t *) card->private_data; + card->private_free = snd_hdsp_card_free; + hdsp->dev = dev; + hdsp->pci = pci; + + if ((err = snd_hdsp_create(card, hdsp, precise_ptr[dev])) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->shortname, "Hammerfall DSP"); + sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, + hdsp->port, hdsp->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_hdsp_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "RME Hammerfall DSP", + .id_table = snd_hdsp_ids, + .probe = snd_hdsp_probe, + .remove = __devexit_p(snd_hdsp_remove), +}; + +static int __init alsa_card_hdsp_init(void) +{ + if (pci_module_init(&driver) < 0) { +#ifdef MODULE + printk(KERN_ERR "RME Hammerfall-DSP: no cards found\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_hdsp_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_hdsp_init) +module_exit(alsa_card_hdsp_exit) + +#ifndef MODULE + +/* format is: snd-hdsp=enable,index,id */ + +static int __init alsa_card_hdsp_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-hdsp=", alsa_card_hdsp_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/rme9652/multiface_firmware.dat linux/sound/pci/rme9652/multiface_firmware.dat --- linux-2.4.21-rc1.orig/sound/pci/rme9652/multiface_firmware.dat 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme9652/multiface_firmware.dat 2002-07-14 15:30:57.000000000 -0600 @@ -0,0 +1,4072 @@ +/* stored in little-endian */ +static u32 multiface_firmware[24413] __devinitdata = { +0xffffffff, 0x66aa9955, 0x8001000c, 0xe0000000, 0x8006800c, 0xb0000000, +0x8004800c, 0xb4fc0100, 0x8003000c, 0x00000000, 0x8001000c, 0x90000000, +0x8004000c, 0x00000000, 0x8001000c, 0x80000000, 0x0002000c, 0x581a000a, +0x00044800, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800000, +0x00000120, 0x00000000, 0x00044800, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x04800000, 0x00000120, 0x00000000, 0x00024001, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000100, 0x00000000, +0x00004000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000100, 0x00000000, 0x00004004, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000900, 0x00000000, 0x0000c004, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000320, 0x00000000, +0x00004801, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000120, 0x00000000, 0x038048fa, 0x38000e00, 0x8014e001, 0x012e0053, +0x15c00538, 0x9e006f80, 0xe0027800, 0x00078001, 0x0070009e, 0x6f0001c0, +0x70009e00, 0xf811c000, 0x00000123, 0x00000000, 0x023fa003, 0x0bfe08ff, +0x0fe003b0, 0xfc403723, 0xc00f3008, 0x00ff0034, 0x0f3003fc, 0xbf003e80, +0xf003cc00, 0x003ec00f, 0x03fc20ff, 0x0fc407b0, 0x00000e00, 0x00000000, +0x00370801, 0x9b74021d, 0x87440510, 0x2c4099a1, 0x40091400, 0x801d0804, +0x01108074, 0x1d000448, 0xd0004400, 0x20064001, 0x0074001d, 0x07400910, +0x00000c20, 0x00000000, 0x0033a011, 0x0834260d, 0x83440091, 0x34004d29, +0x40041011, 0x000d2000, 0x00108034, 0x05080042, 0xd1002400, 0x00034000, +0x0014000d, 0x47400800, 0x00000e80, 0x00000000, 0x0035a803, 0x0174001d, +0x37400591, 0x0420d900, 0x440d1001, 0xc81d0004, 0x80170074, 0x1d310450, +0xd0006400, 0x08074001, 0x1874001d, 0x0f400900, 0x00000620, 0x00000000, +0x0037a802, 0x127400df, 0x474011b0, 0x748b1f02, 0xc0013062, 0x011f0484, +0x1132307c, 0x5f00c6c0, 0xf00c6c22, 0x00c7c031, 0x0c7c031f, 0x0b4011a0, +0x00000e20, 0x00000000, 0x003d8007, 0x02f8043f, 0x2fc09370, 0xfc80bf00, +0xc00ff006, 0x11fe013f, 0x0fe003f4, 0xff003fc0, 0xf00f5500, 0x20fec83f, +0x87fc03ff, 0x1fc02df4, 0x00000600, 0x00000000, 0x00750802, 0x805c021f, +0x170029f0, 0x4c005f08, 0xc00d300b, 0x00df2037, 0x6df0034c, 0xdf0837c0, +0x008b7c02, 0x00b7c0ad, 0x0b7c04df, 0x0ac00df1, 0x00000420, 0x00000000, +0x02b4a013, 0x1144001d, 0x7740a9d0, 0x4500dd00, 0x447db083, 0x13dd0077, +0x4dd02f44, 0xd9113740, 0x50477405, 0x0137400d, 0x037403dd, 0x4d404d90, +0x00000200, 0x00000000, 0x0032a007, 0x115490cd, 0x43000890, 0x0400c900, +0x431d1080, 0x210d1143, 0x20d08c45, 0x1d0c8242, 0x10903413, 0x0cc34320, +0xc8742b0d, 0x1c4320d0, 0x00000a00, 0x00000000, 0x00788004, 0x2584010d, +0x5a605ed0, 0x8401ed08, 0x409a9007, 0x09ed057b, 0x9ed01794, 0xa9027a40, +0x5007b401, 0x007b401e, 0x57b401ed, 0x11401e90, 0x00000200, 0x00000000, +0x02301012, 0x001c10cd, 0x03e208f0, 0x0c0ecf04, 0xc0043800, 0xe0df8173, +0x2cf0670c, 0x8f00b3c0, 0x33233c00, 0x0033c00c, 0x033c00cf, 0x48cc0cf0, +0x00000040, 0x00000000, 0x003db802, 0x01f600ff, 0x3fe84fe0, 0xfc08ef00, +0xc00ff183, 0x80ff023f, 0x0ff113ec, 0xb80037c2, 0xf083fc00, 0x003fc80f, +0x93fc00ff, 0x09c01ff0, 0x00000660, 0x00000000, 0x0037a015, 0x827c01df, +0x14c005b0, 0x5c20df01, 0xc401b000, 0x00130004, 0x0132044c, 0x53280448, +0xb0807c00, 0x0004c001, 0x004c0013, 0x57c00130, 0x00000e00, 0x00000000, +0x08398012, 0x02b4002d, 0x38400600, 0xb400ed04, 0xce0e1903, 0x40eb503e, +0x0eb103ac, 0xeb003ac4, 0xb103b440, 0x103ac40e, 0x03ac40eb, 0x4b000eb1, +0x00000620, 0x00000000, 0x00790003, 0x02b4a0ed, 0x5a403e11, 0x9601ed00, +0x401c9004, 0x01c50478, 0x1c910704, 0xe1087040, 0xd0479601, 0x1070401c, +0x470461c5, 0x0f021e18, 0x00000400, 0x00000000, 0x00372812, 0x833600cd, +0xb2403d10, 0x7440cd00, 0x484f1003, 0x80e0007a, 0x6f902ba4, 0xe980ba08, +0x5013b64d, 0x013a400e, 0x13a400ed, 0x4b401e99, 0x00000c20, 0x00000000, +0x0015a817, 0x15fce07f, 0x96d807b3, 0xdc007f03, 0xc045b029, 0x98542a14, +0x45b02d4c, 0x5301d480, 0xd20d7c00, 0x0094c455, 0x014c4b57, 0x5fc11730, +0x00000620, 0x00000000, 0x10070012, 0x407c201f, 0x85c061f0, 0x7c001f00, +0xc803f280, 0x403f008f, 0x23f008fc, 0x3f248fc0, 0xb008fc02, 0x128fcc03, +0x08fc8a3b, 0x4bc003f0, 0x00000c00, 0x00000000, 0x00a70810, 0x020c0083, +0x74c05978, 0x4c819f01, 0xd0093082, 0x01934164, 0x19f08e4d, 0x934065c0, +0x34164d09, 0x4064d129, 0x26510593, 0x43c01935, 0x00000c20, 0x00000000, +0x50262001, 0x06440090, 0xa4504918, 0x45028d00, 0xe229b002, 0xa2970926, +0x09d00e44, 0x91002446, 0x101e6c40, 0x00e44039, 0x1244e091, 0x06403910, +0x00000800, 0x00000000, 0x0024a018, 0x524480d0, 0xa4600d50, 0x44089d00, +0x00891002, 0x18e11028, 0x8bd082c4, 0xe10a2844, 0x10838410, 0x0238400e, +0x028420e1, 0x63418b10, 0x00000200, 0x00000000, 0x02302810, 0x0a040281, +0x20408810, 0x04008d82, 0x402e9422, 0x42a920a8, 0x2ad00b84, 0xa100a840, +0x100aa402, 0x00a8402e, 0x0a8402a1, 0x43400a10, 0x00000080, 0x00000000, +0x0886a01d, 0x00042011, 0x84c16050, 0x0c005f05, 0x40013008, 0x00130004, +0x05f0004c, 0x130015c0, 0x30004c00, 0x0014c001, 0x005c0053, 0x77c00330, +0x00000ac0, 0x00000000, 0x0127a819, 0x0afc02ff, 0x27c04bf0, 0xfe00bf01, +0xc029f092, 0x829700a7, 0x29f00a7c, 0xdf00a790, 0xf08b5c02, 0x00a7c02d, +0x0a7c829f, 0x66c029f0, 0x00000e60, 0x00000000, 0x00afa018, 0x32fc0cbf, +0x27c00bb0, 0xfc00bf02, 0xc009b002, 0x049f0024, 0x49341a7c, 0x9f01a7e4, +0x059a7c0c, 0x41a4d069, 0x0a7c8493, 0x63c009d0, 0x00000e00, 0x00000000, +0x2007081c, 0x3874ae0d, 0x07490130, 0x74001d02, 0x44011000, 0xa01d2204, +0x45120074, 0x1d2297e4, 0x10987404, 0x00844821, 0x08748e51, 0x734027d2, +0x00000c20, 0x00000000, 0x0023a010, 0x0236808d, 0x23408810, 0x34008d00, +0x41ca9002, 0x1cad0d28, 0x2a180a94, 0xad002b41, 0x111ab682, 0x11a8406a, +0x0ab430a5, 0x43402ad1, 0x00000e80, 0x00000000, 0x8025a818, 0x0274009d, +0x27400998, 0x74209d00, 0x4aa81292, 0x90fd242c, 0x0f101bb4, 0xfd0a2d42, +0x1002f620, 0x01ac400b, 0x82f400b5, 0x63408bd9, 0x00000620, 0x00000000, +0x0027a805, 0x127c009f, 0x27c24938, 0x74009f11, 0xc029b802, 0x009f2024, +0x1932067c, 0x9f002740, 0x31027c00, 0x00244409, 0x627c0897, 0x17c039f0, +0x00000e20, 0x00000000, 0x00258014, 0x0e7c039f, 0x77c00970, 0x7c099f21, +0xd809f002, 0x089b0267, 0x49f0466c, 0x9f0827c0, 0xf00a7802, 0x0427c039, +0x027c019b, 0x53c019f2, 0x00000600, 0x00000000, 0x00050814, 0x48140617, +0x84c02170, 0x7c001f02, 0xc8233000, 0x0432238f, 0x03e028fc, 0x33100fc0, +0xf000fc10, 0x000fc723, 0x00cc123f, 0x53c023f0, 0x00000420, 0x00000000, +0x005c8014, 0x09c4077d, 0x14401770, 0xf6207d08, 0xc0051061, 0x025500d5, +0x15d0015c, 0x530095c5, 0xd0817420, 0x00974005, 0x8144805d, 0x534005d0, +0x00000200, 0x00000000, 0x0072a014, 0x033401cd, 0xb0689c50, 0x7600cd21, +0x410e100f, 0x03e1003b, 0x4ed003b4, 0xe94c7a40, 0x9033f400, 0x08ff401f, +0x638500ed, 0x53421ed0, 0x00000a00, 0x00000000, 0x04088005, 0x04a4012d, +0x38401450, 0xf400ed80, 0x400e1803, 0x02e51079, 0x0ed003b4, 0xe100f940, +0x9003b440, 0x00bb402e, 0x838411ed, 0x17402ed0, 0x00000200, 0x00000000, +0x00781015, 0x01b421e7, 0x78501e70, 0xb401ef02, 0xc81e1007, 0x01e38073, +0x1ef007bc, 0xeb187fe4, 0xf007bc01, 0x187bc61e, 0x878c01ff, 0x57ce1ef0, +0x00000040, 0x00000000, 0x0035b810, 0x015c201f, 0xa7c80df1, 0x3400df01, +0xc201e503, 0x001f0807, 0x01f0805c, 0x5f0807c0, 0xf0007c20, 0x0007c201, +0x807c001f, 0x43c001f1, 0x00000660, 0x00000000, 0x807fb000, 0x27fc09f3, +0x5cc05ff1, 0xcc01f308, 0xc41f3804, 0x29f3007c, 0x1f3007cc, 0xf5007cc0, +0x30a7cc01, 0x027c089f, 0x27c08df3, 0x00c09f30, 0x00000e00, 0x00000000, +0x02098015, 0x02b40ea1, 0xd840f6d1, 0xac00e120, 0xc0d61004, 0x01eb02ba, +0x8eb2232c, 0xeb223ac5, 0xb003ac2d, 0x223ac48e, 0x63ac8ceb, 0x56c08eb1, +0x00000620, 0x00000000, 0x00290000, 0x41b480e1, 0x106008d1, 0xc400f900, +0x404c1010, 0x0cc9003a, 0x0c104304, 0xc1003240, 0x124304ac, 0x1030408c, +0x83061cc1, 0x00411c13, 0x00000400, 0x00000000, 0x00232804, 0x07345b91, +0xc05808d0, 0x2402c903, 0x40d8120c, 0x020d2802, 0x10904c24, 0x49004242, +0x98082413, 0x12022010, 0x00264209, 0x12400098, 0x00000c20, 0x00000000, +0x00358815, 0x067c0113, 0x74c019f0, 0x4c00cb40, 0xc83d10a0, 0x01d90876, +0x1d310f4c, 0x9308b682, 0x300b4c10, 0x00f4c02d, 0x0f4c03d3, 0x54c02d30, +0x00000620, 0x00000000, 0x00870001, 0x007c001f, 0x37c0a1f0, 0x7c06d700, +0xc00df228, 0x0cdb03b7, 0x2df0037c, 0x9f1337c1, 0xf0037c02, 0x0137c4ed, +0x437c80df, 0x07c04df0, 0x00000c00, 0x00000000, 0x081f0880, 0x00dc003f, +0x5fc057f0, 0xec253300, 0xc05bf004, 0x05ff0135, 0x9ff00bfc, 0xaf017fc0, +0xf0c7cc01, 0x013f009f, 0x23fc14ff, 0x03c08ff0, 0x00000c22, 0x00000000, +0x24962081, 0x05440b1d, 0x474025b0, 0x55841100, 0x4405d148, 0x001d0047, +0x31f10074, 0x1d110748, 0xd1005401, 0x04874821, 0x1874521d, 0x074001d2, +0x00000802, 0x00000000, 0x0034a001, 0x1274005d, 0x034009d0, 0x4400d182, +0x400dd023, 0x20150007, 0x01d80074, 0x1d000740, 0xd0184428, 0x20074821, +0x0074001d, 0x074009d1, 0x00000200, 0x00000000, 0x00002010, 0x820400cd, +0x034040d0, 0x0420d190, 0x4444d013, 0x240d1003, 0x00d80834, 0x0d000340, +0xd0001400, 0x00024000, 0x0034000d, 0x434408c0, 0x00000080, 0x00000000, +0x0002a000, 0x005c005f, 0x47c051f0, 0x4c001140, 0xc359f857, 0x55170805, +0x01f0007c, 0x1f1007c2, 0xf0004c25, 0x2007c201, 0x807c001f, 0x03c409e0, +0x00000ac0, 0x00000000, 0x000fa805, 0x03fc00ff, 0x07c08130, 0x3c002f02, +0xc00df003, 0x40df057f, 0x0f7007fc, 0x9f003fc0, 0xf003fc02, 0x003fc80f, +0x03fc80ff, 0x17c007f0, 0x00000e60, 0x00000000, 0x820f8003, 0x20fc00ff, +0x04e00dd0, 0x5c04ff00, 0xc00d3000, 0x00df0034, 0x0f3003cc, 0xd30034c0, +0x3003dc00, 0x003cc00f, 0x03cc00f3, 0x0cc00f30, 0x00000e00, 0x00000000, +0x02070801, 0x187400dd, 0x8444add0, 0x4508dd00, 0x40017010, 0x401d0004, +0x01100044, 0x11000440, 0x10007440, 0x20044001, 0x00440011, 0x04400111, +0x00000c20, 0x00000000, 0x0023a011, 0x183400cd, 0x834000d0, 0x2400cd00, +0x50001842, 0x00cd0030, 0x08940315, 0x81401050, 0x57031400, 0x4032500c, +0x020500c1, 0x44500c14, 0x00000e80, 0x00000000, 0x8525a803, 0x237440dd, +0x875001d0, 0x2408dd00, 0x600d5202, 0x040d0004, 0x04920034, 0x51002040, +0x58003400, 0x00024000, 0x01440001, 0x0c400090, 0x00000620, 0x00000000, +0x10878802, 0x0c7480dd, 0xc74201f0, 0x64611f00, 0xc00d3061, 0x205f3924, +0x05b1015c, 0x530234c0, 0x70005c00, 0x08064801, 0x01440013, 0x08c00130, +0x00000e20, 0x00000000, 0x040d8007, 0x45fc80fd, 0x1c4000f0, 0xcc113f31, +0xc901f205, 0x41bf201f, 0x0b7026cc, 0xbf000fc0, 0xb003fc00, 0x003dc00f, +0x02fc00fe, 0x1fc00f60, 0x00000600, 0x00000000, 0x00950802, 0x087c20d7, +0x96c00df0, 0x7c420f20, 0xc481b00b, 0x185f0024, 0x01f0214c, 0x1f0017c3, +0x70407c00, 0x0205c081, 0x207c081f, 0x0bc081f0, 0x00000420, 0x00000000, +0x00d4a013, 0x0f7404dc, 0x56c01dd1, 0x5c831d04, 0x402d1403, 0x439d01d4, +0x1dd00645, 0xdd04a744, 0xd04b7407, 0x0274509d, 0x277409dd, 0x4f409dd0, +0x00000200, 0x00000000, 0x00d2a007, 0x003446cc, 0x42490cda, 0x30010d20, +0x422d1000, 0x008d0c12, 0xacd04204, 0xcd046340, 0x40873410, 0x00b0402c, +0x0b7402cd, 0x1f402cd0, 0x00000a00, 0x00000000, 0x8c588004, 0x44b441ed, +0x5a609ed1, 0xb4092d00, 0x44121824, 0x414d116a, 0x12d11584, 0x2d115b42, +0xd114b441, 0x10484410, 0x04b6412d, 0x124012d1, 0x00000200, 0x00000000, +0x90301012, 0x3b3c08cf, 0x72c150f0, 0x3c170f01, 0xd0103905, 0xa98f2952, +0xd8f0160c, 0x8f0243c0, 0x70033c01, 0x0031c08c, 0x223c60cf, 0x4bc00cf0, +0x00000040, 0x00000000, 0x023db802, 0x23fc80ff, 0x3f8083f0, 0x9c003f02, +0xc00f7021, 0x007f022d, 0x05f121fc, 0x5f2037c0, 0xf1107c40, 0x000fc003, +0x01fc403f, 0x0bc013f0, 0x00000660, 0x00000000, 0x2027a015, 0x097c80db, +0x60c011f0, 0x2c20d101, 0xc00d2006, 0x001f0004, 0x05f0007c, 0x5f002700, +0x30047c00, 0x0007c001, 0x017c001f, 0x57c001f0, 0x00000e00, 0x00000000, +0x98298812, 0x41bc00e1, 0x38c00270, 0x8500e104, 0x44021202, 0x00ed1038, +0x0ad003b4, 0xad001b40, 0x10032400, 0x003b400e, 0x02b400ed, 0x4b400ed0, +0x00000620, 0x00000000, 0x04700003, 0x87b401e5, 0x7b601ed8, 0x8431c900, +0x40109007, 0x012d004a, 0x12d084b4, 0x2d104b64, 0x1204b601, 0x004b6012, +0x84b4012d, 0x0f4012d0, 0x00000400, 0x00000000, 0xa0f30812, 0x2714a0c5, +0xb1407c5a, 0x2440c902, 0x700c9547, 0x92cd0874, 0x6cd04b34, 0xcd003340, +0x146b3412, 0x0cf3413c, 0x4b3413cd, 0x4b41acd0, 0x00000c20, 0x00000000, +0x40dda817, 0x0174c057, 0x554017d1, 0xcc007b20, 0xc0059805, 0xc25d0054, +0x25f1897c, 0x5f0017c0, 0x300d7402, 0x01d7c075, 0x097c075f, 0x5fc035f0, +0x00000620, 0x00000000, 0x10070012, 0x087e001b, 0x04c08170, 0x5c000701, +0xc0037020, 0x023f228f, 0x22f008fc, 0x3f100f40, 0xf000ec02, 0x008fc023, +0x08fc023f, 0x4bc023f0, 0x00000c00, 0x00000000, 0x04e70810, 0x077c009b, +0x37c08960, 0x4d109f00, 0xc008f002, 0x00930024, 0x0970027c, 0x9f0824c0, +0xf0027c80, 0x0027c009, 0x027c009f, 0x43c009f0, 0x00000c20, 0x00000000, +0x00662001, 0x0e5c0891, 0x274439d0, 0x44489d00, 0x5409d002, 0x029140a4, +0x29d10a74, 0x9d0024d0, 0xd1027402, 0x10a5c429, 0x0a74a29d, 0x074029d0, +0x00000800, 0x00000000, 0x0020a018, 0x1a548090, 0x256009d0, 0x46009d80, +0x4019d802, 0x00b8802c, 0x0bd002f4, 0xbd802c60, 0xd002f600, 0x002f400b, +0x02f400bd, 0x63400bd0, 0x00000200, 0x00000000, 0x42202010, 0x22344881, +0x23420cd0, 0x06008d80, 0x403ad022, 0x02e180b8, 0x2ed00bb4, 0xed80a860, +0xd00ab402, 0x00b9402e, 0x0bb402ed, 0x43402ed0, 0x00000080, 0x00000000, +0x0586b01d, 0x585c8213, 0x05c141f8, 0x44141f05, 0xc001f008, 0x80132004, +0x01700074, 0x1f0014c0, 0xf0007c00, 0x0007c001, 0x007c001f, 0x77c003f0, +0x00000ac0, 0x00000000, 0x012fb819, 0x125c849f, 0x37c00af0, 0xfc00bf00, +0xc029f112, 0x029f10a7, 0x29d00a7c, 0x9f00b5c0, 0xf00a7c82, 0x00a5c029, +0x0a7c029f, 0x67c009f0, 0x00000e60, 0x00000000, 0x01afa818, 0x826c08bf, +0x2fc00bf8, 0x8c20af00, 0xc149700a, 0x28930024, 0xc9320a4c, 0x9300a705, +0x30326c06, 0x00254089, 0x0a4c0093, 0x60c02930, 0x00000e00, 0x00000000, +0x1187081c, 0x2836800d, 0x044001d0, 0x6c001d00, 0x4461b000, 0x04154104, +0x61140045, 0x11008640, 0x1028444e, 0x00874021, 0x00451611, 0x70400310, +0x00000c20, 0x00000000, 0x0323a010, 0x5224048d, 0xa16008d2, 0x66008d20, +0x411ad202, 0x02ad04a8, 0x0a109a84, 0xa9c1a940, 0x14128500, 0x45a9406a, +0x0a8422a1, 0x40504a14, 0x00000e80, 0x00000000, 0x0525a818, 0x4274009d, +0x244109d0, 0x44009c00, 0x42191302, 0x20b1082c, 0x0b1002c4, 0xb1002e04, +0x5802e480, 0x002f400b, 0x02c400b1, 0x60400b10, 0x00000620, 0x00000000, +0x00278005, 0x0e64009f, 0xa54009d0, 0x44009e10, 0xd409c012, 0x00938024, +0x1930024c, 0x9300a780, 0x30824c00, 0x0025c209, 0x024c0093, 0x14c00930, +0x00000e20, 0x00000000, 0x00258014, 0x277c009f, 0xe5c099f0, 0x7d308f00, +0xc009f00e, 0x408f1023, 0x39f1027c, 0x8f1026c0, 0xb0c21c04, 0x0027c009, +0x123c408f, 0x53c009f0, 0x00000600, 0x00000000, 0x00850014, 0x884c101f, +0x86c46070, 0x6c00131a, 0xc0023000, 0x0033000c, 0x033000cc, 0x33108fc0, +0x3000cc00, 0x080cc003, 0x00fc003f, 0x53c003f0, 0x00000420, 0x00000000, +0x021ca014, 0x0140807d, 0x14c01750, 0x04827140, 0x50051001, 0x00514814, +0x05140145, 0x51e01744, 0x16094500, 0x00145005, 0x0174005d, 0x534007d0, +0x00000200, 0x00000000, 0x80b2a014, 0x470402cd, 0x30403c90, 0x260ac500, +0x400cd002, 0x00c10230, 0x0c900354, 0xc1123740, 0x10230420, 0x0034400c, +0x033608cd, 0x53400dd0, 0x00000a00, 0x00000000, 0x00388005, 0x478401ed, +0x78410e10, 0xc4006500, 0x40005012, 0x20218008, 0x02108094, 0x21000340, +0x91008400, 0x00084210, 0x80b4a02d, 0x174002d0, 0x00000200, 0x00000000, +0x90781015, 0x478c21af, 0x70d01474, 0xac81e700, 0xc012f136, 0x41733050, +0x1630051e, 0x42005bc0, 0x3007cc01, 0x0058c016, 0x05bc016f, 0x57c016f0, +0x00000040, 0x00000000, 0x2025b810, 0x007da0df, 0x37e20d70, 0x4c805b20, +0xc80db01a, 0x009d0027, 0x09f0026c, 0x970027c8, 0x72007c80, 0x2027c009, +0x027c009f, 0x43c009f0, 0x00000660, 0x00000000, 0x007fa000, 0x05cd0d73, +0x64c01f71, 0xce01df00, 0xd01f7066, 0x01d24b64, 0x1f3427fc, 0xf3426ed0, +0xf104dd89, 0x407c909f, 0x07cd09b3, 0x03c01f34, 0x00000e00, 0x00000000, +0x02198815, 0x11840871, 0x6a805610, 0xc60d4d05, 0x40821006, 0x892b0358, +0x91102424, 0x31005841, 0x90178409, 0x020840c3, 0x20840861, 0x57408210, +0x00000620, 0x00000000, 0x00310000, 0x01060421, 0x28404410, 0x8404ed03, +0x40025582, 0x40610008, 0x061011b4, 0x61010060, 0xd0108404, 0x00104406, +0x01841001, 0x03401610, 0x00000400, 0x00000000, 0x00072804, 0x00040041, +0x60400010, 0x04004d80, 0x401c1002, 0x13810070, 0x28100e24, 0x8104f040, +0x9a4f0402, 0x04e04138, 0x4e0413d1, 0x13413810, 0x00000c20, 0x00000000, +0x00058815, 0x6344c0d1, 0x34520c1d, 0xc500cf10, 0xc03d7002, 0xb29110f4, +0x99324a7c, 0x9104b6c2, 0xf04b4411, 0x04a4c129, 0x4a4c43d3, 0x57c12930, +0x00000620, 0x00000000, 0x00370001, 0x037c001f, 0x27c00d30, 0x7c02df00, +0xc0c1f102, 0x005f0307, 0x05f2016c, 0x5f0007c0, 0xb0006c80, 0x0017c005, +0x013c001f, 0x07c005f0, 0x00000c00, 0x00000000, 0x00cf0880, 0x23fc01b3, +0xfcc007f0, 0xcc03df00, 0xc002f002, 0x403f001c, 0x12f04474, 0x3f001cc0, +0xf003cc11, 0x004bc013, 0x04cc0073, 0x03c01330, 0x00000c22, 0x00000000, +0x04e62081, 0x00740315, 0x65400d71, 0x7c13dd00, 0x400dd002, 0x89dd2024, +0x1dd00774, 0xdd002450, 0xd0004580, 0x1077400d, 0x07440091, 0x07401d14, +0x00000802, 0x00000000, 0x0804a001, 0x00740ad0, 0x34400dd1, 0x44005d00, +0x400dd002, 0x009c0024, 0x49d01274, 0x8d002440, 0xd1004404, 0x41274049, +0x12440091, 0x07404910, 0x00000200, 0x00000000, 0x00102210, 0x00240001, +0x214044d0, 0x24064900, 0x5220d002, 0x004d0110, 0x04d00134, 0x4d111040, +0xd0030400, 0x00134004, 0x01040041, 0x43400410, 0x00000080, 0x00000000, +0x4006b000, 0x007c8093, 0x74c235f8, 0xcc375f21, 0xc020f03e, 0x0b1f0340, +0xf1f23c7c, 0x1f02c4c0, 0xf0144c07, 0x0007c001, 0x000d0013, 0x03c00130, +0x00000ac0, 0x00000000, 0x000f9805, 0x00fc003f, 0xefc1d178, 0x7e053f04, +0xc01ff00e, 0x09ff0b7f, 0x9df0377c, 0xdf027740, 0xf0237c17, 0x003fc00f, +0x03fc20ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x000fa003, 0x20e804ff, +0x0fc083f0, 0xec263c08, 0xc20fb053, 0x00f3003e, 0x0f3003cc, 0xb3003fc0, +0x3003ec80, 0x083ec00f, 0x83ec20ff, 0x0cc007f0, 0x00000e00, 0x00000000, +0x02070801, 0x2844369d, 0x274a81d0, 0x44c61d00, 0x4009100b, 0x00110004, +0x01140045, 0x11000740, 0x10004400, 0x00044601, 0x0045001d, 0x07c809d1, +0x00000c20, 0x00000000, 0x0013a011, 0x122408cd, 0x01604451, 0x04020d00, +0x50049003, 0x00014800, 0x00808014, 0x01480340, 0x16000500, 0x20005800, +0x0004800d, 0x446408d1, 0x00000e80, 0x00000000, 0x0055a803, 0x0466408d, +0x07400dd0, 0x44201d21, 0x440c1013, 0x08110004, 0x41902014, 0x01000340, +0x10004410, 0x00044081, 0x0064101d, 0x0f4009d1, 0x00000620, 0x00000000, +0x0007a802, 0x0c6c095f, 0x07c041f0, 0x6d001f00, 0xc04db007, 0x231304c6, +0x01b0145c, 0x530307c0, 0x30186c00, 0x0006c051, 0x086c001f, 0x00c401f3, +0x00000e20, 0x00000000, 0x000d8007, 0x005c003f, 0x67c007f0, 0xbc057f08, +0xc05bf107, 0x01ff027f, 0x1f6007e4, 0xff103fc0, 0xf227fc00, 0x017fc00f, +0x27dc05fb, 0x1e403ef0, 0x00000602, 0x00000000, 0x00a50802, 0x0a7c00df, +0x35c00170, 0x7c104300, 0xc125f063, 0x40df2834, 0x2df08b7c, 0xdf00b7c0, +0xf00b7c02, 0x00b7c02d, 0x0b7c1ac3, 0x0bc20df0, 0x00000420, 0x00000000, +0x1464a013, 0x1044009d, 0xf7400d10, 0x74025101, 0x445db007, 0x26dd01f4, +0x2dd00b74, 0xdd02b741, 0xd20b7487, 0x60b7492d, 0x4b7481d1, 0x4f41add0, +0x00000200, 0x00000000, 0x0022a007, 0x0214060d, 0xd2680810, 0x24030104, +0x4030100b, 0x111d1404, 0x10d06434, 0x0d22c245, 0x90646410, 0x06434111, +0x44240209, 0x1f4810d0, 0x00000a00, 0x00000000, 0x00688004, 0x0684212d, +0x7b409214, 0xb4116100, 0x501c9807, 0x81ed0878, 0x9ed087b4, 0xad007b42, +0x9007b421, 0x0073401e, 0x07b421e9, 0x1b401ed0, 0x00000200, 0x00000000, +0x82301012, 0x221c0a5d, 0x43e08c70, 0x7c8c4340, 0xe88c7403, 0x02cf3230, +0x0cf0033c, 0x8f0133c0, 0xf0233c00, 0x0233c00c, 0x233c00cb, 0x4bc08cf0, +0x00000040, 0x00000000, 0x083db802, 0x23dc007f, 0x0fce8ff0, 0xfc0a7f00, +0xc60ff003, 0x00ff023f, 0x0ff803fe, 0xbf803fc2, 0xf283fc00, 0x003fc40f, +0x83fc00f5, 0x0bc01ff0, 0x00000660, 0x00000000, 0x0017a015, 0x027c811f, +0x56c209f2, 0x7c001300, 0xe0013423, 0x801f0007, 0x01f2007c, 0x531044c0, +0xf2807e80, 0x0007c801, 0x805c001f, 0x57c001f0, 0x00000e00, 0x00000000, +0x00198812, 0x03b4002d, 0x384004d0, 0xf4006b00, 0x600e5013, 0x00ed003b, +0x0ed003b4, 0xe1183042, 0xd003b400, 0x003b400e, 0x839c00ed, 0x4b4a0ed1, +0x00000600, 0x00000000, 0x00f90003, 0x46b400e5, 0x7a411ad0, 0xb6236100, +0x4a1e100f, 0x11ed2079, 0x1ed107b4, 0xe5007a40, 0xd18fb401, 0x087b4a1e, +0x0f9403ed, 0x0f401ed0, 0x00000402, 0x00000000, 0x10f32812, 0x033402cd, +0xb0400dd2, 0x34015912, 0x420e5403, 0x40ed03fb, 0x3ed10bb4, 0xe5043a50, +0xd04bb412, 0x00bb402e, 0x4b9401ed, 0x4b000ed0, 0x00000c20, 0x00000000, +0x00dda817, 0x05fc007f, 0x5ec007f0, 0xbc015300, 0xc0051004, 0x035f0017, +0x55f0117c, 0x570496c0, 0xf1017c01, 0x0417c525, 0x015c005f, 0x5fc107f8, +0x00000620, 0x00000000, 0x01070012, 0x007c101f, 0x07c801d0, 0x7c021f04, +0xc003f000, 0x403b200f, 0x03f148fc, 0x3a408dc1, 0xf000bc08, 0x118fc003, +0x00fc043f, 0x4bc823f0, 0x00000c00, 0x00000000, 0x02270810, 0x064c088f, +0x24c209f1, 0x4c41df20, 0xc0093404, 0x109f0027, 0x08f0024c, 0x9f0064c0, +0xf0027c00, 0x0027c109, 0x027c009f, 0x43c009f0, 0x00000c20, 0x00000000, +0x00262001, 0x2a44019d, 0xa44009d0, 0x44209d00, 0x40091082, 0x809d0027, +0x09d00245, 0x9c03e458, 0x10827420, 0x00274009, 0x0274809d, 0x074129f0, +0x00000800, 0x00000000, 0x0034a018, 0x4344009d, 0xa44009d0, 0x44089d00, +0x40091022, 0x00a5082f, 0x0fd082c4, 0xbd002c48, 0x1402f000, 0x003e640b, +0x03f600b9, 0x63400bd0, 0x00000200, 0x00000000, 0x00202010, 0x2204028d, +0x2051c8d0, 0x4508cd02, 0x422a100a, 0x02ad00ab, 0x2ad00a84, 0xad00a84c, +0x100ab442, 0x00ab482a, 0x02b602ad, 0x43400ad0, 0x00000080, 0x00000000, +0x0506b01d, 0x584c001f, 0x80c061f0, 0x4c161f05, 0xc0013850, 0x00170007, +0x00f0014c, 0x1d0004c4, 0xf0007c40, 0x0017c001, 0x007c801f, 0x77c203f0, +0x00000a80, 0x00000000, 0x002fb819, 0x13fc02ff, 0x2f804bf0, 0xf8249f01, +0xca29f000, 0x029f00a7, 0x29f00a7c, 0x9f00a7cc, 0xf00a7c22, 0x00a7c029, +0x0a7c029f, 0x67c02970, 0x00000e60, 0x00000000, 0x002fa018, 0xc2cc04bb, +0xacc06930, 0x4c06b301, 0xc1093016, 0x04930227, 0x09300a4c, 0x930427c0, +0x301a4c04, 0x00a4c069, 0x024c009b, 0x67c00930, 0x00000e00, 0x00000000, +0x0297081c, 0x0044341d, 0x84424510, 0x6e861141, 0x40611018, 0x16110287, +0xe1140145, 0x51408748, 0x10104114, 0x03944045, 0x00440411, 0x72400714, +0x00000c22, 0x00000000, 0x0023a010, 0x4224428d, 0xa0486810, 0x84028100, +0x410a5c4a, 0x48a1492b, 0x0a100a84, 0xa1052b41, 0x141a8402, 0x4128506a, +0x0aa532a1, 0x43402a10, 0x00000e80, 0x00000000, 0x0125a818, 0x0264089d, +0x25444814, 0x2400c100, 0x430b5082, 0x10b1042b, 0x6b1002c4, 0xb108af4a, +0x101a8400, 0x0028404e, 0x42e426a1, 0x63408b12, 0x00000620, 0x00000000, +0x08e7a805, 0x0a6c0299, 0x24902930, 0x44009301, 0xc0297002, 0x00930027, +0x3930064c, 0x930027c0, 0x300e4c01, 0x00e4c019, 0x026c009b, 0x17c03930, +0x00000e20, 0x00000000, 0x00258014, 0x0e5c409c, 0x26c209f1, 0x7c189f01, +0x8129b00e, 0x088f0167, 0x19f00e7c, 0x9f0127c4, 0xf2027803, 0x0127c019, +0x221c009f, 0x52c009f0, 0x00000600, 0x00000000, 0x40850814, 0x187c0003, +0x04d021f0, 0xfc001f20, 0xc4237100, 0x1033000c, 0x23f000fc, 0x33000cc1, +0x3048cc00, 0x000cc003, 0x80cca23f, 0x530003f2, 0x00000420, 0x00000000, +0x1154a014, 0x49f44271, 0x1d4005d0, 0x76927d21, 0x5405d001, 0x00514014, +0x05d00174, 0x514016d2, 0x16014580, 0x00145005, 0x014d005d, 0x534005d2, +0x00000200, 0x00000000, 0x0072a014, 0x07740cc1, 0x35400cd0, 0x3441cd01, +0x420c4283, 0x00c10230, 0x0cd00374, 0xd1003440, 0x10030408, 0x0230400c, +0x032400cd, 0x53440cd0, 0x00000a00, 0x00000000, 0x00608005, 0x03b481e1, +0x38400cd0, 0xb400ed00, 0x4010d000, 0x20210800, 0x12d000b6, 0x01000042, +0x10000420, 0x18084000, 0x0084200d, 0x174402d0, 0x00000200, 0x00000000, +0x00781015, 0x85bc20a3, 0x59c33ef0, 0xb481ef20, 0xc01e7206, 0x01e30078, +0x1cf085bc, 0xe3004846, 0x31078c01, 0x0058c01e, 0x07ac01ef, 0x57c016f0, +0x00000040, 0x00000000, 0x0825b810, 0x007c0098, 0x17c40df0, 0x7c009f00, +0xc801f001, 0x001f2007, 0x01f2027c, 0x1f2037c0, 0xf0007c80, 0x0027c801, +0x007c001e, 0x43c209f8, 0x00000660, 0x00000000, 0x007bb000, 0x06fc8973, +0x7f041ff1, 0xfc01ff02, 0xc0973027, 0x097f026c, 0x973007cc, 0xbf107cc0, +0xf125cd09, 0x026cd01f, 0x07c801f3, 0x0bc01f30, 0x00000e00, 0x00000000, +0x02398015, 0x82b408eb, 0x03400ed0, 0xf4006d00, 0x42cb1008, 0x08ad0318, +0xcb102084, 0x6d020840, 0xd0b2840c, 0x021c4182, 0x60844831, 0x57480210, +0x00000620, 0x00000000, 0x00290000, 0x60b63821, 0x3b408ed0, 0xb418ed10, +0x44065082, 0x084d0028, 0x06100104, 0x8d000840, 0xd0018400, 0x0208400c, +0x030400e1, 0x63401610, 0x00000400, 0x00000000, 0x10232804, 0x08360089, +0x83400cd1, 0x34010c00, 0x54984081, 0x108d0010, 0x98102204, 0x4d02b050, +0xd00e0483, 0x42f04030, 0x08050a01, 0x1b403810, 0x00000c20, 0x00000000, +0x0035a815, 0x067c2113, 0xa7c82ff0, 0x3c21df00, 0xc0996401, 0x019f1414, +0x19102a4c, 0x5f0074c0, 0xf04a4c10, 0x00744201, 0x0c4c0313, 0x77c10910, +0x00000620, 0x00000000, 0x10230001, 0x4a70021f, 0x23011df0, 0x7c22df00, +0xc005b006, 0x085f0023, 0x05f4017d, 0x9f1407c1, 0xf0117c12, 0x0407c90d, +0x1b7c02df, 0x07c104f4, 0x00000c00, 0x00000000, 0x003f0880, 0x82fc0833, +0x0fc20ff8, 0xfc857304, 0xc21a3000, 0x01b3041c, 0x5b3084bc, 0x7f000fc0, +0xf006fc00, 0x001bc013, 0x04fc1033, 0x04c013f0, 0x00000c20, 0x00000000, +0x00262081, 0x06740111, 0x47400dd0, 0x74231102, 0x40951003, 0x015b4024, +0x05140774, 0x9d083740, 0xd0017424, 0x0827420d, 0x177404d1, 0x05460dd0, +0x00000802, 0x00000000, 0x0034a001, 0x0e749051, 0x37400dd0, 0x74809100, +0x04011001, 0x04110084, 0x01101274, 0x1d183740, 0x90107000, 0x08274241, +0x10744811, 0x044249d0, 0x00000200, 0x00000000, 0x00302010, 0x023400c1, +0x02400cd0, 0x74000108, 0x400c1002, 0x00c90030, 0x0c128134, 0xcd080340, +0xd0033420, 0x0813400c, 0x032420c1, 0x41400490, 0x00000080, 0x00000000, +0x0026a000, 0x007c0053, 0xb7c00fd0, 0x7c001340, 0xc0013000, 0x60130004, +0x0131007c, 0x1f0007c4, 0xf0007c00, 0x0007c001, 0x007c0013, 0x04c401f0, +0x00000ac0, 0x00000000, 0x002fa805, 0x00fc80ff, 0x4fc80ff0, 0xfc003f15, +0xc00ff103, 0x40f7003f, 0x0ff003fc, 0xff003fc0, 0xf003fc00, 0x583fc00f, +0x03fc00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x030f8003, 0x03cc0833, +0x3fc08114, 0x4c00f308, 0xc00f381b, 0x00ff003f, 0x0df0837c, 0x9f0034c0, +0xf0037c00, 0x003f400f, 0x03fc00ff, 0x0cc007f0, 0x00000e80, 0x00000000, +0x00970801, 0x2a442e01, 0xb8400110, 0xc402f194, 0x40091023, 0x401d2807, +0x01900074, 0x1d000450, 0xd0007400, 0x00074201, 0x0074a019, 0x06c009d1, +0x00000420, 0x00000000, 0x21a3a011, 0x03142081, 0x30604852, 0x3402c163, +0x48045213, 0x800d1002, 0x00d00014, 0x0d000140, 0xd0003620, 0x00034000, +0x0034a00d, 0x444008d0, 0x00000c80, 0x00000000, 0x0025a803, 0x02442011, +0x34748950, 0x5540d010, 0x690d5083, 0x201d2007, 0x01d00074, 0x1d080540, +0xd2007400, 0x06074a81, 0x8074b01d, 0x0e4089d2, 0x00000620, 0x00000000, +0x4007a802, 0x215c8153, 0x36c03570, 0x5c20d110, 0xc00d5403, 0x041f2887, +0x81e0087c, 0x5f0605c1, 0xf00c7c10, 0x00c7c011, 0x007c011f, 0x08c211f0, +0x00000e20, 0x00000000, 0x001d8007, 0x00ad09af, 0x3ac307b0, 0xac40ff00, +0xc08bb203, 0x08ff30bf, 0x1fb013ec, 0xff007ec0, 0xf117fc20, 0x047fc11f, +0x0ffc09fb, 0x1fc11ef0, 0x00000600, 0x00000000, 0x00b10802, 0x094c04d3, +0x35e02530, 0x6c80d700, 0xc045f203, 0x18d304b4, 0xec714b4c, 0xd301b7c0, +0x30237c08, 0x12b4c68d, 0x034c0cd3, 0x0bc40d30, 0x00000420, 0x00000000, +0x0834a013, 0x14540091, 0x3f420510, 0xec0ae100, 0x420d1003, 0x03d51c74, +0x2d104b44, 0xd104f7c8, 0x10877002, 0x00b4402d, 0x2f4400d1, 0x4f402d10, +0x00000200, 0x00000000, 0x0222a007, 0x8024a001, 0x30480192, 0x0482c108, +0x40301003, 0x43011082, 0x20110c04, 0x0100c344, 0x00103446, 0x10424410, +0x08244301, 0x1f441011, 0x00000a00, 0x00000000, 0x20788004, 0x05340961, +0x7b419290, 0x248dc501, 0x511c1007, 0x01c14072, 0x1e141705, 0xa1507342, +0x14073401, 0x4872501c, 0xc72701c1, 0x13401e14, 0x00000200, 0x00000000, +0x01a41012, 0x102c1883, 0x71ca5cb0, 0x0409c342, 0xc01c3817, 0x09c333f2, +0x9c300f0c, 0x830373c0, 0x10273c01, 0x8132c02c, 0x032c00c3, 0x4bc10c30, +0x00000040, 0x00000000, 0x402db802, 0x01dc087f, 0x3fc00f70, 0xdc48db00, +0xc00f742b, 0x28ff003d, 0x0db2037c, 0xbf003dc2, 0xf083f808, 0x003dc08f, +0x03dc08ff, 0x0bc01ff0, 0x00000660, 0x00000000, 0x0067a015, 0x024c0053, +0x36900936, 0x5c04df00, 0xc201302b, 0x001f1807, 0x0130007c, 0x5fa007e0, +0xf0004c01, 0x0007c001, 0x004c011f, 0x57c001f0, 0x00000e00, 0x00000000, +0x30398812, 0x030400c1, 0x38400a10, 0xc490ed0b, 0x400e1203, 0x80e7003b, +0x0e1203b4, 0xcd203b40, 0xd00384a0, 0x203b480e, 0x038480fd, 0x4b480ed2, +0x00000620, 0x00000000, 0x00790003, 0x068403e1, 0x78401e10, 0xb409ed02, +0x621e1097, 0x03ed007b, 0x1e1047b4, 0xed007b48, 0xd2078441, 0x00fb421e, +0x078621ed, 0x0f401ed0, 0x00000400, 0x00000000, 0x02332812, 0x0b0413d1, +0x30401c10, 0x4440cd00, 0x640e1003, 0x00f5023b, 0x2e148ff4, 0xed0c3f40, +0xd003c421, 0x00bf483f, 0x23c742ed, 0x4b409fd2, 0x00000c20, 0x00000000, +0x405da817, 0x09c50373, 0x16c07730, 0x7c805f08, 0xc2053001, 0x095f02d7, +0x9530097c, 0x5f0897c6, 0xf0494d12, 0x0057c0a5, 0x8d4c045f, 0x5fc007f0, +0x00000620, 0x00000000, 0x00870012, 0x487c821f, 0x07c841f2, 0x5c001f00, +0xc023f000, 0x8037808f, 0xa3f048fc, 0x3f028fc8, 0xd290fc86, 0x008fc8a3, +0x08fc063f, 0x4bc023f0, 0x00000c00, 0x00000000, 0x00270810, 0x024c019f, +0x24c009b1, 0x4c818300, 0xc0493202, 0x809f0167, 0x9932064c, 0x931264c0, +0x31027c41, 0x2c24c219, 0x427c0493, 0x40c01930, 0x00000c20, 0x00000000, +0x00a62001, 0x0204069d, 0x24400910, 0x440a9130, 0x40091482, 0x039d21e7, +0x09146e45, 0x9140a458, 0x140e74a0, 0x406451b9, 0x02700391, 0x0451a914, +0x00000800, 0x00000000, 0x0024a018, 0x4344049d, 0x20500c90, 0x44109140, +0x47091002, 0x18bd002f, 0x0a1182c4, 0xb1002c49, 0x1123f408, 0x062c400b, +0x13f400b1, 0x60400b10, 0x00000200, 0x00000000, 0x02202010, 0x0204088d, +0x20488812, 0x05808102, 0x422a1122, 0x02ad00ab, 0x2a100a84, 0xa100b848, +0x100ab402, 0x20a8402a, 0x0ab442e1, 0x40402a10, 0x00000080, 0x00000000, +0x0592b01d, 0x504d161f, 0x84d020b0, 0x4c941325, 0xc0013058, 0x801f0807, +0x0130000c, 0x53200482, 0x30817c00, 0x0004c001, 0x017c0013, 0x74c00330, +0x00000ac0, 0x00000000, 0x012fb819, 0x02fc04ff, 0x27c04bf0, 0x7c008f11, +0xc229f012, 0x229f00a7, 0x29f08a7c, 0x9f00a7c8, 0xf00a7c02, 0x00a7c829, +0x0a7c029f, 0x67c229f0, 0x00000e60, 0x00000000, 0x00afa018, 0x02fc02bf, +0x24c00a70, 0x4c00b310, 0x4049321a, 0x82930524, 0x29300a4c, 0x930024c1, +0x30a24c12, 0x9024c889, 0x024c049f, 0x63c06930, 0x00000e00, 0x00000000, +0x1087081c, 0x0074021d, 0x04400510, 0x04001101, 0x50615008, 0x00114184, +0x01142845, 0x11010450, 0x102944c0, 0xa78450a1, 0x584d141d, 0x73686314, +0x00000c20, 0x00000000, 0x01a3a010, 0x0234068d, 0xa0400850, 0x0402c154, +0x418a140a, 0x02a10428, 0x0a100284, 0xa144a842, 0x14128500, 0x0028404a, +0x0aa402ad, 0x4340ca10, 0x00000e80, 0x00000000, 0xa425a818, 0x8274009d, +0x24500910, 0x04009100, 0x41885002, 0x82b91028, 0x0b1002c4, 0xa1002c41, +0x1103c490, 0x043c410b, 0x42c410fd, 0x63400b10, 0x00000620, 0x00000000, +0x0027a805, 0x027c029f, 0x24c00970, 0x4d009300, 0xc0293002, 0x83930464, +0x3930e244, 0x9306e4c0, 0x10064c0a, 0x00a4c039, 0x0e6c029d, 0x17404930, +0x00000e20, 0x00000000, 0x02658014, 0x027c009f, 0x27c289f0, 0x7c008f00, +0xc009b202, 0x85872967, 0x89f0023c, 0x9f0c63c0, 0xf0a63c20, 0x0567c298, +0x263c058f, 0x53c059f0, 0x00000600, 0x00000000, 0x04850814, 0xa07c801f, +0x02c021f0, 0x4c001300, 0xc1033000, 0x003f010c, 0x03f0c0fc, 0x3f008cc0, +0xd100cc02, 0x008fc823, 0x88fcb23b, 0x50c023f0, 0x00000420, 0x00000000, +0x00dca014, 0x2db4007d, 0x144007d1, 0x44207100, 0x50351001, 0x4b5d0414, +0xa5d00574, 0x5d001450, 0xd0094502, 0x44d74075, 0x2d740351, 0x514017d0, +0x00000200, 0x00000000, 0x02f2a014, 0x073410cd, 0x32400dd0, 0x3400c100, +0x42be1003, 0x02ed1838, 0x0fd00394, 0xeda07840, 0xd0478490, 0x003b404e, +0x07b401e1, 0x50421ed2, 0x00000a00, 0x00000000, 0x00288005, 0x03b400ed, +0x384203d0, 0xb400e10b, 0x400c1413, 0x10ed0038, 0x0cd00bb4, 0xcd10b841, +0xd00b84a0, 0x0073400e, 0x43b410e1, 0x15411ed0, 0x00000200, 0x00000000, +0x00781015, 0x05bc012f, 0x7ec01ef3, 0xbc010341, 0xc01e303f, 0x41ee2078, +0x1ef007be, 0xef0078c0, 0xf0878c41, 0x207bc41e, 0x07bc01e3, 0x54c01cf0, +0x00000040, 0x00000000, 0x0025b810, 0x017c00df, 0xb74001f0, 0x4d201f05, +0xc801f803, 0x001f2007, 0x01f0007c, 0x5f0007c0, 0xf0007c80, 0x0007c001, +0x807c0017, 0x43c001f0, 0x00000660, 0x00000000, 0x007fa000, 0x06fc01ff, +0x75c89cf0, 0x7c49f32a, 0xc01d3007, 0x81d30074, 0x1ff207fc, 0xd31877c4, +0x30277c45, 0x007c409f, 0x27cc09f3, 0x00c21f30, 0x00000e00, 0x00000000, +0x00398815, 0x02b4006d, 0x704092d0, 0xb481c102, 0x40555007, 0x0fc10074, +0x5ed03784, 0xe106fb42, 0x1227b415, 0x203849ae, 0x2b8480e1, 0x54488e12, +0x00000620, 0x00000000, 0x00190000, 0x02b4002d, 0x31404fd0, 0xb4042140, +0x404a1013, 0x2ce1043a, 0x4e922384, 0xe9123b44, 0x5233b484, 0x203a608e, +0x238400e1, 0x00401e18, 0x00000400, 0x00000000, 0x00132804, 0x0234034d, +0x304201d2, 0x34000100, 0x561c9003, 0x06015482, 0x30d06c05, 0x59640344, +0x16043491, 0x46427100, 0x04050901, 0x1051101c, 0x00000c20, 0x00000000, +0x0155a815, 0x03708edf, 0x3dc001f0, 0xfc40d330, 0xc00d3003, 0x80d300b6, +0x2db00b5e, 0x9b8077c0, 0x504b7c41, 0x0836c01d, 0x4f4c01d3, 0x54c10d30, +0x00000620, 0x00000000, 0x00070001, 0x227c009f, 0x37c02df0, 0x3c025f00, +0xc08d7203, 0x08df0035, 0xadfb0b5e, 0x930037c0, 0xb04b7c02, 0x0835e00d, +0x1b7c00df, 0x07c00df0, 0x00000c00, 0x00000000, 0x001b0880, 0x15bc0933, +0x38d01334, 0x7c01fb00, 0xc90e7003, 0x00f30038, 0x9ff117fc, 0xbd217fc9, +0xf107c881, 0x003fc80f, 0x03f093ff, 0x03c00ff2, 0x00000c22, 0x00000000, +0x04c62081, 0x88740191, 0x35009d12, 0x74185500, 0x40010003, 0x09115684, +0x01d00074, 0x1d10074c, 0xd00c449a, 0x0ac74681, 0x2864621d, 0x074091d1, +0x00000802, 0x00000000, 0x0224a001, 0x097400d1, 0x36002110, 0x74009900, +0x420dc003, 0x00110004, 0x01d08854, 0x15000741, 0xd0105500, 0x08074001, +0x0074201d, 0x074209d0, 0x00000200, 0x00000000, 0x08202010, 0x00240001, +0x30444c10, 0x34000101, 0x40441ac3, 0x10010880, 0x80d01836, 0x0d950342, +0xd0201440, 0x00034000, 0x0024000d, 0x434008d0, 0x00000080, 0x00000000, +0x4006b000, 0x017e2013, 0xfcc0b130, 0xfc059b02, 0xc0f97007, 0x21030844, +0x71f0147c, 0x1f0043c1, 0xf01c1c1b, 0x0003c000, 0x003c000f, 0x03c008f0, +0x00000ac0, 0x00000000, 0x080fb805, 0x00fe003f, 0x7f40dff0, 0xfc131f02, +0xc89fb147, 0x15ff00ff, 0x5df01f74, 0xbf047fc0, 0xf237ec05, 0x003fc00f, +0x03fc40ff, 0x17c007f0, 0x00000e60, 0x00000000, 0x0b0f8003, 0x33cc8c3f, +0x2bc003f0, 0xfc063342, 0xc00fb033, 0x00ff003f, 0x0ff003cc, 0xbf003cc0, +0x3003fc00, 0x003dc00f, 0x03cc00f3, 0x0cc007f0, 0x00000e00, 0x00000000, +0x03070801, 0x324c0e5d, 0xaf40e1d0, 0x74061100, 0x4809100b, 0x800d0007, +0x01d28064, 0x1d000450, 0x14007420, 0x00045001, 0x00440011, 0x044009d0, +0x00000c20, 0x00000000, 0x0123a011, 0x1124000d, 0xa1490050, 0x34820521, +0x4004908b, 0x800d2001, 0x00d20025, 0x0d000160, 0x92003480, 0x60004200, +0x00270009, 0x445008d0, 0x00000e80, 0x00000000, 0x0005a807, 0x0044041d, +0x276001d0, 0x74005100, 0x000d1003, 0x021d0507, 0x61d80064, 0x1d000440, +0x90007620, 0x00844001, 0x00640819, 0x0c0009d0, 0x00000620, 0x00000000, +0x00c7a802, 0x326d031d, 0x27c0d5f0, 0x7c801700, 0x847db003, 0x081f00c7, +0x01f0144c, 0x1f0105c0, 0x90687c02, 0x0084c051, 0x386c011b, 0x08c281f2, +0x00000e20, 0x00000000, 0x02158003, 0x02fc016f, 0x2ec01ff0, 0xa0083f00, +0xc049f00b, 0x01ff0077, 0x1df00f5c, 0x9f0277c0, 0x70077c08, 0x0936c09d, +0x075c00d3, 0x1fc01dd0, 0x00000600, 0x00000000, 0x44a50802, 0x004c089f, +0x23c40530, 0x7c005304, 0xd005b807, 0x0ec340b4, 0x2d34035c, 0x934034d0, +0x740b4d00, 0x4034d00d, 0x034d02d3, 0x08d00d34, 0x00000420, 0x00000000, +0x00d48017, 0x10450890, 0x2c400db0, 0x5c055108, 0x42bd1807, 0x02d10af4, +0xbd102f45, 0x9b0af400, 0x10af442b, 0x02f442bd, 0xaf440bd0, 0x4c40bd10, +0x00000200, 0x00000000, 0x0212a007, 0x11040381, 0xa0400811, 0x24000100, +0x40001083, 0x81010001, 0x01920004, 0x41000200, 0x10006400, 0x08044001, +0x00240010, 0x1d400110, 0x00000a00, 0x00000000, 0x00688004, 0x058401e1, +0x6c481898, 0xd4016100, 0x410e1007, 0x81e11479, 0x1e904786, 0xe9047a41, +0x1047a411, 0x0478411e, 0xc7a431e1, 0x11419e10, 0x00000200, 0x00000000, +0x80b01012, 0x214c4c53, 0x21c24c30, 0x3c080300, 0xe80c1003, 0x88c32031, +0x4cb0031c, 0xc30132c0, 0x70132c02, 0x0230c00c, 0x132c02c3, 0x49c00c30, +0x00000040, 0x00000000, 0x002db802, 0x01fc0874, 0xadc00fe0, 0xbc205f44, +0xe00f7043, 0x08fe023e, 0x0d70037c, 0xf7003de0, 0xf003dc08, 0x203fc08f, +0x03dc00ff, 0x0ac21ff8, 0x00000660, 0x00000000, 0x4017b011, 0x027c009b, +0xa7ca01f0, 0x3c945b00, 0xd0013407, 0x00130804, 0x0134007c, 0x534007c0, +0xf2804d80, 0x0007c001, 0x007c001f, 0x54d001f0, 0x00000e00, 0x00000000, +0x803d8012, 0x03b420e3, 0x2f440ad0, 0x9c006101, 0x400f1013, 0x20e1003c, +0x0e1003b4, 0xf1803b40, 0x70038400, 0x003b420e, 0x03a400ed, 0x48400ed0, +0x00000620, 0x00000000, 0x00790003, 0x0eb401e5, 0x6b4016d0, 0xf4016100, +0x401e1007, 0x01e5007a, 0x1e5887b4, 0xe9887b40, 0xd0078621, 0x007b401e, +0x07b401ed, 0x0c401ed0, 0x00000400, 0x00000000, 0x80332812, 0x1f3640c5, +0x23401dd8, 0x14004940, 0x400e1003, 0x1be544b8, 0x1e103bb4, 0xe900bb40, +0x500b8012, 0x243b404e, 0x0ba40bed, 0x48400ed0, 0x00000c20, 0x00000000, +0x081da817, 0x0df40277, 0x1740d7d0, 0xf4855b00, 0xe0053000, 0x025701d4, +0xc570017c, 0x5b041740, 0xf0154400, 0x24174125, 0x1d7c005f, 0x5cc107f0, +0x00000620, 0x00000000, 0x00070012, 0x007c9010, 0x07c001b0, 0x5c001700, +0xe002f200, 0x003b008b, 0x22d008bc, 0x27210bca, 0x7010bc80, 0x000bc002, +0xc0b0102f, 0x4bc022f2, 0x00000c00, 0x00000000, 0x02270810, 0x021c2593, +0x25c20970, 0x7c00d300, 0xc009f000, 0x20932026, 0x0930027c, 0x932165ca, +0x31026c00, 0x0024c009, 0x824c0093, 0x40c00938, 0x00000c20, 0x00000000, +0x00662001, 0x0a744195, 0xe0480910, 0x5c499100, 0x5009d802, 0x209140a4, +0x29144a74, 0x9b406459, 0x14024580, 0x4024d809, 0x02450491, 0x045d2904, +0x00000800, 0x00000000, 0x0820a018, 0x427400d9, 0x24600958, 0x74009145, +0x4219d0a2, 0x00b1002c, 0x0b1002f6, 0xb1882c40, 0x1002c400, 0x802e400b, +0x02c400b1, 0x62400b90, 0x00000200, 0x00000000, 0x02202210, 0x0a3408c5, +0x2441c810, 0x34088100, 0x403ad022, 0x02a100b8, 0x2a100ab4, 0xa980a840, +0x100a8402, 0x00aa402a, 0x0a8482a1, 0x42402a18, 0x00000080, 0x00000000, +0xa586a01d, 0x005c161b, 0x05d06170, 0x7c561305, 0xc001d0d8, 0x00130004, +0x01300034, 0x130001c0, 0x32004c00, 0x0006e001, 0x814c0013, 0x76c20230, +0x00000ac0, 0x00000000, 0x212f8819, 0x0afc04bf, 0x2fc04bf1, 0xdc049f0d, +0x4029f010, 0x029f00a5, 0x29f20a7c, 0xd720a7c0, 0xe20a7c02, 0x00b5c02d, +0x0a7c029f, 0x65c029f0, 0x00000e60, 0x00000000, 0x133faa18, 0x0afc10bf, +0xa8c06978, 0xdc029301, 0xc049301d, 0x629f0224, 0x69f0027c, 0x9f8627c1, +0xf0327c0c, 0x2827ca49, 0x0a7c209f, 0x63c009f0, 0x00000e00, 0x00000000, +0x0307001c, 0x0874001d, 0x046441d0, 0x64001140, 0x5061101c, 0x421d0294, +0x21d29874, 0x1d218748, 0xd0917484, 0x00074141, 0x0834021d, 0x734143d0, +0x00000c20, 0x00000000, 0xa123a010, 0x1a34908d, 0xa2406850, 0x14068100, +0x418a108a, 0x06ad0128, 0x4ad04ab4, 0xa5002b60, 0xd00ab402, 0x05ab402a, +0x0ab416ad, 0x43402ad0, 0x00000e80, 0x00000000, 0xa425a818, 0x8274009d, +0x264208d0, 0x64409110, 0x42491102, 0x00bd002c, 0x0bd802f4, 0xbd212f6b, +0xd012f600, 0x002f440b, 0x8af410bd, 0x63404bd0, 0x00000620, 0x00000000, +0x0027a005, 0x127cb09f, 0x26500970, 0x5c009300, 0xc1293402, 0x1b9f02e4, +0x39f06e7c, 0x9f0127c4, 0xd08e7c03, 0x0267c899, 0x027c039d, 0x17c049f0, +0x00000e20, 0x00000000, 0x00258014, 0x027c459f, 0x21c099f0, 0x6c08df00, +0xc009f020, 0x009f0067, 0x99f0027c, 0x9f0067c8, 0xf0027c02, 0x0827c309, +0x167c229f, 0x53c009f0, 0x00000600, 0x00000000, 0x00850014, 0x007c801f, +0x07c80172, 0x6d001300, 0xd003f200, 0x0037000c, 0x233000cc, 0x33048fc0, +0xf000fc00, 0x200fc003, 0x08fc023f, 0x53c02330, 0x00000420, 0x00000000, +0x001ca014, 0x41dc007d, 0x1f400510, 0x44405106, 0x4005d084, 0x005c0814, +0x05148145, 0x55401748, 0xd1817400, 0x08170605, 0x0174605d, 0x53400714, +0x00000200, 0x00000000, 0x01328014, 0x032408cd, 0xf3400c50, 0x44894100, +0x400dd007, 0x08c50230, 0x0c900324, 0xc1003364, 0xd023341a, 0x0233408c, +0x233400cd, 0x53400c10, 0x00000a00, 0x00000000, 0x00388805, 0x049440ad, +0x3f404e10, 0x84016140, 0x4002d073, 0x212d0008, 0x029004a4, 0x21004b62, +0x9000b400, 0x000b4002, 0x84b4002d, 0x17400210, 0x00000200, 0x00000000, +0x00780015, 0x073c016f, 0x5bc01e7a, 0x8c894300, 0xc013f207, 0x01f70058, +0x16b005ec, 0xe3006fc0, 0xf007bc01, 0x007bc01e, 0x05bc016f, 0x57c01632, +0x00000040, 0x00000000, 0x2025a810, 0x025c001f, 0x17e16de0, 0x5c005f00, +0xc80df003, 0x801f2027, 0x0970025c, 0x1f2017c8, 0xf2007c80, 0x0007c801, +0x027c809f, 0x43c009f0, 0x00000660, 0x00000000, 0x027fa000, 0x06ca49ff, +0x7fc11ff0, 0xbc017f00, 0xc01f3007, 0x09bb426c, 0x9ff017fc, 0x3f025fc0, +0xf026fc09, 0x026fc093, 0x36cd09ff, 0x03c01f34, 0x00000e00, 0x00000000, +0x00198015, 0x188620ed, 0x1bc00ed8, 0xb4802d02, 0x40031003, 0x0c610018, +0x829000b4, 0xed232a40, 0xd101b408, 0x001b440e, 0x31ec002d, 0x574802b0, +0x00000620, 0x00000000, 0x08390200, 0xc284002d, 0x2b400ed0, 0xf4086d00, +0x41021003, 0x00a10008, 0x86d811b4, 0x2d000960, 0x5002b408, 0x042b4102, +0x3096006d, 0x034016d0, 0x00000400, 0x00000000, 0x24032804, 0x0604210d, +0x01402cd0, 0x34000c00, 0x402c1003, 0x004500f0, 0xb8902234, 0xcd023260, +0xd00d3403, 0x00d3403c, 0x0f160d8d, 0x13403850, 0x00000c20, 0x00000000, +0x2805a815, 0x054405dd, 0x27400fd0, 0x7c055c00, 0xd09d3d17, 0x81530274, +0x19f02a7c, 0xdf02f5c0, 0xf0497410, 0x0057401d, 0x4f54009f, 0x57c0a9d0, +0x00000620, 0x00000000, 0x22330001, 0x017c020d, 0x27c10df2, 0x7c005f10, +0xc801f003, 0x08930207, 0x05c0017c, 0x1f200748, 0xf01a7404, 0x28a7c001, +0x0064805f, 0x07808580, 0x00000c00, 0x00000000, 0x020f0880, 0x05ec20fb, +0x0cc40e72, 0xcc003300, 0xc003f003, 0x106f101c, 0x03f004cc, 0xff002c43, +0x3281fc00, 0x041cc30f, 0x41fc0033, 0x03c213f0, 0x00000c22, 0x00000000, +0x00a62081, 0x0f450411, 0x04400d10, 0x4d091110, 0x500dd003, 0x129d2024, +0x9dd20745, 0x1d0014d0, 0x16124580, 0x4124d001, 0x025c48d5, 0x07401dd1, +0x00000802, 0x00000000, 0x0084a001, 0x100400d1, 0x30400d50, 0x44804102, +0x400cd293, 0x001d0024, 0x08d01244, 0x0d801540, 0x10000400, 0x00024000, +0x02740091, 0x074049d0, 0x00000200, 0x00000000, 0x40102010, 0x01040041, +0x10500c10, 0x04000100, 0x4000d003, 0x20cd8810, 0x04d00104, 0xcd902140, +0x18030420, 0x0032420c, 0x81042041, 0x43400498, 0x00000080, 0x00000000, +0x0006a000, 0x004c2083, 0x24c00f70, 0x44001340, 0xc001f003, 0x001f0000, +0x01f00044, 0x1f000540, 0x32005c00, 0x0006e001, 0x007c0003, 0x03c001f0, +0x00000ac0, 0x00000000, 0x800f9805, 0x03dc803f, 0x0fc00ff8, 0xdc003f00, +0x400ff103, 0x00fc003f, 0x0ff003fc, 0xff003cc2, 0xf003de00, 0x003dc00f, +0x03dc00ff, 0x17c00ff1, 0x00000e40, 0x00000000, 0x000fa003, 0x00fc083f, +0x8fe003f0, 0xed801300, 0xc003f000, 0x003f000c, 0x01f0007c, 0xff203f80, +0xf100f400, 0x006cc057, 0x03cc01bb, 0x0cc403f3, 0x00000e00, 0x00000000, +0x00370801, 0x037400dd, 0x37410df0, 0x4400d500, 0x080dd003, 0x20cd0030, +0x0dd00374, 0xdd003640, 0xd0002400, 0x20154005, 0x03540a55, 0x054005d0, +0x00000c20, 0x00000000, 0x0013a011, 0x0234000d, 0x134048d0, 0x24000501, +0x5200d003, 0x004d0012, 0x04d20034, 0xcd303360, 0x92003420, 0x02004284, +0x0304808d, 0x446001d8, 0x00000e80, 0x00000000, 0x0035a803, 0x017400dd, +0x27400550, 0x4402d518, 0x422cd000, 0x009d0826, 0x09908b64, 0xdd803641, +0xd2a06480, 0x00354005, 0x83500055, 0x0d4101da, 0x00000620, 0x00000000, +0x0077a802, 0x017c00df, 0x274605d0, 0x6c005718, 0xc401f800, 0x041f0026, +0x01f0017c, 0xdf003740, 0xb3407c89, 0x008cc047, 0x024c00bf, 0x08802cf0, +0x00000e20, 0x00000000, 0x00dd8007, 0x02fc003f, 0x1fc80bf0, 0xfc20bf00, +0xc02ff803, 0x00ff101d, 0x0ff082fc, 0xff143fc4, 0xf000ec31, 0x003bc907, +0x02fc204f, 0x1f810ff0, 0x00000600, 0x00000000, 0x00350802, 0x037c00db, +0x36c04df0, 0x7c025b20, 0xc0e1f003, 0x12530437, 0xe5f0694c, 0xdf1036c0, +0x31097c00, 0x0307c084, 0x034d0c97, 0x0bc00df0, 0x00000420, 0x00000000, +0x0014a013, 0x80742d11, 0x874021d0, 0x74009c08, 0x401dd000, 0x82914047, +0x29d00a45, 0xdd00fd52, 0x14057660, 0x00b74025, 0x43440251, 0x4dc23d71, +0x00000200, 0x00000000, 0x0212a007, 0x1c240009, 0x076000d0, 0x34009d01, +0x402cd000, 0x4a812083, 0x28d00604, 0xcd023140, 0x111434c0, 0x8043441c, +0x1f340181, 0x1f4018d0, 0x00000a00, 0x00000000, 0x20788004, 0x47b401e1, +0x7b405ed0, 0xb4016d04, 0x4212d01f, 0x0d61007b, 0x56d02584, 0xed107940, +0x1034b409, 0x0833481e, 0x87a44161, 0x114c1e50, 0x00000200, 0x00000000, +0x01141012, 0x023c000b, 0x53c058f0, 0x3c018f03, 0xc0dcf017, 0x0dc30653, +0xddf0160c, 0xcd0371c0, 0x30573419, 0x0203c00c, 0x033c0083, 0x4bc300f8, +0x00000040, 0x00000000, 0x403db802, 0x01f400ff, 0x27c087f0, 0xfc005f10, +0xc003f000, 0x001e1027, 0x93f801fc, 0xff053cc8, 0xf123fc40, 0x013fc08f, +0x23dc017f, 0x0be403f0, 0x00000660, 0x00000000, 0x1037a015, 0x017c80d3, +0x27c005f0, 0x5c00d700, 0xc00df000, 0x019c0027, 0x0934034d, 0xd3407784, +0x32064f21, 0x6044d21d, 0x86490093, 0x54901d3c, 0x00000e08, 0x00000000, +0x00118812, 0x02b40021, 0x1b420ad0, 0xac003d08, 0x0402d283, 0x207d201b, +0x061000c4, 0xe10139c0, 0x10828600, 0x003c400f, 0x02c40071, 0x48600f38, +0x00000620, 0x00000000, 0x047d0003, 0x07b401e1, 0x7b403ed0, 0xb611ed00, +0x401ed007, 0x01e9007b, 0x1e910784, 0xe1007b48, 0x1207c411, 0x0048401e, +0x079401ad, 0x0c4c1e10, 0x00000400, 0x00000000, 0x04d72812, 0x24341201, +0x874001d0, 0x24001d01, 0x4001d800, 0x200d0003, 0x00902004, 0x81003140, +0x102f0601, 0x09b0420c, 0x27040049, 0x48408c12, 0x00000c20, 0x00000000, +0x4095a817, 0x057c1053, 0x97c015f0, 0x7c005700, 0xc005fa01, 0x605b1017, +0x05b0854c, 0x530017c4, 0x0001cc00, 0x00944005, 0x015c207b, 0x5c403730, +0x00000620, 0x00000000, 0x000f0012, 0x00fc403f, 0x0fc403f1, 0xfc003f00, +0xc023f808, 0x003f000f, 0x237108fc, 0x1f108740, 0xf5486c80, 0x100fcc21, +0x08604227, 0x4bc10170, 0x00000c00, 0x00000000, 0x02670810, 0x027c009b, +0x24c019f0, 0x6c209b00, 0xc219f00e, 0x039f04e4, 0x1930267c, 0x9f00e70a, +0xf0027c22, 0x0027c259, 0x164c059f, 0x42c009f0, 0x00000c20, 0x00000000, +0x00262001, 0x02741091, 0xa44aa9d8, 0x44009300, 0x5479d04a, 0x039f00e4, +0x09140274, 0x9d10e64a, 0x70367c00, 0x00e74049, 0x1244849d, 0x05c039d0, +0x00000800, 0x00000000, 0x00248018, 0x12f400b9, 0xac402bd3, 0xe640b100, +0x404bd002, 0x22bd042c, 0x8b1042b4, 0x9d102762, 0xd0827480, 0xb1bf4809, +0x024400bd, 0x624069d0, 0x00000200, 0x00000000, 0x02282010, 0x02b408a1, +0x28608ad0, 0x8428a102, 0x422ad002, 0x02a588a8, 0x2a120ab4, 0xcd00b248, +0x500a1422, 0x00ab4028, 0x8a0402ad, 0x434028d0, 0x00000080, 0x00000000, +0x0086b01d, 0x017c221b, 0x84d021d0, 0x6c021300, 0x4201f000, 0x001d0804, +0x0730007c, 0x0f000380, 0xe0807480, 0x2007c001, 0x004d003f, 0x76e001f2, +0x00000ac0, 0x00000000, 0x012fb819, 0x027c049f, 0x27c049f0, 0x7c049f41, +0xc409f002, 0x009f2027, 0x09e0027c, 0xbf00a7c0, 0x6082bc20, 0x00a7c029, +0x82fc829f, 0x65c00bf0, 0x00000e60, 0x00000000, 0x002fa018, 0x02bc02b3, +0x2cc00b70, 0xec089310, 0xc0093002, 0x809608a7, 0x29c0027c, 0x8f11a748, +0xf0823c00, 0x00e4c499, 0x024c0793, 0x638029f0, 0x00000e00, 0x00000000, +0x0107081c, 0x00740011, 0x00400170, 0x44001500, 0xc1411001, 0x40100105, +0x41920074, 0x17000740, 0x92117640, 0x03044001, 0x50440411, 0x720047d2, +0x00000c20, 0x00000000, 0x0423a010, 0x03348081, 0x205008d1, 0x25048100, +0x440a1102, 0x14a1692b, 0x4ad052b4, 0xad222b64, 0x9042b490, 0x0128406a, +0x02a4ccad, 0x43404ad0, 0x00000e80, 0x00000000, 0x4025a818, 0x82740091, +0x244008d0, 0x46009500, 0x48091c02, 0x04f1002f, 0x0b9102f4, 0xb5003f02, +0x9003f408, 0x082c400b, 0x12f440f9, 0x62448bd0, 0x00000620, 0x00000000, +0x0027a805, 0x427c0093, 0x64c109f0, 0x2c809328, 0xc0093006, 0x00930067, +0x19f0827c, 0x9f0827c0, 0xb0067400, 0x0424d009, 0x026c009b, 0x178009f0, +0x00000e20, 0x00000000, 0x00e58014, 0x027c009f, 0x27c00970, 0x7c009f02, +0xc009f212, 0x409f1125, 0x89b0027c, 0x970027c0, 0xb0ca7c40, 0x0027c009, +0x424c0097, 0x52c009f0, 0x00000600, 0x00000000, 0x00050814, 0x005c5013, +0x04c001b0, 0x6c001300, 0xc003f008, 0x843f100f, 0x43f000fc, 0x33000fc4, +0xa180cc00, 0x248bc0c2, 0x00c00823, 0x53c003f2, 0x00000420, 0x00000000, +0x0014a014, 0x21c41071, 0x1c4c0711, 0xc4005100, 0x4005d04d, 0xc25d0057, +0x05d00974, 0x51509740, 0x50095413, 0x00d5c005, 0x09440b51, 0x534217d0, +0x00000200, 0x00000000, 0x0036a014, 0x8b1483c1, 0x74500c90, 0x0480d100, +0x420ed04f, 0x0be5013f, 0x3ed063b4, 0xe1863b22, 0x90878410, 0x403b402e, +0x079501e1, 0x53400ed0, 0x00000a00, 0x00000000, 0x40288005, 0x078401a1, +0x08440610, 0x8400e144, 0x420ed003, 0x00ed043b, 0x2ed003b4, 0xf1207b44, +0x000fd481, 0x0479400e, 0x0b9401e1, 0x17410ed0, 0x00000200, 0x00000000, +0x006c1015, 0x079c0173, 0x58d013b0, 0x8d05f300, 0xc01ef004, 0xe1ef007b, +0x1ef907b4, 0xe3007fc0, 0xb1078e81, 0x1033c81e, 0x879c00c3, 0x57c01ef2, +0x00000040, 0x00000000, 0x0025b810, 0x0174001f, 0x27c009f0, 0x5c60df08, +0x4401f000, 0x001f0007, 0x01f8007c, 0x5f000780, 0xe0007c00, 0x0807c001, +0x806c201d, 0x43c001b8, 0x00000660, 0x00000000, 0x407fa000, 0x05fc01f3, +0x6fc01df0, 0x4c59ff00, 0xc09ff027, 0x29f3227f, 0x1df1a77c, 0x97107704, +0xb6277c09, 0x407cd21f, 0x07cd01f3, 0x00d01f34, 0x00000e00, 0x00000000, +0x022d8015, 0x21bc0021, 0x538054d0, 0x2c81fd01, 0x40dcd105, 0x0dc727f2, +0x1ed02fa6, 0x81017341, 0xb0177c19, 0x003ac00e, 0x23ac00e3, 0x54400eb0, +0x00000620, 0x00000000, 0x00290800, 0x03b400e1, 0x0b40c2d0, 0x8404ed02, +0x424ed853, 0x24e5823b, 0x0ed933b4, 0xa5013b44, 0x1023b404, 0x0038400e, +0x038400e9, 0x00401e10, 0x00000400, 0x00000000, 0x00632804, 0x19101101, +0x3140a8d0, 0x2504dd10, 0x4020d001, 0x03050083, 0x10d22c24, 0x11000340, +0x10001401, 0x02c00000, 0x00000b01, 0x10401010, 0x00000c20, 0x00000000, +0x01f5a815, 0x033001d3, 0xb74a29f0, 0x0c83ff00, 0xc02df103, 0x53d70277, +0xbdf0a37c, 0xd71837c0, 0x10033401, 0x00b4020d, 0x034002da, 0x54c04c10, +0x00000620, 0x00000000, 0x01270001, 0x077c08df, 0x07c441f0, 0x7d08df06, +0xc1a5f213, 0x22df00b6, 0x2df0836c, 0xdf8037e0, 0xf00b7c42, 0x01b7c02d, +0x0b7c44df, 0x07c00df0, 0x00000c00, 0x00000000, 0x802f0880, 0x03cd0273, +0x1cc04730, 0xfc80f320, 0xc05bb200, 0x11ff00bf, 0x0ff017fc, 0xf310b242, +0x300fcd40, 0x083bc05f, 0x07cc01e3, 0x00c00f30, 0x00000c22, 0x00000000, +0x20222081, 0x01440155, 0x24400d50, 0x4400db00, 0x487d3000, 0x0a1d2047, +0x11d00874, 0x55200450, 0x50004410, 0x03c74471, 0x40144211, 0x06c29151, +0x00000802, 0x00000000, 0x00348001, 0x05464081, 0x20400810, 0x7400d108, +0x400d9012, 0x001d8007, 0xa0d00074, 0x11000542, 0x18004400, 0x00074001, +0x10440411, 0x04400911, 0x00000200, 0x00000000, 0x00242010, 0x01060005, +0x10506050, 0x0480c901, 0x49041110, 0x040d1803, 0x00d02034, 0x05030140, +0x50584464, 0x40024000, 0x00450001, 0x42400950, 0x00000080, 0x00000000, +0x0026a000, 0x034e0091, 0xc4c07530, 0x7c07d300, 0xc0f9b0ae, 0x091f03c7, +0x31f00c7c, 0x130047c0, 0x301c440b, 0x1003c000, 0x800c0003, 0x00c00830, +0x00000ac0, 0x00000000, 0x002fa805, 0x00fc003f, 0xf7c89ff0, 0xfc97dd05, +0xc01d721c, 0x0ddf2277, 0x9ff137fc, 0xbf08fec1, 0xf987fc07, 0x003fe40f, +0x03fe00ff, 0x17c007f8, 0x00000e60, 0x00000000, 0x003fa003, 0x03cc08f3, +0x3f440f70, 0xcc00ff00, 0xc00ff183, 0xc0ff303f, 0x0fb103c4, 0xf6183c80, +0x3003fc40, 0x003cc00f, 0x03bcc0f3, 0x0ed40f81, 0x00000e00, 0x00000000, +0x10070801, 0x00450411, 0x070001b0, 0x54001d00, 0xc001d000, 0x001d0005, +0x00500054, 0x15080548, 0x12007400, 0x00044801, 0x03f40011, 0x04410f50, +0x00000c20, 0x00000000, 0x40210015, 0x001080c1, 0x32480810, 0x0480cd20, +0x0008d203, 0x80cd2023, 0x0c520314, 0xc1202148, 0x10033080, 0x0030400c, +0x033400c9, 0x45404c50, 0x00000e80, 0x00000000, 0x2017a807, 0x83540011, +0x07600510, 0x54041d00, 0x40059000, 0x001d0014, 0x41500054, 0x15101540, +0x14106400, 0x40045001, 0x83742019, 0x0c400d50, 0x00000620, 0x00000000, +0x001fa882, 0x03dc82b3, 0x1ec00714, 0xcc007f08, 0xc007e000, 0x003f001f, +0x077000dc, 0x37001dc0, 0x3001fc40, 0x025cc097, 0x037c897b, 0x0bc00cb4, +0x00000e20, 0x00000000, 0x00298007, 0x006c006f, 0xafc00bb3, 0xfc41bf00, +0xc00bf003, 0x00ff102f, 0x1af003fc, 0xff002fc4, 0xf006fc00, 0x002fc009, +0x03fc00b7, 0x1fc00fb1, 0x00000600, 0x00000000, 0x00050802, 0x101c4593, +0x17c04130, 0x4c044301, 0xc041f010, 0x04030104, 0x45b0100c, 0x131100c0, +0x30114c04, 0x0214c045, 0x233c0c5f, 0x0bc40db0, 0x00000420, 0x00000000, +0x0174a013, 0x07440051, 0x67401d90, 0x44039100, 0xc01dd007, 0x01d10076, +0x19100744, 0xd0007440, 0x10064401, 0x08e44019, 0x03f4209d, 0x4f400fa0, +0x00000200, 0x00000000, 0x0072a007, 0x0b240041, 0xa2402c90, 0x34028500, +0x402cd08b, 0x02c900b0, 0x28100b04, 0xc800b240, 0x100a4402, 0x01e44029, +0x4334038d, 0x1e424c10, 0x00000a00, 0x00000000, 0x00408004, 0x24a401a1, +0x5b401294, 0xa4016140, 0x4210d004, 0x0929004a, 0x161c0484, 0x29024a40, +0x10010501, 0x00584006, 0x07a4016d, 0x13401e90, 0x00000200, 0x00000000, +0x40201012, 0x40348453, 0x26c14834, 0x3c109704, 0xc108f043, 0x18db0424, +0x0830434c, 0xd90626c1, 0x30720c10, 0x0020c188, 0x033c008f, 0x4bc04c30, +0x00000040, 0x00000000, 0x001db802, 0x83dd00bf, 0x1fc00750, 0xdd807f00, +0xc807f000, 0x2837421f, 0x057000fd, 0x174215d0, 0xf0117c08, 0x001fc047, +0x03fc417e, 0x0bc12ff0, 0x00000660, 0x00000000, 0x40512810, 0x034d0013, +0x44c00530, 0x4c001300, 0xc0053800, 0x00100014, 0x0130004c, 0x130015d0, +0x32006c00, 0x2844c811, 0x0b740013, 0x56801df0, 0x00000e00, 0x00000000, +0x0029091a, 0x008400c1, 0x38480a11, 0x8440e100, 0x480a0003, 0x00e3002a, +0x0e120384, 0xe1202842, 0x10038480, 0x0038400e, 0x33b400e1, 0x48404fd0, +0x00000600, 0x00000000, 0x00490000, 0x04a4112d, 0x4a401290, 0xa4612900, +0x4012d204, 0xa1298049, 0x109084a4, 0x2d004a40, 0x10049401, 0x00484012, +0xb7b4a121, 0x0e405ed0, 0x00000400, 0x00000000, 0x00b12812, 0x030401cd, +0xb2406d90, 0x6406c901, 0x406dd01f, 0x07c101b1, 0x6d901f24, 0xc104b240, +0x101b1407, 0x01b0406c, 0x033406c1, 0x48400cd0, 0x00000c20, 0x00000000, +0x00158017, 0x016d005f, 0x96e025b8, 0x64425b00, 0xc025f00d, 0x035b0095, +0x25b00d6c, 0x5f009640, 0x30095c43, 0x0094c025, 0x017c0273, 0x5ec005d0, +0x00000620, 0x00000000, 0x200f0812, 0x80dc0233, 0x8dd42376, 0xdd023750, +0x50232420, 0x083f600e, 0x037420dd, 0x3c608cc0, 0xf400e10a, 0x400fd003, +0x087c003f, 0x4bc001d0, 0x00000c00, 0x00000000, 0x06270810, 0x020c058f, +0x64c29930, 0x4e008f00, 0xc0093002, 0x40870860, 0x0930020c, 0x832020c0, +0x31060c00, 0x0027c019, 0x063c009f, 0x43c00930, 0x00000c20, 0x00000000, +0x00260001, 0x0244049d, 0x64e0293c, 0x6e009f02, 0xc129b002, 0x009b0266, +0x09b0026c, 0x9b04a4c0, 0xb0266c12, 0x00274099, 0x2274009d, 0x07400910, +0x00000800, 0x00000000, 0x00248018, 0x82c600fd, 0x2e500b90, 0xc450bd20, +0x400b1002, 0x00b5002c, 0x0b1002c4, 0xb1002c40, 0x9002c400, 0x002f400b, +0x027480b9, 0x63400910, 0x00000200, 0x00000000, 0x00282010, 0x0a8508ad, +0xa8402e18, 0xa422a500, 0x402a100a, 0x02a100a8, 0x2a900aa4, 0xa920aa44, +0x900aa402, 0x00ab482a, 0x023480ad, 0x43482814, 0x000000a0, 0x00000000, +0x0006b01d, 0x0004021d, 0x12400090, 0x04000d00, 0x40001000, 0x00050000, +0x0130000c, 0x030000c0, 0xb0000c00, 0x0003c000, 0x017c003f, 0x77c20130, +0x00000a80, 0x00000000, 0x00a79911, 0x0a7c269f, 0xa7c029f0, 0x7c029f00, +0xd029f40a, 0x029f10a7, 0x29f00a78, 0x9f10a5d4, 0xf00a7e02, 0x00a7c029, +0x0a7c029f, 0x67c029f8, 0x00000e20, 0x00000000, 0x016fa018, 0x1e4c0db7, +0x64c05930, 0x7c079301, 0xc059301e, 0x05930167, 0x79f0164c, 0x9f8167c0, +0xf0164c05, 0x0167c059, 0x02cc0593, 0x60d06b78, 0x00000e00, 0x00000000, +0x1287081c, 0x28400e1d, 0x8540a100, 0x5c0a1502, 0x40a15028, 0x0a152287, +0xa1d02844, 0x1d028448, 0xd0005e0a, 0x00074001, 0x08440031, 0x704041d8, +0x00000c20, 0x00000000, 0x0023a012, 0x12a5048d, 0x28410a14, 0xb404a144, +0x410a1012, 0x10a5042b, 0x4a504295, 0xad042a40, 0x904a9410, 0x04ab412a, +0x0a0502a1, 0x40406850, 0x00000e80, 0x00000000, 0x0025a818, 0x0a44009d, +0x2d440b18, 0xf400b500, 0x404b5002, 0x00b5012e, 0x0bd202d4, 0xbd012c40, +0xd002d400, 0x002f400b, 0x824000b1, 0x604009d8, 0x00000620, 0x00000000, +0x00278805, 0x026c809f, 0x24c40914, 0x7c009300, 0x40093002, 0x00970027, +0x0970025c, 0x9d0026c0, 0xb0025c00, 0x0027c009, 0x024c0093, 0x14c00970, +0x00000e20, 0x00000000, 0x02658016, 0x167c808f, 0x23c058f2, 0x58009f00, +0xc009f002, 0x009f0027, 0x09f0026c, 0x9f0026c1, 0xf0027c00, 0x6027c009, +0x427c008f, 0x53c008f0, 0x00000600, 0x00000000, 0x00050814, 0x088c0013, +0x0cc00338, 0x8c083340, 0xc0033000, 0x00230008, 0x023010fc, 0x23200fc8, +0x2380ec00, 0x0008c203, 0x007c003f, 0x50c601f0, 0x00000420, 0x00000000, +0x01148014, 0x11440071, 0x16c00530, 0x44015100, 0x40051001, 0x00510014, +0x05100174, 0x51001740, 0x10015400, 0x00144005, 0x11e4005d, 0x504027d0, +0x00000200, 0x00000000, 0xc0622014, 0x4f0400c1, 0x30500c90, 0x0500c120, +0x500c144b, 0x80c954b0, 0x0c1e0334, 0xc1403340, 0x174b0580, 0x04b0512c, +0x073412c9, 0x504138d0, 0x00000a00, 0x00000000, 0x08620005, 0x00848021, +0x0a400010, 0x86012100, 0x40121240, 0x02291008, 0x02180434, 0x21104b40, +0x11048400, 0x00084012, 0x03a4002d, 0x144022d0, 0x00000200, 0x00000000, +0x006a1015, 0x07cd01c3, 0x40c012b6, 0x0c014320, 0xc2143007, 0x01eb0060, +0x1e3007bc, 0x430053c0, 0x30860e81, 0x0078ca18, 0x87bc01ef, 0x54d01bf1, +0x00000040, 0x00000000, 0x0025b810, 0x007400dd, 0x37400df1, 0x7c609f08, +0xc009f080, 0x20171015, 0x01f0807c, 0x9f0827c0, 0xf0015c00, 0x0007c805, +0x807c001f, 0x43c001f0, 0x00000660, 0x00000000, 0x006fa000, 0x04fc01f3, +0x7cc09f30, 0xfe09bf00, 0xc01f3004, 0x49bf005c, 0x17f005fc, 0xff007fc0, +0xf005f801, 0x025f0417, 0x07fc017f, 0x00c017f0, 0x00000e00, 0x00000000, +0x06298815, 0x03fc0020, 0x0905c210, 0xb40c6d02, 0xc0825213, 0x486d222b, +0x8ad062b0, 0x2d220b40, 0xd022b408, 0x0a2a448a, 0x23b480af, 0x56d006d2, +0x00000620, 0x00000000, 0x00290000, 0x00b400e9, 0x08000010, 0xa6402d00, +0x40065000, 0x00ad0008, 0x06d00190, 0x6d001b40, 0xd000b400, 0x181b4002, +0x03b4016d, 0x004004d0, 0x00000400, 0x00000000, 0x50e32804, 0x03540cc9, +0xb1443c10, 0x3403cd82, 0x4038510f, 0x014d02f3, 0x28d00e24, 0x8d02e340, +0xc0af340b, 0x026208bc, 0x00308a85, 0x124204d0, 0x00000c20, 0x00000000, +0x02e7a815, 0x177422db, 0x34401d34, 0x6413df00, 0xc139704f, 0x115d00b4, +0xf9f0067c, 0x9f00a7c0, 0xd00b7402, 0x00a7002d, 0x0330009d, 0x54c00dd0, +0x00000620, 0x00000000, 0x00218001, 0x407c0217, 0x03c100f0, 0x7c041f14, +0xc045e010, 0x129f0105, 0x04f0413c, 0x5f0117c0, 0xf0107c04, 0x0116c041, +0x037c104f, 0x05c005f0, 0x00000c00, 0x00000000, 0x00e50880, 0x03ec0133, +0x0cc00330, 0x0c006300, 0xc0003003, 0x00630028, 0x0b300ecc, 0x2380c8c0, +0x30028c03, 0x0028c00a, 0x03cc00b3, 0x03c00f30, 0x00000c22, 0x00000000, +0x00260081, 0x00440b14, 0x36c20d40, 0x44009100, 0x420d0000, 0x12990014, +0x05100564, 0xd1007442, 0x10814421, 0x08144205, 0x60448051, 0x074015f2, +0x00000802, 0x00000000, 0x0024a001, 0x006482c0, 0x34400d10, 0x44209100, +0x40891000, 0x001d0014, 0x81100064, 0x91002440, 0x10016400, 0x02044005, +0x00640811, 0x07404510, 0x00000200, 0x00000000, 0x80202010, 0x03146001, +0x02500014, 0x04604140, 0x40041003, 0x00c94020, 0x0c140324, 0x41401040, +0x14020500, 0x40305008, 0x000500c1, 0x43400490, 0x00000080, 0x00000000, +0x4026b000, 0x006c8013, 0x04c00134, 0x4d001300, 0xd0013400, 0x00170004, +0x0130004d, 0x110004d0, 0x38806c00, 0x0004e201, 0x004e8013, 0x03c40530, +0x00000ac0, 0x00000000, 0x003fb805, 0x03ee003e, 0x3fc00ff0, 0xfc00ff00, +0xc00fc003, 0x00f7003f, 0x0ff003dc, 0xfc003fc0, 0xf803fe00, 0x003fe00f, +0x03fe00ff, 0x17c007f8, 0x00000e60, 0x00000000, 0x050da003, 0x60bc20ff, +0x36c103b0, 0xfc881307, 0xc801f273, 0x18d30337, 0x0f700bcc, 0xf3023fc0, +0x3003c400, 0x003dc00f, 0x02cc00f3, 0x0c400bf0, 0x00000e00, 0x00000000, +0x00870801, 0x1b7400c7, 0xbf402110, 0x74041108, 0x4005d11b, 0x02f52307, +0x4f100bc4, 0x11003f40, 0xf0034000, 0x02bc420f, 0x83450055, 0x054009d0, +0x00000c20, 0x00000000, 0x00032011, 0x003414cd, 0x31614012, 0x34040500, +0x40005013, 0x00c10103, 0x0c949b14, 0x11413349, 0x91030500, 0x0030400c, +0x03260041, 0x465608d0, 0x00000e80, 0x00000000, 0x8085a803, 0x537400d5, +0x33606190, 0x760a9100, 0x4805d807, 0x00c11037, 0x0d908347, 0x91003744, +0xd1034674, 0x0034600d, 0x136400d5, 0x0f4009d9, 0x00000620, 0x00000000, +0x00e78802, 0x007c40df, 0x36c23db4, 0x7c031510, 0x81b97103, 0x00d14037, +0x0df20346, 0x030037c0, 0xb0034801, 0x0030d40c, 0x076c1a93, 0x02c009f0, +0x00000e20, 0x00000000, 0x013d8007, 0x06bc00ff, 0x36401f72, 0x6c013f40, +0xc80ff083, 0x00ff000f, 0x0f7003bc, 0x7f0037c0, 0xb003ae81, 0x003ec20f, +0x03dc003f, 0x1dc008f0, 0x00000602, 0x00000000, 0x00250802, 0x104c00d7, +0x35c12db0, 0x5d005300, 0xc089b103, 0x00da0007, 0x0db0037c, 0xd74032c0, +0x34037c02, 0x0234c00d, 0x034c008f, 0x08d019f0, 0x00000420, 0x00000000, +0x0034a013, 0x0a440add, 0x3dc03d10, 0x4400d100, 0x401d1903, 0x00fd0037, +0x0f1203f4, 0xdb003c42, 0x1003f400, 0x007c19af, 0x3f4481dd, 0x4c400bd0, +0x00000200, 0x00000000, 0x8012a007, 0x000409c1, 0x33608098, 0x2600c100, +0x42701603, 0x80cd1035, 0x0cd08324, 0x05003240, 0x18033625, 0x0832180c, +0x0f2401cd, 0x0c4008d0, 0x00000a00, 0x00000000, 0x80488004, 0x05a501ed, +0x79405a10, 0xa401e100, 0x401a5807, 0x41ed034b, 0xdc002734, 0xed527840, +0x1137b415, 0x007a009c, 0x07a401cd, 0x08409ad0, 0x00000200, 0x00000000, +0x01301012, 0x000c00c3, 0x71c614b0, 0x2c251340, 0xc0501007, 0x45cf0843, +0x5ce1173c, 0x470372c0, 0x32177c0d, 0x0032d00c, 0x232c104f, 0x58d008f0, +0x00000040, 0x00000000, 0x002db802, 0x01dc80fe, 0x37e00db2, 0x5c08bf02, +0xc0893023, 0x08df0037, 0x0fd023fc, 0xdb0a37c0, 0xf0037c00, 0x00bdc02f, +0x03dc02fd, 0x0bc00bf0, 0x00000660, 0x00000000, 0x8017a015, 0x037c20df, +0xb7c001f0, 0x4c01cb01, 0xe0057003, 0x40d22034, 0x2d00234c, 0x9305b4c3, +0x74037800, 0x0334c04d, 0x4b4c00db, 0x54c099f0, 0x00000e00, 0x00000000, +0x00198812, 0x039c80ed, 0x39c00a70, 0xce80ed04, 0x040e100b, 0x0ce00808, +0x2e102384, 0xe1003fc0, 0x500bcc00, 0x0132c04c, 0x038500f1, 0x59400ad0, +0x00000620, 0x00000000, 0x00790003, 0x479441ed, 0x796016d8, 0x84017502, +0x60165007, 0x09c91040, 0x5c101724, 0xe10a7844, 0x1017b041, 0x007a409e, +0x0f8541a9, 0x0c4048d0, 0x00000402, 0x00000000, 0x00b32812, 0x075400dd, +0x3148fc5a, 0x4408cd00, 0x603c1003, 0x40c9c034, 0x0c100324, 0xc1203740, +0x51030413, 0x0032420c, 0x030400d9, 0x494008d1, 0x00000c20, 0x00000000, +0x009da817, 0x09dc005f, 0x15c007d0, 0xcc017e08, 0xc0467000, 0x005b001c, +0x053001ec, 0x62001462, 0x10017c07, 0x0016c005, 0x054c0c5b, 0x5ce204f0, +0x00000620, 0x00000000, 0x10070012, 0x405c001f, 0x05c00170, 0x5c021f00, +0xc001f800, 0x00170007, 0x01f0801c, 0x1f0006e8, 0xf0006c00, 0x0007c001, +0x087d0217, 0x4bc001f0, 0x00000c00, 0x00000000, 0x14230810, 0x024c009f, +0x25c04970, 0x6c219f00, 0x0109b004, 0x009300a5, 0x08700244, 0x9b1024c0, +0x30026c08, 0x0220c008, 0x074800d3, 0x40c009f0, 0x00000c20, 0x00000000, +0x00262201, 0x0a450091, 0x24482910, 0x74039d00, 0xc0495026, 0x80915066, +0x09100244, 0x91482540, 0x14025403, 0x20645009, 0x1a441a9b, 0x044019d0, +0x00000800, 0x00000000, 0x0024a018, 0x0a448191, 0x24402810, 0x54049d10, +0x40091002, 0x00810424, 0x09500254, 0x91002440, 0x10020402, 0x40244089, +0x12550091, 0x604419d2, 0x00000200, 0x00000000, 0x40202010, 0x22040881, +0x20508814, 0x34088982, 0x40095022, 0x28810222, 0x88122215, 0x81022140, +0x100a1400, 0x2320600c, 0x02140889, 0x404088d0, 0x00000080, 0x00000000, +0x2506b01d, 0x58442217, 0x85c16170, 0x5c025f05, 0xc0013058, 0x16130584, +0x2142585c, 0x1b0584e0, 0x32004c80, 0x00c4d001, 0x005d1613, 0x74c021f0, +0x00000ac0, 0x00000000, 0x002fb819, 0x92fc049f, 0x27c04bf8, 0xfc04bf11, +0xc20af110, 0x049f012e, 0x69e0126c, 0xbf012780, 0xfc0a7c00, 0x4327c029, +0x026c0496, 0x67c06bf0, 0x00000e60, 0x00000000, 0x80379018, 0x02fc02bf, +0x24418b30, 0xcc00bf00, 0xc80b10c7, 0x0293002d, 0x8990326c, 0xbb0124c1, +0x30e27400, 0x40a0c04b, 0x028d06b3, 0x64c143f0, 0x00000e00, 0x00000000, +0x1007001c, 0x0874001d, 0x04d02114, 0x04000d11, 0x08015008, 0x0a114000, +0x01140804, 0x55008540, 0xb4006440, 0x03854021, 0x00440653, 0x704031d0, +0x00000c22, 0x00000000, 0x00a3a010, 0x0a34008d, 0xa24048d0, 0x46009d24, +0x40090202, 0x80918021, 0x48101a64, 0x89872040, 0x91123620, 0x10204888, +0x02240289, 0x405008d0, 0x00000e80, 0x00000000, 0x20a5a818, 0x0274009d, +0x244049d2, 0x44009d00, 0x46091802, 0x80910020, 0x08100264, 0x95002564, +0x94826400, 0x20254009, 0x12640491, 0x604209d0, 0x00000620, 0x00000000, +0x0027a805, 0x027c809d, 0x26c009f4, 0x45188d00, 0xe0081206, 0x00919125, +0x0920026e, 0x9b2024e0, 0xb2027c03, 0x0024d009, 0x026c029b, 0x14c009f0, +0x00000e20, 0x00000000, 0x00258014, 0x027c009f, 0x27c00930, 0x5c019f00, +0xc009f512, 0x009f0127, 0x0970025e, 0x9f5023c0, 0xf0022c44, 0x0033c109, +0x425c009f, 0x53c000f0, 0x00000600, 0x00000000, 0x00050814, 0x005c041f, +0x04c04170, 0x6c821220, 0xc001b000, 0x001b1007, 0x0132005c, 0x1f000480, +0x32004c86, 0x0004d000, 0x004c0213, 0x50c00130, 0x00000420, 0x00000000, +0x0014a014, 0x05f4117d, 0x144067d0, 0x8c807110, 0x40171209, 0x0051011f, +0x05100104, 0x75001546, 0xf0014540, 0x04555007, 0x09c48871, 0x52c00110, +0x00000200, 0x00000000, 0x0032a014, 0x077401cd, 0x30502d10, 0x2409c180, +0x420cd81b, 0x00d10133, 0x0c510306, 0xc5103260, 0x10032402, 0x806242bc, +0x0b040a91, 0x50400c10, 0x00000a00, 0x00000000, 0x00388005, 0x41b4006d, +0x7a400cd8, 0xa400e101, 0x402e1103, 0x04e1003b, 0x8e508384, 0xf1013340, +0x9013a411, 0x002b400c, 0x838400a1, 0x1640ac10, 0x00000200, 0x00000000, +0x00780015, 0x079c21af, 0x7ce41672, 0xac812340, 0xc416f106, 0x09f1404b, +0x5c72078c, 0xe723fac1, 0x3017e401, 0x407ac81e, 0x038d01f3, 0x54c05e30, +0x00000040, 0x00000000, 0x0035a810, 0x817c405f, 0xb56209f8, 0x5c801f07, +0xc0057202, 0x0ed50007, 0x6d900b78, 0xc50035c0, 0xf45b5c00, 0x24b5c00d, +0x037d009f, 0x43c00df0, 0x00000660, 0x00000000, 0x007fa000, 0x26fa81f3, +0x7cc09b32, 0xdc019202, 0xc09e3015, 0xc9f3004d, 0x1d306f4c, 0xf3007dc8, +0xb067cc01, 0x007cd01f, 0x078c01ff, 0x0bc81f30, 0x00000e20, 0x00000000, +0x00398815, 0x209c022b, 0x7ac054b0, 0x0441bb02, 0x40da116d, 0x41f00048, +0x1d502744, 0x81027040, 0x50172c01, 0x003ac006, 0x2bac04fd, 0x57408eb0, +0x00000620, 0x00000000, 0x00310000, 0x423408a1, 0x3a400010, 0x94006c20, +0x49c61000, 0x00e1043b, 0x0e9003a5, 0x61813948, 0x10138430, 0x0038400a, +0x43c4006d, 0x23400e10, 0x00000400, 0x00000000, 0xa0732804, 0x00144009, +0x32402091, 0x04024000, 0x40001100, 0x00c10072, 0x0cc20324, 0x01403060, +0xd2032600, 0x20325200, 0x0724100d, 0x1b400c90, 0x00000c00, 0x00000000, +0x0435a815, 0x0a7c00d1, 0x3cc02d30, 0x54885c40, 0x40193104, 0x40f34077, +0x0fbd03ec, 0xd1103d40, 0xb003cc00, 0x0024c00c, 0x870401df, 0x57800d30, +0x00000600, 0x00000000, 0x00370001, 0x1a5c00df, 0x33c24df0, 0x7cc05f10, +0xc041f808, 0x00cf2031, 0x0d70035c, 0x5f0033c2, 0x72037c86, 0x0037c029, +0x037c01df, 0x27c00df0, 0x00000c00, 0x00000000, 0x003f0880, 0x20fc00b7, +0x3fc007f0, 0xcd00b300, 0x821bf801, 0x00f70406, 0x0ef003cc, 0xb7003ec0, +0x2003fc03, 0x003cc055, 0x07cd00f3, 0x04c03e30, 0x00000c00, 0x00000000, +0x08362081, 0x0c7400dd, 0x374009d0, 0x44469500, 0x4031d889, 0x80d14007, +0x0dd20354, 0x1b403448, 0x50037006, 0x00354021, 0x0e540185, 0x84501d50, +0x00000802, 0x00000000, 0x0034a001, 0x4354005d, 0x37420990, 0x44200980, +0x414dd012, 0x00d10004, 0x0dd00346, 0xd5003648, 0x50032400, 0x0230402d, +0x1345c1d1, 0x04400d10, 0x00000220, 0x00000000, 0x00302010, 0x0334000d, +0x3340c4d0, 0x04040143, 0x4880d012, 0x2cc56803, 0x0cd81b16, 0x0908b002, +0x59133010, 0x00314000, 0x011400c5, 0x40400c50, 0x00000080, 0x00000000, +0x0036b000, 0x015c0017, 0x7bc051f0, 0x0c07db00, 0xc11dd02f, 0x01f302f6, +0xdff217cc, 0x1781fac9, 0x703fbc2f, 0x0034c001, 0x034c0053, 0x04c00d30, +0x00000ac0, 0x00000000, 0x003fb805, 0x01fc403f, 0x77c0d1f0, 0x7c1dff8b, +0xc051f19f, 0x0ddb08f7, 0x1ff01ffc, 0xcf09f7c0, 0xf0277c05, 0x003fc00f, +0x02fc803f, 0x17c00ff0, 0x00000e60, 0x00000000, 0x002f8003, 0x18ec00ff, +0x0fc14330, 0xfc08ff00, 0xc00fb003, 0x00f3203c, 0x0f3003ec, 0xf3003fc0, +0x7003cc00, 0x183cc00f, 0x03dc00fb, 0x0cc00f30, 0x00000e00, 0x00000000, +0x08270801, 0x314450dd, 0x34402110, 0x74041d00, 0x40011100, 0x00114004, +0x01140045, 0x11400744, 0x11804500, 0x00045001, 0x00450011, 0x04400f10, +0x00000c20, 0x00000000, 0x0023a011, 0x002004cd, 0x02400050, 0x34044d00, +0x400cd000, 0x00890021, 0x08500214, 0xc1002340, 0x14021400, 0x40304a08, +0x012400c5, 0x45640c14, 0x00000e80, 0x00000000, 0x2425a803, 0x084400dd, +0x14400154, 0x74009d00, 0x60015303, 0x22490010, 0x05504114, 0x01341340, +0x52011420, 0x00004804, 0x8224a005, 0x0d400c12, 0x00000620, 0x00000000, +0x0067a802, 0x006c00df, 0x46c8dd70, 0x7c01df11, 0xd015f003, 0x22db0cb5, +0x05700b7c, 0x5310b7c8, 0x30015c01, 0x0004c005, 0x276c129f, 0x09500d30, +0x00000e20, 0x00000000, 0x016d8007, 0x237c20cf, 0x7ec00bb0, 0x7c081f12, +0xc489b000, 0x00172007, 0x09b00064, 0x9f1007c9, 0xb0426c08, 0x0437c509, +0x044c005b, 0x1ec10ff0, 0x00000600, 0x00000000, 0x00650802, 0x000c40df, +0x07c00530, 0x4c005304, 0xc0453000, 0x129304a4, 0x0130127c, 0x530124c2, +0x30004c00, 0x0804c481, 0x014c0293, 0x0ac08d30, 0x00000420, 0x00000000, +0x10a4a013, 0x464404fd, 0x17400110, 0x6c119140, 0xc109b003, 0x025b0456, +0x3db0156c, 0x9b0496c1, 0xb0136c09, 0x04f6c02d, 0x066c045b, 0x4c422fb0, +0x00000200, 0x00000000, 0x0102a007, 0x020407cd, 0x02400191, 0x64008102, +0x04391003, 0x0a490092, 0x0c900904, 0x8900d040, 0x10032401, 0x0072400c, +0x4a341141, 0x1c400c10, 0x00000a00, 0x00000000, 0x00488004, 0x058501ed, +0x6b621698, 0xa6016180, 0x60169824, 0x21a9806a, 0x1298a6a6, 0x69826a60, +0x9804a609, 0x024a6092, 0xc5a409a9, 0x10401c90, 0x00000200, 0x00000000, +0x00001012, 0x230420cd, 0x27400894, 0x24000102, 0x40081020, 0x04091002, +0x08900014, 0x89000040, 0x30022c10, 0x0032c008, 0x103c0043, 0x48d08c30, +0x00000040, 0x00000000, 0x000db802, 0x01fd00ff, 0x2fc00f70, 0xfc00ff02, +0xc087f023, 0x00ff023f, 0x07f003fc, 0x7f0037c0, 0xf001fc00, 0x000fc007, +0x07fc00bf, 0x09c08ff0, 0x00000660, 0x00000000, 0x0007a015, 0x824d00df, +0x16c01df1, 0x6c019f20, 0xc0013007, 0x015b0014, 0x1530054c, 0x1b0856c0, +0x30054c41, 0x0044c015, 0x064c0113, 0x57c04d30, 0x00000e00, 0x00000000, +0x20098812, 0x038402ed, 0x38500fd0, 0x14004d00, 0x400c5000, 0x008d0029, +0x08500214, 0xc5002140, 0x50021400, 0x00314008, 0x011400c5, 0x4b092e50, +0x00000620, 0x00000000, 0x00690003, 0x078405ed, 0x79401ed0, 0x84012d80, +0x60125004, 0x012d0048, 0x12500484, 0x25004840, 0x10049401, 0x00494012, +0x04840125, 0x0f025e10, 0x00000400, 0x00000000, 0x00332812, 0x0b4400dd, +0x31402cd0, 0x1400cd00, 0x640c5003, 0x00cd0031, 0x0c500314, 0xc5083140, +0x50031400, 0x1071400c, 0x031601c5, 0x4b400c50, 0x00000c20, 0x00000000, +0x009da817, 0x81cc005f, 0x1fc137f0, 0x6c005f01, 0xc8857001, 0x005d0014, +0x8570014c, 0x5f0816c0, 0x30215c08, 0x0615c085, 0x01cc1057, 0x5fc60530, +0x00000620, 0x00000000, 0x20c70012, 0x407c021f, 0x064101f0, 0xdc003f00, +0xc003d000, 0x003f000b, 0x03e000fc, 0x3f008fc0, 0xf008fc80, 0x000fc003, +0x00fc023f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x00270810, 0x064c009f, +0x27c00970, 0x7c019f00, 0xc049f102, 0x00932024, 0x19f0027c, 0x9f0067c0, +0xf0267c00, 0x00a7c029, 0x1674089f, 0x43c409f2, 0x00000c20, 0x00000000, +0x04a62001, 0x0644079d, 0x27400910, 0x74089d00, 0x5129d002, 0x1b9b06e4, +0x19d06e74, 0x9d00a641, 0xd0027402, 0x00274009, 0x0a74249d, 0x0740a9d0, +0x00000800, 0x00000000, 0x0024a018, 0x9244889d, 0x27400950, 0xf6009d00, +0x400ad002, 0x00b1002c, 0x4bd002f4, 0xb901af40, 0xd042f418, 0x002f400b, +0x42e400bd, 0x634029d0, 0x00000200, 0x00000000, 0x00202010, 0x2204488d, +0x33408812, 0xb488ad02, 0x402ad0a2, 0x42a900a8, 0x2ad00ab4, 0xad30aa40, +0xd00ab402, 0x00bb402e, 0x0ab402ad, 0x434028d0, 0x00000080, 0x00000000, +0x0006b01d, 0x584d020f, 0x87c56150, 0x7c021f05, 0xc001f008, 0x00130004, +0x01f0007c, 0x1f0007c0, 0xf0007c00, 0x0007c001, 0x00fc001f, 0x77c001f0, +0x00000ac0, 0x00000000, 0x002fb819, 0x12fc069f, 0x2f404bd0, 0x7c869d01, +0x4429d01a, 0x029d00a7, 0x29d00a74, 0x9d00a740, 0xd00a7402, 0x00a74029, +0x0a7c829f, 0x67c029f0, 0x00000e60, 0x00000000, 0x003fa018, 0x52cc04ba, +0x2c0589f2, 0xfc049302, 0xc069f012, 0x849f1024, 0x29f0125c, 0x9f01a4c0, +0xf01a7c06, 0x0427c029, 0x027c549f, 0x60c049f0, 0x00000e00, 0x00000000, +0x0007081c, 0x0844061d, 0x844001d0, 0x74001102, 0x4061d118, 0x161d2584, +0x61d05074, 0x1d018440, 0xd0187406, 0x00074061, 0x5874161d, 0x704143d0, +0x00000c20, 0x00000000, 0x0063a010, 0xc2041289, 0x204048d1, 0x36568101, +0x402a5062, 0x08ad00a9, 0x6ad00ab4, 0xad032850, 0xd00ab408, 0x042b402a, +0x0ab400ad, 0x40402ad0, 0x00000e80, 0x00000000, 0x0025a818, 0x0245009d, +0x240609d0, 0x74009100, 0x4009d002, 0xa0bd002d, 0x0ed002f4, 0xbd002c40, +0xd002f480, 0x002f400b, 0x03f400fd, 0x60400bd0, 0x00000620, 0x00000000, +0x02e7a805, 0x024c009b, 0x249049f0, 0x7c109341, 0xd009706a, 0x289f2025, +0x19f0225c, 0x9f1224c0, 0xf0067ca0, 0x0027c019, 0x4274009f, 0x14d009f0, +0x00000e20, 0x00000000, 0x00658014, 0x127c008f, 0x23c208f0, 0x7c00df00, +0xc009f002, 0x009f10e6, 0x49f0027c, 0x9f0427c2, 0xf0127c00, 0x0027c049, +0x027c809f, 0x53c009f0, 0x00000600, 0x00000000, 0x03050814, 0x104c201f, +0x06c001f0, 0x7c201f00, 0xc0037008, 0x003f000f, 0x83f000fc, 0x3f020fc2, +0xf000fc00, 0x010fc003, 0x00fc183f, 0x505003f0, 0x00000420, 0x00000000, +0x005ca014, 0x09c5007d, 0x5c4005d0, 0x74015d04, 0x00151001, 0x035d0117, +0x05d00574, 0x5d001740, 0xd0097403, 0x001740a5, 0x1d74035d, 0x504007d0, +0x00000200, 0x00000000, 0x0012a014, 0x030510cd, 0x33400cd0, 0x34004d00, +0x401f5002, 0x0ded04bb, 0x2ed013b4, 0xed007b41, 0xd063b413, 0x02bb408e, +0x1bb401ed, 0x50409fd0, 0x00000a00, 0x00000000, 0x00088005, 0x038400cd, +0x39404ed1, 0xb4426d00, 0x442e1206, 0x00ed003b, 0x0ed043b4, 0xed203b40, +0xd003b430, 0x003b400e, 0x03b400ed, 0x14400cd0, 0x00000200, 0x00000000, +0x00581015, 0x078c012f, 0x5bc01ff1, 0xfc016f00, 0xc01e601e, 0x01ef007b, +0x1ef007bc, 0xef007bc0, 0xf007bc01, 0x207bc01e, 0x07bc81ef, 0x54c01ef0, +0x00000040, 0x00000000, 0x0005b810, 0x027c00df, 0x36c08df0, 0x7c121f00, +0xc001f002, 0x001f0007, 0x01f0007c, 0x5f0007c0, 0xf0007c00, 0x0007c801, +0x007c001f, 0x43c601f0, 0x00000660, 0x00000000, 0x004ba000, 0x07ec01f3, +0x7cc21f30, 0x8c097340, 0xc01f3206, 0x01ff207f, 0x1ff007fc, 0xbf007fc0, +0xf227fc01, 0x027fc09f, 0x07fc01ff, 0x03401ff0, 0x00000e00, 0x00000000, +0x22098815, 0x83844061, 0x28420eb0, 0x84006100, 0x400e1402, 0x00ed023b, +0x8ed003b4, 0xad003b41, 0xd003b408, 0x083b402e, 0x23a400ed, 0x57400ed0, +0x00000620, 0x00000000, 0x040d0000, 0x03a40021, 0x18400c10, 0x80406100, +0x400a1002, 0x00ed003b, 0x0ed003b4, 0xad003b40, 0xd103b400, 0x103b480e, +0x43b400ed, 0x03401ed0, 0x00000400, 0x00000000, 0x00232804, 0x32042041, +0x60421d90, 0x44070110, 0x40f51066, 0x111d03c7, 0x01d04474, 0x1d01c641, +0xd0087411, 0x01874071, 0x18641b1d, 0x134000d0, 0x00000c20, 0x00000000, +0x0005a815, 0x036800d3, 0xe4c05f30, 0xcd005200, 0xc01d300e, 0x40df2077, +0x0df10b7c, 0xdf0037c4, 0xf1037c02, 0x0137c00d, 0x0b7801df, 0x57c00df0, +0x00000620, 0x00000000, 0x00070001, 0x037c009f, 0x13c00df0, 0x7c085f02, +0xc005f002, 0x0adf00b7, 0x2df02b7c, 0xdf0237c0, 0xf0bb7c0a, 0x00b7c08d, +0x0b6cc0df, 0x07c00df0, 0x00000c00, 0x00000000, 0x00cf0880, 0x03dd0903, +0x0dc00ff0, 0xfc007304, 0xc00f7002, 0x05f3013c, 0x4f3027cc, 0xf3003cc0, +0x3007cc00, 0x003cc00f, 0x23cc03f3, 0x03c00f30, 0x00000c22, 0x00000000, +0x00422081, 0x02440191, 0x55400dd0, 0x74081102, 0x40691002, 0x031100c4, +0x21101c44, 0x5100c440, 0x10044446, 0x02044011, 0x88442211, 0x07409111, +0x00000802, 0x00000000, 0x0004a001, 0x895400d1, 0x25420dd0, 0x74001100, +0x400d5002, 0x02110c04, 0x01100044, 0x11028440, 0x12504400, 0x04044881, +0x00440011, 0x07400910, 0x00000200, 0x00000000, 0x20002010, 0x01040001, +0x40500cd0, 0x34000100, 0x42041002, 0x00010000, 0x00100004, 0x01000048, +0x10000400, 0x00004000, 0x00040001, 0x43400810, 0x00000080, 0x00000000, +0x0006b000, 0x011c0013, 0x01c00df0, 0x7c000340, 0xc0097002, 0x00130004, +0x0131004c, 0x130004c0, 0x32004c00, 0x0004c401, 0x004c0013, 0x03c00930, +0x00000ac0, 0x00000000, 0x002fb805, 0x00fc003f, 0x0fc00ff0, 0xfc003f00, +0xc00ff002, 0x00ff003f, 0x0ff003fc, 0xbf203fc0, 0xf003fc00, 0x003fc00f, +0x03fc00ff, 0x17c007f0, 0x00000e60, 0x00000000, 0x000fa003, 0x10fcc433, +0x35c001b0, 0x7c5cff07, 0xc04d7272, 0x00d32337, 0xc33070cc, 0xdb40b4c1, +0xf0137c08, 0x003dc003, 0x01c8003f, 0x0cc00bf0, 0x00000e00, 0x00000000, +0x43170801, 0x5b741201, 0x3c448510, 0xf406fd10, 0x41ec041a, 0x06f103b7, +0x21500a44, 0xd5103d40, 0xd0237462, 0x00334809, 0x0144005d, 0x045009d0, +0x00000c20, 0x00000000, 0x0423a011, 0x20140c81, 0x30400810, 0x5444cc20, +0x400c0082, 0x12c10137, 0x4c100004, 0xc1113144, 0xd0833406, 0x08334000, +0x0324000d, 0x444209d0, 0x00000e80, 0x00000000, 0x0035a803, 0x0b341891, +0x34480512, 0x7400dd18, 0x400d1402, 0x00d10077, 0x8d500244, 0xd5003548, +0xd0037400, 0x00374009, 0x2b64039d, 0x0c4009d0, 0x00000620, 0x00000000, +0x0287a802, 0x047c0353, 0x34d011b4, 0x3c80df00, 0xc09d3002, 0x00d30037, +0x1930074d, 0xd35034c0, 0xf0037c20, 0x0035c015, 0x0c6d035f, 0x08c008f0, +0x00000e20, 0x00000000, 0x0c458007, 0x17fc117f, 0x3ec08af2, 0x7c00ff00, +0xc00fb102, 0x00cf083f, 0x0bf027bc, 0xff003bc0, 0xf003fc50, 0x003fe05f, +0x44dc006f, 0x1fc00bf0, 0x00000600, 0x00000000, 0x40850802, 0x405d0313, +0x35f00970, 0x5d00df18, 0xc00d7c02, 0x00d30035, 0x0cf0034c, 0xd34037c0, +0x30037c00, 0x0234c005, 0x025d0213, 0x08c009f0, 0x00000420, 0x00000000, +0x0044a013, 0x1b440011, 0xbc400950, 0xe480fd02, 0x400db802, 0x0af10030, +0x0dd00344, 0xfb103f4a, 0x5003f400, 0x02fd400d, 0x4644651b, 0x4ec00bd0, +0x00000200, 0x00000000, 0x0006a007, 0x0c040201, 0x30400010, 0x24c0cd00, +0x400c9082, 0x80c10031, 0x04d10104, 0xc1003348, 0x50033480, 0x80704000, +0x05042081, 0x1c4008d1, 0x00000a00, 0x00000000, 0x00588004, 0x24040921, +0x7a60d650, 0xa409ed00, 0x405e1036, 0x15e10278, 0x16d007a6, 0xe90a7340, +0x5087b429, 0x0079401a, 0x05810069, 0x12401ad0, 0x00000200, 0x00000000, +0x06a01012, 0x021c08c1, 0x71e09970, 0x1c45df03, 0xc01cd006, 0x01c30371, +0x5cd0150c, 0xc34373c0, 0x70077405, 0x0030e041, 0x021e08d3, 0x48c009f0, +0x00000040, 0x00000000, 0x103db802, 0x02fc28ff, 0x3dcc05f0, 0x5c00ff00, +0xc50ff8a2, 0x8afe0c3b, 0xcdf02358, 0xdf0237c0, 0xf0037c28, 0x003fc00f, +0x02fd60ff, 0x0bc00bf0, 0x00000660, 0x00000000, 0x0037a015, 0x014c00df, +0x34ca0530, 0x4c04df05, 0xc00d3012, 0x02d33037, 0x0d30076d, 0xd30534c2, +0x31034c41, 0x2037c005, 0x004c41df, 0x57c009f0, 0x00000e00, 0x00000000, +0x00398812, 0x01c480ed, 0xb8600f10, 0xc516ed00, 0x400eb02a, 0x06c1213b, +0x0f108314, 0xe1013848, 0x101bc424, 0x103b410e, 0x0084006d, 0x4b402ad0, +0x00000620, 0x00000000, 0x00f90003, 0x078601c5, 0x78411e10, 0x8405cd01, +0x405e1096, 0x81e1187b, 0x1e904784, 0xe1027840, 0x9007840d, 0x027b4012, +0x0c8400cd, 0x0f401ad0, 0x00000400, 0x00000000, 0x00f32812, 0x230402dd, +0x34400d15, 0x0400cd20, 0x400c9002, 0x00c10033, 0x0c104304, 0xc1003040, +0x91030400, 0x2033420c, 0x1004014d, 0x4b4008d0, 0x00000c20, 0x00000000, +0x00dda817, 0x05cd057f, 0x14d42732, 0xcc005f00, 0xc0013001, 0x4053400f, +0x87b301cc, 0x534014c2, 0xb0014c00, 0x1017c805, 0x05cc027f, 0x5fc005f0, +0x00000620, 0x00000000, 0x01070012, 0x807c001f, 0x07c101f0, 0x3c201f00, +0xc001f000, 0x001f0007, 0x01f0005c, 0x1f0003c0, 0x75007c02, 0x0006c081, +0x087c8c1f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x00270810, 0x0a0c109f, +0x64d00930, 0x4c009302, 0xc000b002, 0x00934000, 0x09f0024c, 0x930026ca, +0x71023c02, 0x0267c00d, 0x024c088f, 0x40c008f0, 0x00000c20, 0x00000000, +0x04262001, 0x0a44019d, 0xa5400912, 0x44409150, 0x40091002, 0x00912024, +0x09d00244, 0x91002448, 0x10027403, 0x20274009, 0x1a44419d, 0x054009d0, +0x00000800, 0x00000000, 0x0024a018, 0x42440a9d, 0xa5400910, 0x64408120, +0x40099202, 0x00812024, 0x08d00244, 0x91002640, 0x10027400, 0x20a74209, +0x2264009c, 0x604009d0, 0x00000200, 0x00000000, 0x02202010, 0x2205288d, +0x2151c814, 0x25888100, 0x50881022, 0x08810820, 0x88d02207, 0x81022040, +0x18a23408, 0x15234028, 0x0225088d, 0x414008d0, 0x00000080, 0x00000000, +0x2586b01d, 0x584c361f, 0x05d06131, 0x6c361305, 0xc361b0d8, 0x16130500, +0x61f0584c, 0x434582c1, 0x74587c16, 0x0107c941, 0x006c161f, 0x74c001f0, +0x00000ac0, 0x00000000, 0x012fb819, 0x12fc04bf, 0x27c04bf2, 0xdc049f05, +0xc843f012, 0x849f050f, 0x4bf012fc, 0x9f0127c0, 0xf3127c04, 0x0527c009, +0x02dc04bf, 0x67c16bf0, 0x00000e60, 0x00000000, 0x00af2018, 0x02fc02b3, +0xaec049f0, 0x4c849301, 0x403f3202, 0x02b3007c, 0x69f0024c, 0xb31627c9, +0xf012cc0c, 0x40a6c129, 0x024c12b3, 0x60c14930, 0x00000e00, 0x00000000, +0x0007081c, 0x1874001b, 0x044000d2, 0x6c061141, 0x50201050, 0x04110084, +0x60d2a854, 0x15008740, 0xd008540e, 0x00044061, 0x00440211, 0x70502114, +0x00000c20, 0x00000000, 0x00a32010, 0x4a340281, 0xa24968d0, 0x04988121, +0x4428000a, 0x468124a0, 0x48d04204, 0x81412340, 0xd8720400, 0x80224048, +0x026500c1, 0x40400910, 0x00000e80, 0x00000000, 0x0025a818, 0x8a740299, +0x244029d0, 0x64409100, 0x40191482, 0x00d18064, 0x09d00254, 0x9500274b, +0xd0025400, 0x00244009, 0x02640491, 0x60400910, 0x00000620, 0x00000000, +0x04a7a805, 0x027c0093, 0x26c029f0, 0x4d009300, 0xc0191002, 0x00934064, +0x39f0024c, 0x93002742, 0xd0024c00, 0x0026c009, 0x162c0593, 0x14c00830, +0x00000e20, 0x00000000, 0x00258014, 0x027c199f, 0x27c009f0, 0x5c008f00, +0xc009f002, 0x009f0027, 0x99f0027c, 0x8f4023c0, 0xf0027c80, 0x0033c099, +0x0f5c018f, 0x53c009f0, 0x00000600, 0x00000000, 0x00810814, 0x007c021f, +0x01c220f0, 0x0c001f04, 0xc0013100, 0x001f0004, 0x2130005c, 0x130004c0, +0xb2804400, 0x2407c800, 0x084c021d, 0x50c001f0, 0x00000420, 0x00000000, +0x015ca014, 0x41f4017d, 0x5d4805d0, 0x44005d00, 0xc0051001, 0x057d2017, +0x0510014c, 0x71001440, 0x10014411, 0x04174605, 0x4544005d, 0x514005d0, +0x00000200, 0x00000000, 0x0032a014, 0x1b340bcd, 0x71500cd0, 0x0400cd90, +0x40095803, 0x00dd0024, 0x0c100324, 0xc1003042, 0x10a20400, 0x00b3400c, +0x040400cd, 0x52400cd0, 0x00000a00, 0x00000000, 0x10388005, 0x02b400ad, +0x19404ed0, 0xc400ed04, 0x444a1033, 0x20ed002b, 0x5e100784, 0xc1093848, +0x10060400, 0x001b400e, 0x008480cd, 0x17400cd1, 0x00000200, 0x00000000, +0x00581015, 0x85b421ef, 0x6942bef0, 0x8c81cf20, 0xc05a7207, 0x01cf00e8, +0x7e3407ed, 0xe340fcc0, 0x30068c21, 0x926bc01e, 0x048cc1ef, 0x56c01ef0, +0x00000040, 0x00000000, 0x2035b810, 0x007c001f, 0x07c06df0, 0x3c02df00, +0xc029d483, 0x00df0526, 0x2de07b78, 0x5f05b7c0, 0x73827c20, 0x0137c08d, +0x187d00df, 0x41c08df0, 0x00000660, 0x00000000, 0x006fb000, 0x07f80977, +0x74081ff0, 0xcc1bd302, 0xc1b93007, 0x01df1065, 0xbf4027fc, 0xf3427cc4, +0xa027cc49, 0x405cc09f, 0x0c8c01f3, 0x00c21f30, 0x00000e00, 0x00000000, +0x00298015, 0x01b4086d, 0x78545fd0, 0x8401d102, 0x401b1007, 0x0dad0b68, +0x1d380734, 0xf5007840, 0xd0279449, 0x009ac00f, 0x018400e1, 0x54400e30, +0x00000620, 0x00000000, 0x00090000, 0x22a600a5, 0x0850ced0, 0xc4c0e103, +0x404a9003, 0x04ed0028, 0x0e5023b4, 0xa1023040, 0xd8038400, 0x003a400e, +0x00c40aa1, 0x00400f90, 0x00000400, 0x00000000, 0x00632804, 0x0034400d, +0x00400cd0, 0x0400c100, 0x4088d003, 0x008d1060, 0x0c128334, 0x05503040, +0xd0031400, 0x1032419d, 0x00040091, 0x10400c10, 0x00000c20, 0x00000000, +0x0035a815, 0x0b6c00d7, 0x34c02ff0, 0x8d40f340, 0xd01b8003, 0x00df3469, +0x0f500bfc, 0xd3003cd0, 0xb1034420, 0x1036c80f, 0x014d0092, 0x54c00fb4, +0x00000620, 0x00000000, 0x02070001, 0x327c408f, 0x17c18df0, 0x7c00cf00, +0xc0093003, 0x825f0027, 0x0d70437c, 0xdf0033c0, 0xf0037c20, 0x0097c00d, +0x0a7c01df, 0x07c00df0, 0x00000c00, 0x00000000, 0x005f0880, 0x01cc00f3, +0x74d40ff0, 0xcf00f300, 0xc00b3003, 0x00f3002f, 0x0e1403f4, 0xfb003cc0, +0xf007f420, 0x0076c00f, 0x04c4019f, 0x03c00f30, 0x00000c22, 0x00000000, +0x06060081, 0x00444011, 0x57d00dd1, 0x4480d500, 0x42095003, 0x02510027, +0x0d100374, 0x550036c0, 0xd0037400, 0x00bd400d, 0x101401dd, 0x07400d50, +0x00000802, 0x00000000, 0x8030a001, 0x03440051, 0x22400cd2, 0x4400d501, +0x40095003, 0x40d13027, 0x0d500334, 0xd9403640, 0xc1237400, 0x0236440d, +0x215508dd, 0x07400d10, 0x00000200, 0x00000000, 0x00002010, 0x01040041, +0xa2506cd0, 0x4408c511, 0x00481813, 0x120100a7, 0xcc101334, 0xc5033240, +0xd0033090, 0x0011400c, 0x031500cd, 0x43400c50, 0x00000080, 0x00000000, +0x0016a000, 0x024c0093, 0xc4d07ff2, 0x4401f700, 0x85fb7877, 0x87d340ef, +0x3f7097bc, 0x9b057ec1, 0xf00f784f, 0x2036c00d, 0x005d009f, 0x03c00d30, +0x00000ac0, 0x00000000, 0x200fa805, 0x00fc003f, 0xcfc29df0, 0x3c0dff00, +0xc05bf097, 0x053f026f, 0x1de0e774, 0x1f2177c8, 0xf05f7c25, 0x003fc00f, +0x00fc00bf, 0x17c00ef0, 0x00000e60, 0x00000000, 0x802fa803, 0x23fc08f3, +0x8fc143f0, 0xdc003f00, 0xc00ff053, 0x00f7003c, 0x0fb003cc, 0xf7003ec0, +0x30c5fc00, 0x046cc01b, 0x11ec00f3, 0x0fc00f30, 0x00000e00, 0x00000000, +0x00270801, 0x0a748251, 0x324825d0, 0xa4021700, 0x400fd00b, 0x22e10038, +0x0f108394, 0xc1003042, 0x10894400, 0x009040a5, 0x094400c1, 0x07480f10, +0x00000c20, 0x00000000, 0x0023a011, 0x193486c1, 0x314540d0, 0x14020d01, +0x400cd003, 0x48c50030, 0x0c900314, 0xc5103248, 0x50013400, 0x00224400, +0x212400c1, 0x47400c50, 0x00000e80, 0x00000000, 0x4065a003, 0x01760051, +0x376011d0, 0x64845d00, 0x480dd003, 0x00d12034, 0x0d110354, 0xd1203400, +0x50016420, 0x2016480c, 0x414400d1, 0x0f400d50, 0x00000620, 0x00000000, +0x0037a002, 0x077c0213, 0x05c029f2, 0x5c001f10, 0xd00dd006, 0x00d70034, +0x0db2035c, 0xd71036c8, 0x7403fc00, 0x402ed003, 0x0d6c00d3, 0x0bc00d70, +0x00000e20, 0x00000000, 0x003d8007, 0x22fc0237, 0x3ec005f0, 0xfc007700, +0xc00ff04e, 0x00ff003b, 0x0ef003bc, 0xef203bc0, 0xb003dcc0, 0x0019c00f, +0x253c00ef, 0x1fc00fb6, 0x00000600, 0x00000000, 0x00750002, 0x214c2017, +0x36d02930, 0x4c001300, 0xe10c3402, 0x00c34035, 0x0d30034d, 0xd30037c0, +0xf0030d00, 0x4025c001, 0x094d00d3, 0x0bc00d70, 0x00000420, 0x00000000, +0x00b4a013, 0x09540011, 0x74400150, 0x450f5100, 0x400f1002, 0x11f1047f, +0x1f1047c4, 0xf1117c41, 0xd0474401, 0x0454411d, 0x054411f1, 0x4f420f10, +0x00000200, 0x00000000, 0x0426a807, 0x1024808d, 0x41400090, 0x00000120, +0x400c1006, 0x12c104b1, 0x2c104b04, 0xc104b141, 0xd0490412, 0x04a04120, +0x490412c1, 0x1f400cd1, 0x00000a00, 0x00000000, 0x40788004, 0x043403c1, +0x78401ed0, 0x84012100, 0x411e1026, 0x01e1107b, 0x9c182784, 0xe1027940, +0xd0858401, 0x0259401e, 0x058401e5, 0x12401e90, 0x00000200, 0x00000000, +0x00241012, 0x202c828f, 0x31c6a4b1, 0x0c085304, 0xc00c3042, 0x08c30031, +0x8c34030c, 0xc34031c0, 0xf0010c10, 0x0221d0a1, 0x520c08c3, 0x4bc00cf0, +0x00000040, 0x00000000, 0x003db802, 0x81dc00f5, 0x3dc00f58, 0xfc007f02, +0xc08fb006, 0x00ff003f, 0x8ff8c3fc, 0xff003cc1, 0xf001fc80, 0x0a5ec80f, +0x02fc80fb, 0x0bc02f71, 0x00000660, 0x00000000, 0x0037a015, 0x027c0017, +0x07c009f0, 0x7c125f00, 0xc10d3083, 0x06d701b7, 0x4cb0537c, 0xdb0a74d0, +0xf0036c01, 0x4027c801, 0x017c00d3, 0x57c08df8, 0x00000e00, 0x00000000, +0x00398812, 0x02b480ed, 0x3b400ed0, 0x34006d08, 0x402e7023, 0x16e105b3, +0xce1013b4, 0xe1213840, 0xd0038482, 0x0013400e, 0x01b422e1, 0x4b404ed0, +0x00000620, 0x00000000, 0x00790003, 0x06b40121, 0x7b401ed0, 0xb4016d00, +0x405e900f, 0x01ed007b, 0x1f900734, 0xc9007140, 0xd807a401, 0x106b4012, +0x06b401e1, 0x0f441ed8, 0x00000400, 0x00000000, 0x80732812, 0x0b3400cd, +0x33400cd8, 0x34094d00, 0x400c5007, 0x80c90033, 0x0c100334, 0xc1003148, +0xd2032400, 0x0013402c, 0x023400c1, 0x4b400cd0, 0x00000c20, 0x00000000, +0x0115a817, 0x29f48073, 0x1b4027d0, 0x7c015f00, 0xc005b005, 0x005f0017, +0x07b1017c, 0x5b001dc0, 0xf0096c00, 0x401fc085, 0x01fc0053, 0x5fc005f0, +0x00000620, 0x00000000, 0x00070012, 0x087c011f, 0x07e101f0, 0x7c800f00, +0xc001f080, 0x00170003, 0x01f0803c, 0x1f0006c0, 0xf0485c20, 0x008bc002, +0x087c001f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x00230810, 0x020c0097, +0x67800930, 0x2c00d301, 0xc4093002, 0x00870024, 0x0970024c, 0x9f002480, +0x30027c00, 0x0024c009, 0x025c0083, 0x43c009f0, 0x00000c20, 0x00000000, +0x10660005, 0x0a540291, 0x26c00952, 0x45809100, 0x40095102, 0x00910024, +0x0910027c, 0x9c002440, 0x104a7400, 0x04a54009, 0x4a448095, 0x074009d0, +0x00000800, 0x00000000, 0xc224a01c, 0x03448185, 0x27400910, 0x44009140, +0x40181002, 0x01950064, 0x18d00640, 0x9d006540, 0x10063401, 0x006c401b, +0x06540191, 0x634009d0, 0x00000200, 0x00000000, 0x80e02010, 0x0b140281, +0x26440850, 0x0408c102, 0x5028505a, 0x868180a0, 0x28100a34, 0x8d00a150, +0x140a3602, 0x00a9402a, 0x0a040285, 0x434028d0, 0x00000080, 0x00000000, +0x0006b019, 0x004c0057, 0x87614130, 0x4c161310, 0xc0013110, 0x01170004, +0x0170004c, 0x1f0005c0, 0x30007c00, 0x000cc001, 0x005c0013, 0x77c001f0, +0x00000ac0, 0x00000000, 0x40afb019, 0x0afc02bf, 0x2cc54bf0, 0xfc049f01, +0xc029f002, 0x089f00a7, 0x29f30a6c, 0x9f00a6c0, 0xf00a7c02, 0x08a7c02d, +0x0afc829f, 0x67c029f0, 0x00000e60, 0x00000000, 0x052fa018, 0x32fc02fb, +0xa5c149f0, 0x7c40bf00, 0xc029f05a, 0x069b01a4, 0xc9b0327c, 0x9701a6c0, +0x301e6c14, 0x0164c079, 0x0a4c1293, 0x60d489f2, 0x00000e00, 0x00000000, +0x01870818, 0x385c0011, 0x164021d0, 0x760a1d00, 0x4001d818, 0x04110385, +0xe1103874, 0x11038440, 0xb0184402, 0x15054041, 0x80840411, 0x704821d0, +0x00000c20, 0x00000000, 0x0423a010, 0x02300289, 0x236008d2, 0xb4148d00, +0x406ad012, 0x02a92028, 0x0a900294, 0xa5092a40, 0x1022a410, 0x40aa502a, +0x1e8400a1, 0x404068d0, 0x00000e80, 0x00000000, 0x0025a818, 0x82542099, +0x266009d8, 0x74809d02, 0x400bd003, 0x00b1002d, 0x0b1802f6, 0xb1002c40, +0x9002e400, 0x002f400b, 0x02c400b1, 0x604009d2, 0x00000620, 0x00000000, +0x1027a805, 0x467c009b, 0xe7c689f0, 0x74099f10, 0xc009d002, 0x009b0024, +0x09b0025c, 0x970026c0, 0x30026c00, 0x0026c029, 0x064c2093, 0x14c409f0, +0x00000e20, 0x00000000, 0x00218210, 0x567c1097, 0x35c009f0, 0x7c019f00, +0xe009f002, 0x009f0023, 0x0970023c, 0x8f0023c0, 0xf0025c00, 0x0021c008, +0x927d209f, 0x53c009f0, 0x00000600, 0x00000000, 0x02050814, 0x004c0813, +0x07c001f0, 0xfc201f00, 0xc0023100, 0x0437010f, 0x02b010fc, 0x33010cc0, +0x30008c04, 0x000cc043, 0x00fc0433, 0x50c40130, 0x00000420, 0x00000000, +0x201ca014, 0x01d48171, 0x174805d2, 0x74117d04, 0x48051001, 0x00512017, +0x05108164, 0x57001444, 0x10014400, 0x00144005, 0x01f40051, 0x50400510, +0x00000200, 0x00000000, 0x0072a014, 0x2b0402c9, 0x53600d90, 0x3400cd20, +0x400c5003, 0x00c54033, 0x2c100334, 0xc9403041, 0x10030400, 0x0030500c, +0x033400d1, 0x50440d90, 0x00000a00, 0x00000000, 0x40308005, 0x00b401c1, +0x1b608e90, 0xb400ed00, 0x4802520c, 0x0021000b, 0x021000b4, 0x25000840, +0x10008400, 0x00094002, 0x04b48021, 0x14400e90, 0x00000200, 0x00000000, +0x00681015, 0x078e4163, 0x5bc41ef2, 0xfc016f10, 0xc01a5407, 0x01a70063, +0x1b3406fe, 0xbb806cd0, 0x3404cd01, 0x404cc018, 0x04bc01a3, 0x54d01cb4, +0x00000040, 0x00000000, 0x4025b010, 0x005c805f, 0x87c00df0, 0x7c005f00, +0xc005b000, 0x005f8817, 0x0570016c, 0x5b0017c0, 0xf0037c00, 0x0036c205, +0x037c005f, 0x43c1ed70, 0x00000660, 0x00000000, 0x007fa000, 0x25cc81bb, +0xdcc01fb0, 0xfc01f310, 0xc41f7006, 0x0173105c, 0x17b005cc, 0x73005ac0, +0x3007fc01, 0x007cc217, 0x07ec8173, 0x00c01f31, 0x00000e20, 0x00000000, +0x00298815, 0x00ec9431, 0x1ac20eb0, 0xb000a100, 0x40031001, 0x00bf002c, +0x0a100294, 0xab002940, 0x5000b640, 0x0009c00a, 0x80c420ab, 0x54488e10, +0x00000620, 0x00000000, 0x00290000, 0x038640a1, 0x1e408c10, 0x14006100, +0x400a5022, 0x0029020a, 0x02900024, 0x21000e40, 0x90003400, 0x00084000, +0x04a40821, 0x00400e10, 0x00000400, 0x00000000, 0x00272804, 0x22200301, +0x80601c90, 0x30810000, 0x40041001, 0x80c11032, 0x0c920324, 0xc9203044, +0xd0033480, 0x033148ac, 0x030480c9, 0x10500c10, 0x00000c00, 0x00000000, +0x0075a815, 0x06480093, 0x96423f30, 0x7c049341, 0xd0057001, 0x00d94036, +0x0db1036d, 0xd30036c0, 0xb0037c00, 0x00b4c02d, 0x032c00d3, 0x54c00f34, +0x00000600, 0x00000000, 0x40570001, 0x007c409f, 0x17c0cdf2, 0x7c805f00, +0xc029f00a, 0x401f0001, 0x0170005c, 0x170007c0, 0x70003e00, 0x0005c441, +0x006c001f, 0x07c00df0, 0x00000c00, 0x00000000, 0x00070880, 0x02fc107f, +0x5fc00fb0, 0x8e05bf05, 0xc002b001, 0x00b7002e, 0x087002bc, 0xaf002dc0, +0x3100dc00, 0x0000c00a, 0x00dc0083, 0x00c00d34, 0x00000c02, 0x00000000, +0x04462081, 0xa4740955, 0x06420d53, 0x44005d00, 0x412d100a, 0x00510014, +0x05b20174, 0x5d2016c0, 0x50034400, 0x00354005, 0x034c805b, 0x04400d10, +0x00000822, 0x00000000, 0x0064a001, 0x01340095, 0x07600d11, 0x44028d00, +0x40059000, 0x80550016, 0x05500170, 0x5c001540, 0x10035400, 0x00344805, +0x03540051, 0x05400c51, 0x00000200, 0x00000000, 0x08002010, 0x01340001, +0x03440c50, 0x06000d08, 0x40081003, 0x00810020, 0x08100234, 0x8d102040, +0x50000400, 0x00014008, 0x00440089, 0x40400c10, 0x00000080, 0x00000000, +0x0006b000, 0x037c80d7, 0x07c40d30, 0x44009d00, 0xc001b000, 0x00170006, +0x0150007c, 0x1f200548, 0x30005c20, 0x0004c001, 0x005c0013, 0x00d00f32, +0x00000ac0, 0x00000000, 0x000fb005, 0x03fc007f, 0x0ac00ff0, 0xec003f00, +0xc80ff203, 0x00ff203f, 0x0ff003fc, 0xff003fc0, 0xf003fc00, 0x083fc00f, +0x03dc80ff, 0x17c00ff0, 0x00000e40, 0x00000000, 0x840fa003, 0x10cd943f, +0xbfe44190, 0x7c3c3301, 0xe34ff28b, 0x14ff092c, 0x2df0234c, 0xd300b6c6, +0x30034c04, 0x003cc04f, 0x02cc00e7, 0x0ce00f30, 0x00000e00, 0x00000000, +0x00b70801, 0x5a6c021d, 0x3de16110, 0xde061102, 0x416fd10b, 0x16f11624, +0x0fd01bd4, 0xe140bc40, 0x542bc502, 0x403d502c, 0x02440051, 0x06c00c14, +0x00000c20, 0x00000000, 0x0003a011, 0x2114144d, 0x33048010, 0x14044501, +0x400cd01b, 0x00cd0920, 0x4cd08b04, 0xc101b170, 0x10830508, 0x0030408c, +0x030480d5, 0x44400c10, 0x00000e80, 0x00000000, 0x0235a803, 0x8314048d, +0x35400590, 0x74015560, 0x500cd003, 0x00d10024, 0x0dd00345, 0xd1083540, +0x50034400, 0x4035400d, 0x034408d5, 0x0e000d10, 0x00000620, 0x00000000, +0x0047a802, 0x2854019d, 0x37402db4, 0x5c121700, 0x480df003, 0x80df4024, +0x0dd0834c, 0xd30035c0, 0x34034c00, 0x4034c80d, 0x134c02d5, 0x08000d30, +0x00000e20, 0x00000000, 0x104d8007, 0x806c813f, 0x3dc40572, 0xdc001b00, +0xc00df103, 0x00d70023, 0x0df2037c, 0xef003ec0, 0xf0033c80, 0x003bc00e, +0x0bfd11fb, 0x1fc00ef4, 0x00000600, 0x00000000, 0x40250802, 0x1d7c40d3, +0x32d029fc, 0x6c809f10, 0xd00d7083, 0x80c7a064, 0x0c32034c, 0xd70032c0, +0x74234c00, 0x1034c00d, 0x034c00db, 0x08c80d30, 0x00000420, 0x00000000, +0x00248013, 0x01740091, 0x3c40a5b8, 0xc4019d00, 0x400f1083, 0x20fb806c, +0x0f1103c4, 0xf1003d40, 0xf007d500, 0x403c412f, 0x2f4413c3, 0x4c40ef10, +0x00000200, 0x00000000, 0x0002a007, 0x08248f09, 0x31440010, 0x04288d00, +0x480c1003, 0x00c50020, 0x0c108305, 0xc1003148, 0x521f0480, 0x5030413c, +0x0f4583c9, 0x1d600c10, 0x00000a00, 0x00000000, 0x00708004, 0x06b40161, +0x79605a92, 0x8605ad00, 0x401e10b7, 0x09e10368, 0xde101784, 0xe1017940, +0xd4079609, 0x5270501e, 0x478449f9, 0x11601e11, 0x00000200, 0x00000000, +0x00001012, 0x083c084b, 0x71c0f050, 0x4d014f00, 0xc05c3027, 0x05c50960, +0x5c10070c, 0xc30075c4, 0x60070c05, 0x4230c80c, 0x034e80cb, 0x49d08c30, +0x00000040, 0x00000000, 0x003db802, 0x02fc00ff, 0x3cc00f70, 0xdc005f06, +0xc08fe023, 0x08ff122f, 0x8df0237c, 0xff023d03, 0xb00ffc08, 0x023fc00f, +0x01fe20f7, 0x0ac20ff0, 0x00000660, 0x00000000, 0x2017b015, 0x037c409f, +0x37c00d70, 0x7c80d325, 0xc44d30b3, 0x16df8224, 0x4df0634c, 0xd30334d0, +0xf60b4c08, 0x4334da8d, 0x034c01d3, 0x57800df2, 0x00000e00, 0x00000000, +0x08198012, 0x03b4606d, 0x3b420ed0, 0xb420e148, 0x44cc11c3, 0x06f64168, +0xced08384, 0xc10c3840, 0xf4170408, 0x0338401e, 0x038400a1, 0x4b404ed0, +0x00000620, 0x00000000, 0x00790003, 0x07b401ed, 0x7b401ad0, 0xb403e101, +0x40de1017, 0x01ed0160, 0x5ed197a4, 0xe9017240, 0x90278405, 0x0170405e, +0x078501e1, 0x0f029ed0, 0x00000400, 0x00000000, 0x00732812, 0x237412cd, +0x33403d90, 0x3402c100, 0x400c1003, 0x00c90020, 0x0cd00324, 0xc9003240, +0xd0030400, 0x0030400c, 0x030453c1, 0x4b400cd2, 0x00000c20, 0x00000000, +0x009da817, 0x05fc007f, 0x17c1a7f0, 0x7c007300, 0x10073001, 0x005f001c, +0x05f0016c, 0x5b0016c0, 0xf0014c00, 0x4014c005, 0x49cc8373, 0x5fc005f0, +0x00000620, 0x00000000, 0x02070012, 0x487c001f, 0x07c001f0, 0x3c100f00, +0xc001f400, 0x00170007, 0x01f0004c, 0x175005c2, 0xf0007c00, 0x0007c201, +0x0c7c881f, 0x4bc021f0, 0x00000c00, 0x00000000, 0x04270810, 0x067c029f, +0x20c03970, 0x4c809300, 0xc009b202, 0x00974020, 0x0934020e, 0x930024c0, +0x30020c00, 0x4024c008, 0x02680093, 0x40c009f0, 0x00000c20, 0x00000000, +0x00262001, 0x3e74009d, 0x24501911, 0x44009110, 0x40091002, 0x00950064, +0x09100244, 0x91002440, 0x30025400, 0x0a24dc09, 0x2a60c291, 0x045129d0, +0x00000800, 0x00000000, 0x0024a018, 0x0274009d, 0x24400910, 0x44019100, +0x40089202, 0x00850064, 0x08100244, 0x81002050, 0x14824480, 0x48204a29, +0x026400d1, 0x604008d0, 0x00000200, 0x00000000, 0x00202010, 0x2234088d, +0x20008810, 0x045c8102, 0x50881022, 0x28850220, 0x88102206, 0x814a2060, +0x04221402, 0x02304088, 0x22240881, 0x414088d0, 0x00000080, 0x00000000, +0x0506a01d, 0x587c161f, 0x80c16174, 0x0d061305, 0xc561b058, 0x16152584, +0x20305845, 0x130584c0, 0x20584c54, 0x0584c160, 0x096c0243, 0x74c021f2, +0x00000ac0, 0x00000000, 0x002fa819, 0x92fc04bf, 0x27c04bf8, 0x7804bf41, +0xc049e012, 0x049f012f, 0x69f0127c, 0x9f012700, 0x70927c40, 0x0125c049, +0x12fc04bf, 0x66c069f0, 0x00000e60, 0x00000000, 0x0127a018, 0x02fc06b3, +0x2cc00bf0, 0x4c043313, 0xc0693052, 0x0093008d, 0x0970224c, 0x931524e0, +0x710acc90, 0x01a6c209, 0x0a4c0093, 0x60c16936, 0x00000e00, 0x00000000, +0x0007001c, 0x18748011, 0x045141d2, 0x0c151140, 0x40411050, 0x10111044, +0xc1142014, 0x112102c1, 0x12105506, 0x0007c041, 0x00054451, 0x70400310, +0x00000c20, 0x00000000, 0x00a3a010, 0x4a348681, 0xa04028d8, 0x06028120, +0x40681002, 0x48810131, 0x48500204, 0x81052040, 0x50122414, 0x00aa510a, +0x82c490a1, 0x40400a10, 0x00000e80, 0x00000000, 0x0425a818, 0x82740091, +0x244008d0, 0x44349100, 0x40091002, 0x00910024, 0x09100244, 0x91002646, +0x10027080, 0x402f400b, 0x42c408a1, 0x60400b10, 0x00000620, 0x00000000, +0x0027a805, 0x267c0493, 0x244409f2, 0x4c009300, 0xc0093402, 0x00934025, +0x0970024d, 0x93402440, 0x71026c00, 0x0026c009, 0x06480393, 0x14d00930, +0x00000e20, 0x00000000, 0x48218014, 0x067c049f, 0x27c089f0, 0x1d201f00, +0xc008f402, 0x009f2007, 0x09f0027c, 0x9f0023c0, 0xd1025c00, 0x0027c008, +0x127d209f, 0x53d009f0, 0x00000600, 0x00000000, 0x00850814, 0x005c061f, +0x07c001f0, 0x5c021300, 0xc0013000, 0x00038004, 0x0030000c, 0x130004c0, +0x34404c80, 0x0106c001, 0x084d0213, 0x50408130, 0x00000420, 0x00000000, +0x2014a014, 0x01d4807d, 0x174017d0, 0x74013100, 0x40051401, 0x005f0004, +0x05100144, 0x51001542, 0xf0092c40, 0x00d6c045, 0x01040151, 0x51403514, +0x00000200, 0x00000000, 0x0032a014, 0x4b0000cd, 0x27451cd2, 0x1401d100, +0x400c1083, 0x20c10034, 0x0c500304, 0xd1003060, 0x100a3400, 0x00f6401c, +0x031401c1, 0x50413d10, 0x00000a00, 0x00000000, 0x00388805, 0x0284818d, +0x2b4106d2, 0xb498e100, 0x480e1003, 0x04c58230, 0x8e191384, 0xe1013960, +0xd0023404, 0x04324008, 0x039410e1, 0x15400011, 0x00000200, 0x00000000, +0x00780015, 0x859d01ef, 0xefc81ed2, 0x9c05e315, 0x503f3007, 0x01e10178, +0x5c701784, 0xc301f8c0, 0x3406b891, 0x407ac81e, 0x049c0173, 0x54c11e34, +0x00000040, 0x00000000, 0x0035a810, 0x027c009f, 0x27c005f0, 0x7c00df00, +0xc96de10b, 0x4adf0037, 0x0db18b7c, 0xde00b7c5, 0xf0026c08, 0x4037c009, +0x016c00df, 0x43c009f0, 0x00000660, 0x00000000, 0x007fb000, 0x07c881ff, +0x64c099f0, 0x4c01f303, 0xc01d304f, 0x0bdb1874, 0x1f306fcc, 0xd302f448, +0xb1064c01, 0x004dc01f, 0x07cc81b3, 0x03801f30, 0x00000e00, 0x00000000, +0x02398015, 0x0084302d, 0x604052d0, 0xc409c100, 0x401d3037, 0x01d21071, +0x1f5007c4, 0xf1027c40, 0x54368401, 0x000c500b, 0x038400b1, 0x57408210, +0x00000620, 0x00000000, 0x00390000, 0x218408cd, 0x2840a8d0, 0x9408e022, +0x400e9003, 0x00e1003c, 0x4e108384, 0xe1003a44, 0x94120440, 0x0029400e, +0x00a41821, 0x03400e10, 0x00000400, 0x00000000, 0x04b32804, 0x0804021d, +0x204090d0, 0x1400c100, 0x400c1003, 0x00c90031, 0x0c500304, 0xc1203250, +0x54020500, 0x00204008, 0x09241291, 0x13400010, 0x00000c20, 0x00000000, +0x04f5a815, 0x234c005f, 0x28500df0, 0xd500d348, 0xc00eb503, 0x00f10030, +0x0f3403cd, 0xf3403ec0, 0xb0014c00, 0x4035c005, 0x0b6d83d1, 0x57c00d34, +0x00000620, 0x00000000, 0x10370001, 0x033c105f, 0x27c00df0, 0x2c00df00, +0xc00df003, 0x00d70037, 0x0df0837c, 0xdf0031c0, 0xf0007c00, 0x2033c005, +0x0b5c02df, 0x07c805f0, 0x00000c00, 0x00000000, 0x003f0880, 0x20dc003f, +0x2dc00b31, 0xdc05f300, 0xc00f3003, 0x00fb003c, 0x0ef003cc, 0xf3003cc4, +0xb00dfc00, 0x003cc03e, 0x46cc0873, 0x00c08f20, 0x00000c22, 0x00000000, +0x00360081, 0x0644e01d, 0x25c00910, 0x7420d100, 0x400d1503, 0x00d10034, +0x0dd08354, 0xd1083540, 0x10007440, 0x02f54025, 0x14058251, 0x04401d10, +0x00000802, 0x00000000, 0x8034a001, 0x03440add, 0x27401510, 0x7400d158, +0x400d1003, 0x00c10074, 0x0dd00344, 0xd1003448, 0x90433400, 0x40944025, +0x134602d1, 0x04400c50, 0x00000200, 0x00000000, 0x80302010, 0x0104004d, +0xa3489410, 0x2404c100, 0x400d1043, 0x04c18234, 0x0cd03314, 0xc1403141, +0x14303480, 0x40114000, 0x03460081, 0x40400050, 0x00000080, 0x00000000, +0x0036a000, 0x005d009f, 0xebcc5130, 0xfc4bd301, 0xc0df30b7, 0x49f32174, +0x1ff01fcc, 0xf3237cc0, 0xb0877c2f, 0x4004c80d, 0x020c0053, 0x00d00d74, +0x00000ac0, 0x00000000, 0x083fa805, 0x00fc803f, 0x6de2d3f4, 0xfc0dce02, +0xc2dff017, 0x07ff037b, 0x5df2277c, 0xff037fc1, 0xf01cfc09, 0x400fc003, +0x00fc003f, 0x17c003b0, 0x00000e40, 0x00000000, 0x003fa003, 0x70fc0437, +0x0fc1c3f0, 0xde023f02, 0xc00f70a0, 0x0cfb083e, 0x0fb003fc, 0xf7003ac0, +0x3000cc04, 0x000cc00b, 0x00e440f3, 0x0cc40fb0, 0x00000e20, 0x00000000, +0x00370801, 0x096c0a01, 0x874469d1, 0x44441d02, 0x400fd008, 0x88e5033c, +0x0d009234, 0xfd003440, 0x50015408, 0x0034400d, 0x034402f5, 0x0d400d50, +0x00000c20, 0x00000000, 0x0033a011, 0x82340015, 0x21404450, 0x04c04d09, +0x614cd030, 0x20c92432, 0x0c904330, 0xcd007240, 0x11000400, 0x0031420c, +0x002402c1, 0x4d440c94, 0x00000e80, 0x00000000, 0x0035a803, 0x03751111, +0x270009d8, 0x44600c00, 0x400dd091, 0x00d90034, 0x0d109274, 0xdd007441, +0x500454a0, 0x0035440d, 0x074440d5, 0x0d400d40, 0x00000620, 0x00000000, +0x0133a802, 0x087c03d7, 0x15c021f0, 0x45049e06, 0xc00d7818, 0x00db0036, +0x3cb0037c, 0xdf0036c0, 0x30874c00, 0x0005d008, 0x076c00d3, 0x89c00da0, +0x00000e02, 0x00000000, 0x003d8007, 0xa16d007f, 0x57c021f0, 0xec007d08, +0xc00ef006, 0x00f7103b, 0x8fe007fc, 0xff003fc0, 0xf003bc00, 0x003ac00f, +0x81bc00ef, 0x1fd80ef2, 0x00000600, 0x00000000, 0x00b50802, 0x485c025b, +0xb45035b4, 0x7e30d700, 0xc00df001, 0x80d70037, 0x0d710354, 0xd78837c0, +0x11225e00, 0x2237c00d, 0x036d0cd3, 0x09400d92, 0x00000400, 0x00000000, +0x02b4a013, 0x09440441, 0x3540a190, 0x72035102, 0x420fd203, 0x00f1203f, +0xbd180344, 0xfa06f4c0, 0x10064480, 0x02b740ad, 0x034400fb, 0x6ec00fb2, +0x00000200, 0x00000000, 0x0032a007, 0x00140449, 0x01400050, 0x348b0100, +0x420cd000, 0x20c50833, 0x8c500354, 0xd1003140, 0x10061400, 0x30434008, +0x010403c1, 0x1e420c10, 0x00000a00, 0x00000000, 0x007c8004, 0x04840161, +0x48401e50, 0xb401a500, 0x001ed105, 0x09e10073, 0x1e108786, 0xc9007c40, +0x10038401, 0x0073401e, 0x278441e9, 0x36409e94, 0x00000200, 0x00000000, +0x00301012, 0x1b1c044b, 0x11c024f2, 0x34084740, 0x804cd000, 0x28c70033, +0x0c50031c, 0xc10031c0, 0x30225400, 0x0033c08c, 0x014c08c3, 0x4bc00d30, +0x00000040, 0x00000000, 0x1039b802, 0x03fc007f, 0x1fc20fb0, 0xfc009b00, +0xc02ff209, 0x28ff003f, 0x0cd1237c, 0xfc103fcc, 0xf013fc00, 0x003f800f, +0x03de38ee, 0x0bc00ff1, 0x00000660, 0x00000000, 0x0497a015, 0x067c005f, +0x24c009b0, 0x7c009f00, 0xc8ed7013, 0x42df0037, 0x4df8035c, 0xdb2033c8, +0xf0034c08, 0x0007c009, 0x037c12df, 0x54c00d38, 0x00000e00, 0x00000000, +0x00398812, 0x02b4006d, 0x28680cb0, 0xb400ed00, 0x424ed003, 0x14cf043b, +0x2ed003ac, 0xe42539c1, 0xf0039088, 0x003b400e, 0x019c00ed, 0x4d404e50, +0x00000624, 0x00000000, 0x00790003, 0x07b6016d, 0x78401e90, 0xb4a1ed00, +0x601e5007, 0x09ed037b, 0x5ed00784, 0xe1267b41, 0xd0078605, 0x007b401e, +0x07b405ed, 0x04409c10, 0x00000400, 0x00000000, 0x08b32812, 0x0334424d, +0xf1402d92, 0x3400dd00, 0x400cd033, 0x40c51033, 0x0cd02734, 0xc5083140, +0xd02b1640, 0x0033400c, 0x071400cd, 0x49000d50, 0x00000c20, 0x00000000, +0x22918817, 0x21fc1a7f, 0x5cd087b2, 0x7c027f01, 0x40057001, 0x405d0017, +0x03d001c4, 0x51100b40, 0xd005cc00, 0x001f4405, 0x05f4005d, 0x5cc00510, +0x00000620, 0x00000000, 0x10070012, 0x0870c01d, 0x06c02170, 0x7c101f04, +0xc000f100, 0x001f0807, 0x01f00068, 0x170007c0, 0x70005c00, 0x8007c001, +0xa07c020f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x00370810, 0x067c0097, +0x64c019f8, 0x4c089300, 0xc409b003, 0x408f8025, 0x41b1024e, 0x930505c1, +0x30426c00, 0x0020c008, 0x425c4393, 0x40c00970, 0x00000c20, 0x00000000, +0x04262001, 0x62740291, 0xa4585978, 0x44419112, 0x4009d002, 0x809fa024, +0x7012022c, 0x9101c448, 0x36060c40, 0x6026d809, 0x02448091, 0x04400910, +0x00000800, 0x00000000, 0x0024a018, 0x02748295, 0xa04089d1, 0x44009100, +0x4809d002, 0x009d3025, 0x01900244, 0x85400540, 0x94077400, 0x00244009, +0x02160091, 0x60400950, 0x00000200, 0x00000000, 0x00242010, 0x22342881, +0x20408850, 0x07088142, 0x4008d002, 0x0c8d0220, 0x89182264, 0x81022464, +0x900a4640, 0x90224008, 0x0a0604c1, 0x40542812, 0x00000080, 0x00000000, +0x0006b01d, 0x583c1617, 0x844161d0, 0x4c961305, 0x4801b350, 0x031d8585, +0x61b80846, 0x078585e1, 0xb8007c9e, 0x8004e001, 0x001e0113, 0x74c00178, +0x00000ac0, 0x00000000, 0x0037b819, 0x12fc04fe, 0x2fc04bf0, 0x7c04bf01, +0xe169e252, 0x8c970127, 0x4be012f8, 0x9f012fc0, 0x780adc01, 0x00afc02b, +0x0afc089d, 0x67c029f0, 0x00000e64, 0x00000000, 0x0027a018, 0x42cc88b7, +0x2dc10bb8, 0x4c08f305, 0xc049f00a, 0x149b0126, 0x1b30222c, 0x93206fc1, +0x3132dc80, 0x0023c009, 0x0a482093, 0x60c329b0, 0x00000e00, 0x00000000, +0x1007081c, 0x48450a11, 0x84482110, 0x440a1141, 0x40a1d000, 0x14130504, +0x31109044, 0x1502c740, 0x10384404, 0x00074401, 0x08440a31, 0x70402150, +0x00000c20, 0x00000000, 0x0027a010, 0x12240485, 0x21414810, 0x16248504, +0x4008d022, 0x008d0322, 0x48101264, 0x810d2341, 0x54021412, 0x006b4008, +0x1e05c0a5, 0x48404890, 0x00000e80, 0x00000000, 0x0825a818, 0x0b648091, +0x24480812, 0x56049501, 0x4809d8a2, 0x00990020, 0x01140244, 0x95040701, +0x10024480, 0x002f4009, 0x024400b5, 0x60400950, 0x00000620, 0x00000000, +0x00a7a805, 0x066c0297, 0x25801918, 0x5d009704, 0xc009e102, 0x009f0026, +0x0130026c, 0x93000340, 0x70065c00, 0xc0a7c019, 0x024c0096, 0x14c009b0, +0x00000e20, 0x00000000, 0x28358014, 0x0a5c009f, 0x27804974, 0x6c008b08, +0xc009f086, 0x00970027, 0x81f0027c, 0x9f0107c0, 0xf00e1c40, 0x8927c459, +0x023c009b, 0x5bd008f0, 0x00000600, 0x00000000, 0x01050814, 0x084c0013, +0x87c881f2, 0x5c001f02, 0xc0007000, 0x40060006, 0x01b0005c, 0x1f0005cc, +0x60007c20, 0x000fc001, 0x807c0033, 0x50c00132, 0x00000420, 0x00000000, +0x80908014, 0x11c40075, 0x1f400770, 0x74107c20, 0x4005b001, 0x00510014, +0xa3100104, 0x5d028c44, 0x14154000, 0x40134005, 0x01740041, 0x50400514, +0x00000200, 0x00000000, 0x20b2a014, 0x034401d1, 0x37401cd0, 0x1401cd00, +0x400c9003, 0x00c50032, 0x8c900314, 0xdd223140, 0x50071040, 0x0033400c, +0x03340045, 0x50500c10, 0x00000a00, 0x00000000, 0x003c8005, 0x038442c5, +0x3b4c02d0, 0xb400ed00, 0x400c8117, 0x08e10038, 0x5f1823c4, 0xcd013944, +0x1003c400, 0x000b4006, 0x133400a5, 0x14400c10, 0x00000200, 0x00000000, +0x00781015, 0x078d0161, 0x4bc41ef3, 0x1c01ef00, 0xc41eaa07, 0x8df705fe, +0x9eb1379c, 0xef0079c0, 0x70079c01, 0x087bc01e, 0x37bc01a7, 0x54c01e30, +0x00000040, 0x00000000, 0x0131b810, 0x017c00df, 0x27c00d70, 0x7ea09f00, +0xc00db023, 0x22df10b7, 0xecc08b74, 0xdf31b64e, 0xf0031c0a, 0x00070005, +0x237c005b, 0x43c00df1, 0x00000660, 0x00000000, 0x00ffa000, 0x27f401f9, +0x7fc01fe0, 0xfc01ff00, 0xc11fb01f, 0x01fe007c, 0x1ff007cc, 0xff06ffc0, +0xf007cc03, 0x007fc01f, 0x07fc017f, 0x00c19f30, 0x00000e00, 0x00000000, +0x00398015, 0x23b604a1, 0x3b4082d0, 0xb480ed00, 0xc04f1053, 0x04ed003a, +0x4ed003ac, 0xf9103b44, 0xd0038404, 0x000b4006, 0x13b400ad, 0x5550cf54, +0x00000620, 0x00000000, 0x00390000, 0x82b4406d, 0x1b400ad0, 0xb408e500, +0x640ed133, 0x00ed0038, 0x0ed00386, 0xed203b4c, 0xd0028600, 0x023b400e, +0x4bb401ad, 0x00700e12, 0x00000400, 0x00000000, 0x00132804, 0x04361085, +0x334038c0, 0x70108d00, 0x400c5003, 0x00cd0032, 0x0cd00726, 0xc9003340, +0xd0020400, 0x00030004, 0x0734404d, 0x11400d50, 0x00000c20, 0x00000000, +0x2235a815, 0x407c809f, 0x37c13df0, 0xf443df00, 0x400ff80f, 0x00fd003c, +0x8dd01384, 0xfd063741, 0xd2074500, 0x0077041d, 0x07b4005d, 0x54400f14, +0x00000620, 0x00000000, 0x00b70001, 0x083c005b, 0x93c061e0, 0x7c484f20, +0xc00cb813, 0x00df0037, 0x2df0037c, 0xdf0037c0, 0xf0227c00, 0x0443c815, +0x037c209f, 0x07c00df0, 0x00000c00, 0x00000000, 0x003f0880, 0x03fc003b, +0x8fc00730, 0xcc01f300, 0xd00f3603, 0x00df003c, 0x1f3003fc, 0xff003fc0, +0xf007fc00, 0x003fc00f, 0x035e80b3, 0x00d00f30, 0x00000c22, 0x00000000, +0x01322081, 0x2d740051, 0x87402552, 0x44111100, 0x420d1083, 0x80dd2034, +0x4d120374, 0xdd203748, 0xd2037480, 0x60074805, 0x03748051, 0x04400df2, +0x00000800, 0x00000000, 0x0034a001, 0x00740399, 0xa7410d10, 0x44089100, +0x400d5103, 0x00dd0034, 0x4d140374, 0xdd007740, 0xd0237400, 0x0037440d, +0x03740051, 0x04400d12, 0x00000200, 0x00000000, 0x00302810, 0x00340001, +0x03400010, 0x04000108, 0x400c1003, 0x00cd0030, 0x0c100374, 0xcd007340, +0xd0023420, 0x00026004, 0x03740081, 0x40500cd0, 0x00000080, 0x00000000, +0x0036a000, 0x027c001b, 0x07c00134, 0x8c009300, 0xe20d3003, 0x00ff883c, +0x0d3803fe, 0xfd8037e0, 0xf8027e00, 0x8037e00d, 0x03de0093, 0x00e00d38, +0x00000ac0, 0x00000000, 0x003ba825, 0x00fc803f, 0x0fc003f0, 0xfc003f00, +0xc00ff003, 0x00ff003f, 0x0ff003f8, 0xff003fc0, 0xf002fc00, 0x100f8007, +0x03fc007d, 0x17c00ff0, 0x00000e60, 0x00000000, 0x003fa003, 0x40fc002b, +0x3fc423b8, 0xcec09522, 0xc24fb033, 0x0c33133f, 0xc1704b7c, 0xf300bcc1, +0x2093fca0, 0x411cc08f, 0x00fc08b3, 0x0ce06f30, 0x00000e00, 0x00000000, +0x08331801, 0x091c8013, 0xbdc26514, 0xcc16b100, 0x40cf1033, 0x02c101bf, +0x6c100bc4, 0xf100bc40, 0x100b7410, 0x0494c04d, 0x005c44d1, 0x0c40ad10, +0x00000c20, 0x00000000, 0x0023a011, 0x52340045, 0x33606012, 0x14888102, +0x410c5113, 0x06d58333, 0x40100326, 0xc521b048, 0x10237444, 0x0306440c, +0x007400c1, 0x4c404c10, 0x00000e80, 0x00000000, 0x0225a007, 0x23541015, +0x31628510, 0x54009180, 0x400dda83, 0x00d50037, 0x1d140306, 0xd5003440, +0x10037400, 0x0014408d, 0x095408d1, 0x0c440d10, 0x00000620, 0x00000000, +0x0077a882, 0x147c0917, 0x37c00910, 0x5c409300, 0xc00d7403, 0x00154037, +0x0878036c, 0xd74034c0, 0x30037860, 0x0056c030, 0x097c0093, 0x00400d30, +0x00000e22, 0x00000000, 0x607d8007, 0x04fc813b, 0x3fc40f73, 0x2d00af20, +0xc00f3003, 0x20fb383b, 0x0ff3036c, 0xfb003bd0, 0xf4037c00, 0x015fc01b, +0x01dc01ff, 0x1fc80df4, 0x00000602, 0x00000000, 0x40250802, 0x0a6c0253, +0x37c809b4, 0x4c009700, 0xc80db083, 0x08db2037, 0x0934036d, 0xd30034c0, +0xb0134c00, 0x0025c021, 0x097c00df, 0x08c04d30, 0x00000420, 0x00000000, +0x2024a013, 0x02441110, 0xff413d10, 0xc50bbd02, 0x41af1003, 0x00d302bf, +0x0d1083e8, 0xf0003c40, 0x10039400, 0x0112c009, 0x133400dd, 0x0d402f10, +0x00000200, 0x00000000, 0x00262003, 0x00641189, 0x33613012, 0x04088502, +0x401c1003, 0x801110b3, 0x04000304, 0xc1003040, 0x90031400, 0x00114808, +0x483400cd, 0x1c400d10, 0x00000a00, 0x00000000, 0x006a0804, 0x058509b1, +0x7b401210, 0x8601bd00, 0x405e1017, 0x05e92173, 0x9e000784, 0xc1017040, +0x10079401, 0x105a4496, 0x04f401ed, 0x7d409e14, 0x00000200, 0x00000000, +0x00321012, 0x802c2003, 0x33423813, 0x0e018721, 0xc01c3827, 0x01c30173, +0x54201704, 0xc30070c0, 0xb0071c05, 0x0011c629, 0x023c68cf, 0x48c08c30, +0x00000040, 0x00000000, 0x003da802, 0x01fc207f, 0x37c08b72, 0xfc148f00, +0x880d70ab, 0x00d740b7, 0x8ff263fc, 0xff02bfd2, 0xf143fc04, 0x003fc007, +0x23bc40ff, 0x0bc18ff0, 0x00000660, 0x00000000, 0x00278014, 0x074c000f, +0x37e00530, 0x4c029f04, 0xd04df02b, 0x401f12b4, 0x0d70937c, 0xdf12b4c4, +0xf0034e06, 0x00149003, 0x017c80df, 0x54c00df0, 0x00000e00, 0x00000000, +0x00299912, 0x03ac002d, 0x39c80630, 0x84148d21, 0x432c7003, 0x00ef0038, +0x0f103334, 0xed003844, 0xd3079c08, 0x00184208, 0x01b400ed, 0x4c504ed0, +0x00000624, 0x00000000, 0x00790000, 0x072401ad, 0x7b601e55, 0xb401ad12, +0x405ed017, 0x01ed017a, 0x1e1017b4, 0xcd017844, 0xd107a409, 0x08ea4832, +0x87b401ed, 0x06501ed0, 0x00000402, 0x00000000, 0x10332016, 0x432640cd, +0x35400c50, 0x35409d00, 0x400d5003, 0x00cd0032, 0x0c100334, 0xcd003040, +0xd0031400, 0x00724008, 0x233400dd, 0x4a4c0cd0, 0x00000c20, 0x00000000, +0x80178817, 0x11c4027d, 0x17400750, 0x7c807f00, 0x4805f001, 0x007f0016, +0x0712017c, 0x5f8014c8, 0xf001ec00, 0x009ec037, 0x0df4005f, 0x5ed005d0, +0x00000620, 0x00000000, 0x00058012, 0x007c101f, 0x05c04138, 0x0c001f80, +0xc0217000, 0x02170005, 0x11a0007c, 0x1f8007c0, 0xf0005c00, 0x0045c001, +0x047c001f, 0x49c001f0, 0x00000c00, 0x00000000, 0x00210810, 0x024d009b, +0x27ca0970, 0x4c029308, 0xc009b002, 0x00930260, 0x0830023c, 0x8f2024c0, +0x30820c20, 0x0024d108, 0x224c009f, 0x40c018f2, 0x00000c20, 0x00000000, +0x00260001, 0x42448083, 0xa7c02971, 0x45039b40, 0x51291202, 0x02950164, +0x09b10274, 0x9d002440, 0x500e5400, 0x00644009, 0x0644009d, 0x06c019d0, +0x00000800, 0x00000000, 0x0024801c, 0x02440099, 0xa74029d0, 0x46109100, +0x48091402, 0x00910024, 0x09920276, 0x95002440, 0x10224400, 0x00746009, +0x02440095, 0x604089d0, 0x00000200, 0x00000000, 0x82202814, 0x22040089, +0x234088d0, 0x04808108, 0x40081022, 0x08c50220, 0x88902234, 0x8d022050, +0x50021408, 0x82206428, 0x0205088d, 0x424088d0, 0x00000080, 0x00000000, +0x1586b01d, 0x584c001b, 0x07c161f9, 0x4c141105, 0xe141b058, 0x56130584, +0x61b0587c, 0x1f0584c5, 0x32514402, 0x2584c941, 0x814e021f, 0x74c161f8, +0x00000ac0, 0x00000000, 0x012fa919, 0x12fc00b7, 0x25c04b70, 0x7c34bf05, +0xc149f412, 0x04bf0127, 0x4bb0127c, 0x9f0127c8, 0xf0027c06, 0x013fc00b, +0x03fca4bf, 0x67c048f2, 0x00000e64, 0x00000000, 0x00a7a018, 0x0adc00bf, +0x2cc02ff0, 0x7c4cb701, 0xc90b320a, 0x00bf00af, 0x69f0127c, 0x9314a4c9, +0x3012cc10, 0x01acc049, 0x02cc0093, 0x64c02bf0, 0x00000e00, 0x00000000, +0x2003181c, 0x086d001d, 0x84d00078, 0x5c0e1147, 0x4021b008, 0x001d0187, +0x20d01064, 0x11008442, 0x18287406, 0x01044271, 0x00445411, 0x704821d0, +0x00000c22, 0x00000000, 0x00a10012, 0x1a34009d, 0x200828d0, 0x36008100, +0x4548509a, 0x168d81a3, 0x08d04a34, 0x85812040, 0x10026410, 0x01a24188, +0x02040091, 0x484068d0, 0x00000e80, 0x00000000, 0x00252018, 0x0364009d, +0x344029d0, 0x1420d008, 0x40095d02, 0x109d1027, 0x09d00264, 0x85102042, +0x10027480, 0x00664209, 0x12450891, 0x604009d0, 0x00000620, 0x00000000, +0x0067a805, 0x127c1b9f, 0x24c009d0, 0x7c809300, 0x40095902, 0x219f3027, +0x08f80274, 0x974024c0, 0x10024c00, 0x00a6c009, 0x0e4c0083, 0x14d009f0, +0x00000e20, 0x00000000, 0x01258012, 0x027d009f, 0x27c00970, 0x5c009f28, +0xc009b002, 0x049f0027, 0x09f0027c, 0x9b4027d0, 0xf4025c00, 0x4025c009, +0x027c009f, 0x5bc009f0, 0x00000600, 0x00000000, 0x00050810, 0x084c0213, +0x00e0a170, 0x7c001300, 0xc001b000, 0x12130007, 0x01f0006c, 0x131004c0, +0x31004c00, 0x4084c000, 0x044c0013, 0x50c001f0, 0x00000420, 0x00000000, +0x0014a014, 0x01c43361, 0x5d4007d0, 0x74007d30, 0x40174081, 0x0371001f, +0x07920168, 0x51001448, 0x10019400, 0x00545007, 0x09c40051, 0x505007d0, +0x00000200, 0x00000000, 0x0032a014, 0x630406c9, 0x70451d93, 0x3400c108, +0x601c1003, 0x0bc90033, 0x0c500304, 0xc8083040, 0x10030600, 0x4034440c, +0x030400c1, 0x504189d0, 0x00000a00, 0x00000000, 0x01788801, 0x03858029, +0x19600e90, 0xb4426d04, 0x41025013, 0x41e9005b, 0x8e9203a4, 0xe9083040, +0x14019404, 0x0420405e, 0x838404e1, 0x144002d0, 0x00000200, 0x00000000, +0x43781011, 0x858c81e3, 0x786216f0, 0xfc41a300, 0xc01e341f, 0x01cb0073, +0x5ee0078c, 0xcb0278c0, 0x32048c07, 0x0078e02c, 0x078d05f3, 0x54c01ff0, +0x00000040, 0x00000000, 0x0235a810, 0x017d0007, 0x17c00df0, 0x7c005f00, +0xc009305b, 0x80d70037, 0x2db02b7c, 0xd713b7c4, 0xf0003c50, 0x0027c01d, +0x037c09df, 0x43c007f2, 0x00000660, 0x00000000, 0x007d2000, 0x27fe0137, +0x7cc05df2, 0x4c09f902, 0xc01f7027, 0x01f3027c, 0xfd320f4c, 0xd30374c0, +0x30170c01, 0x007dc01f, 0x05bc01f3, 0x08c89f30, 0x00000e00, 0x00000000, +0x02391815, 0x238c0863, 0x4ac01030, 0x0c0d2102, 0x40105207, 0x018f0054, +0x9f1007c4, 0xcb007441, 0x50250c61, 0x423c408f, 0x23b400e1, 0x5440ce10, +0x00000620, 0x00000000, 0x00390000, 0xa114003d, 0x38438610, 0xa448a101, +0x600ed003, 0x0c601039, 0x0e121384, 0xe5c23860, 0x9000b424, 0x0028400e, +0x03b410e1, 0x20400a10, 0x00000400, 0x00000000, 0x80312004, 0x01040041, +0x00400091, 0x04000100, 0x40089003, 0x01150031, 0x3c100304, 0xd9003040, +0xd4801440, 0x00a0402c, 0x0b3400c1, 0x18500810, 0x00000c20, 0x00000000, +0x003d8815, 0x435c1117, 0x24500938, 0xec00d340, 0xd00df003, 0x06d10025, +0x0f3403cc, 0xf7203cd0, 0xb0037c80, 0x00b4d05f, 0x0f7c01f3, 0x54c00934, +0x00000620, 0x00000000, 0x00370001, 0x035c8297, 0xb7c00c30, 0x7c02d708, +0x40057003, 0x04df2006, 0x4cf0037c, 0xdc1033c0, 0x70014c40, 0x0167c10d, +0x077c00df, 0x27c001f0, 0x00000c00, 0x00000000, 0x80330884, 0x23cd0933, +0x04c001f0, 0xcc003e00, 0xc055f103, 0x00d3187c, 0x0f3003c4, 0xf3203ce0, +0x3401cc00, 0x003cc00e, 0x43fc00f3, 0x07c009e0, 0x00000c20, 0x00000000, +0x00360085, 0x0f558485, 0x96c09d70, 0x44024900, 0xc225f003, 0x40db0036, +0x0d15036c, 0xd1103440, 0x10010500, 0x0020400f, 0x065c00db, 0x874011d0, +0x00000800, 0x00000000, 0x0034a001, 0x80440011, 0x254009d0, 0x4440dd82, +0x4028d003, 0x00d10120, 0x0d100314, 0xd1003440, 0x10074600, 0x0034400d, +0x237400d1, 0x074019d0, 0x00000200, 0x00000000, 0x88302810, 0x00140005, +0x236060d0, 0x04109901, 0x4100d883, 0x10890202, 0x6c10c334, 0xc1043040, +0x10410410, 0x0034500c, 0x071400c9, 0x434008d0, 0x00000080, 0x00000000, +0x003eb000, 0x00440011, 0x455031d9, 0x8c0b0f00, 0xc091f137, 0x41532174, +0x3f10a7dc, 0xe301fc50, 0x303d4c8f, 0x0024c00d, 0x037c00d3, 0x07c009f0, +0x00000ac0, 0x00000000, 0x003fa805, 0x00fd002f, 0x84023370, 0xec011f00, +0xc0517037, 0x111f0377, 0x3ff017ec, 0xff457fc8, 0xf015fd05, 0x002fc00f, +0x02dc00ff, 0x17c00bf0, 0x00000e60, 0x00000000, 0x002fa003, 0x00fc003b, +0x0fc00fb0, 0xfcc43302, 0xc08ff030, 0x003b001e, 0x0fb010ec, 0xfb003bc0, +0x3002fc04, 0x003ec003, 0x00ec00f3, 0x0cc00f30, 0x00000e00, 0x00000000, +0x20270801, 0x00748081, 0x84480f10, 0x74c81102, 0x404fd020, 0x12d11194, +0x0d502354, 0xf10435c0, 0x34023408, 0x02bd4601, 0x024400d5, 0x05400f10, +0x00000c60, 0x00000000, 0x0023a011, 0x82348889, 0x32500c90, 0x14a04109, +0x404cd010, 0x060d0492, 0x0c100024, 0xc9013340, 0x10001400, 0x00304208, +0x022400c1, 0x44440c10, 0x00000e80, 0x00000000, 0x0025a803, 0x43740391, +0x34440d10, 0x7406d102, 0x400dd013, 0x00c10010, 0x0d510344, 0xd1083740, +0x900664a0, 0x0035430d, 0x036400d5, 0x0d400d10, 0x00000600, 0x00000000, +0x00278802, 0x087c839b, 0xc6c00db2, 0x7c201300, 0xc80df084, 0x803b011e, +0xad3400ec, 0xdb02b7c0, 0x300e7c40, 0x0036c025, 0x256c00d3, 0x08400d30, +0x00000e22, 0x00000000, 0x01298007, 0x15fc00bf, 0x4e400ef0, 0xf401bf44, +0xc00ef083, 0x00fd001f, 0x1ff003fc, 0xdf107cc8, 0x7003bc00, 0x0037c03e, +0x079c00ef, 0x1fc00ef2, 0x00000600, 0x00000000, 0x00250802, 0x0a0c00df, +0xb4c00d70, 0x4c025f08, 0xc00df100, 0x003f001e, 0x0db020bc, 0xd340b6c0, +0x700a5c40, 0x2030c101, 0x025d00d7, 0x08d00d30, 0x00000420, 0x00000000, +0x0024a013, 0x034400dd, 0xf6c02f10, 0x4507dd00, 0x400fd003, 0x00dd0017, +0x7d100340, 0xf1003740, 0x10136c00, 0x083c481d, 0x034400f1, 0x4c400f1a, +0x00000200, 0x00000000, 0x0022a007, 0x000405cd, 0x05411c50, 0x14074d10, +0x420c9000, 0x000d2033, 0x7cd04010, 0xc5403640, 0x10060400, 0x003044a0, +0x011400cd, 0x0d400c10, 0x00000a80, 0x00000000, 0x00788004, 0x048401ed, +0x4b509c10, 0x84096d04, 0x401ed004, 0x41ed007b, 0x1e103784, 0xe5007b40, +0x1003a601, 0x00780000, 0x270481e9, 0x10401c10, 0x00000820, 0x00000000, +0x02201012, 0x004c10cf, 0x81c18c70, 0x1c00df00, 0x480cf00b, 0x041f8033, +0x0cf0101c, 0xc700b2c0, 0x702a1c20, 0x0030c08c, 0x221c00df, 0x48c08c10, +0x00000040, 0x00000000, 0x002db802, 0x20fc08ff, 0x0ec08ff0, 0xfc00ff00, +0x410ff003, 0xc0ff003f, 0xadf0139c, 0xdb0037c0, 0xf0136c02, 0x023fc04f, +0xa3fc00f7, 0x0bc00ff2, 0x00000620, 0x00000000, 0x0027a015, 0x044d00d3, +0x35c08d30, 0x4d00df00, 0xc07df013, 0x00330036, 0x4c3400cd, 0xd30230c0, +0xf002fc02, 0x01b4c00d, 0x847c00d3, 0x43c04d32, 0x00000e00, 0x00000000, +0x00398812, 0x000400f7, 0x38c08c10, 0x8400cd08, 0x40ccd043, 0x00c10830, +0x4e100304, 0xf5023840, 0xd0038404, 0x033c500e, 0x02b440e5, 0x4b414e50, +0x00000664, 0x00000000, 0x00f90003, 0x048401e5, 0x7b405e10, 0x8481ed00, +0x405ed08f, 0x0121007a, 0x9f100484, 0xe5017c40, 0xd004b409, 0x0278401e, +0x06340de1, 0x13429e10, 0x00000400, 0x00000000, 0x20772812, 0x00042ac5, +0xb1400c10, 0x0404cd01, 0x400cd003, 0x00c14034, 0x2c100305, 0xd5203040, +0xd0070400, 0x0030406d, 0x033400c5, 0x5b400c50, 0x00000c00, 0x00000000, +0x005d8817, 0x45cc0173, 0x9fc00534, 0x4c007d10, 0xc005f005, 0x00530016, +0x0231014c, 0x57160cd1, 0xf045fc00, 0x0014c077, 0x41f40053, 0x5fc00534, +0x00000e20, 0x00000000, 0x00070012, 0x007c921d, 0x868420f0, 0x7ca01f00, +0xc001f240, 0x203f0007, 0x21f008fc, 0x1f0007c0, 0xf0407c00, 0x0007c021, +0x007c001f, 0x4bc801f0, 0x00000600, 0x00000000, 0x00270810, 0x024c008b, +0x20e409f0, 0x4c009300, 0xc009b003, 0x40870027, 0x01360e4c, 0x970004c1, +0x74021c00, 0x0825c059, 0x024c0097, 0x43c009f0, 0x00000420, 0x00000000, +0x00260001, 0x06540091, 0xa4e0c970, 0x54009100, 0x4409d002, 0x00910027, +0x91100a2c, 0x91000440, 0x52064400, 0x00244019, 0x82440091, 0x174009d0, +0x00000800, 0x00000000, 0x0024a018, 0x06440098, 0x265409d0, 0x44018100, +0x40099202, 0x00b50027, 0x055202c4, 0x95080040, 0xd0225400, 0x10234809, +0x02440085, 0x734009d0, 0x00000200, 0x00000000, 0x12202010, 0x721408c1, +0x21400850, 0x1408c102, 0x4008d202, 0x2ca10323, 0x8c1032a4, 0x81022050, +0xd00a0402, 0x0122402c, 0x0a040085, 0x534028d0, 0x000000a0, 0x00000000, +0x0586b01d, 0x184c161b, 0x964141f0, 0x4c161345, 0xc001a050, 0x031700c7, +0x61700dcc, 0x170584c1, 0xf0005c14, 0x02c7c001, 0x004d1e17, 0x67c001f0, +0x00000ac0, 0x00000000, 0x092fb819, 0x12fc04bf, 0x3ec009f2, 0x7c04bf01, +0xc169f052, 0x0c9f0327, 0x4bf03278, 0x9f012fc0, 0x740afc00, 0x0265442a, +0x0afc819b, 0x67c029f0, 0x00000e24, 0x00000000, 0x0227a018, 0x42fc08bf, +0x3cc14930, 0x7c10bf00, 0xc109f042, 0x01930067, 0x5a3016cc, 0x930663c1, +0xf0324c20, 0x0424c34a, 0x027c049b, 0x60c049b0, 0x00000e00, 0x00000000, +0x0387081c, 0x00740a1d, 0x805021b0, 0x5c021d0a, 0x4021d000, 0x1e114087, +0x7150146c, 0x15008740, 0xd039453e, 0x00854061, 0x00740211, 0x60414110, +0x00000c60, 0x00000000, 0x0023a010, 0x5234048d, 0x20400810, 0x34148d14, +0x4008d002, 0x008105a3, 0x08904a04, 0x81012741, 0xd0020400, 0x05204518, +0x52341c89, 0x48402890, 0x00000400, 0x00000000, 0x0825a818, 0x0274009d, +0x24400990, 0x54109d01, 0x4009d282, 0x00910027, 0x01d00064, 0x95042740, +0xd2824440, 0x00254009, 0x2a742095, 0x60400910, 0x00000600, 0x00000000, +0x3067a805, 0x1e7c139f, 0xa4c00930, 0x74289f00, 0xc409f24e, 0x00934027, +0x41b0004c, 0x830023c0, 0xf08a4c00, 0x0024c009, 0x0a7c009b, 0x149009b0, +0x00000e20, 0x00000000, 0x00a58014, 0x127c049c, 0x27c00df0, 0x5c019f00, +0xc009f026, 0x009f0027, 0x9170007c, 0x9f1227c8, 0xf2437c00, 0x0827c139, +0x067c009b, 0x4bc009f0, 0x00000600, 0x00000000, 0x00050814, 0x807c021f, +0x860240f0, 0x7c201301, 0xc001f008, 0x40030801, 0x2132100c, 0x131004d0, +0x30085c00, 0x0006c801, 0x887c2013, 0x43c001f0, 0x00000420, 0x00000000, +0x0014a014, 0x4574007d, 0x5d4815d0, 0x74007101, 0x4005d001, 0x005b0014, +0xa3100cc5, 0x75001c41, 0x160d4400, 0x00144017, 0x01748051, 0x534005d0, +0x00000200, 0x00000000, 0x0032a014, 0x037401c9, 0x32400cd0, 0x3480c110, +0x400cd883, 0x00c10031, 0x1c100305, 0xc1003040, 0x10071400, 0x08365425, +0x037400c1, 0x53400dd8, 0x00000a00, 0x00000000, 0x08388005, 0x03b442ad, +0x79502ed0, 0xb400e100, 0x405cd013, 0x04c90138, 0x1f122b14, 0xe5023848, +0x1446c488, 0x1038502e, 0x23b400e1, 0x07404ed8, 0x00000220, 0x00000000, +0x00781015, 0x06bc01ef, 0x7ac01cd0, 0xbc012300, 0xc01ef01f, 0x01e32071, +0x1e34478d, 0xf34378c1, 0x30a79c05, 0x417ed016, 0x07bc01e3, 0x47c0bef0, +0x00000040, 0x00000000, 0x0235b810, 0x827c001f, 0x17c269f0, 0x7c80df40, +0x408df003, 0x84df0137, 0xcdf29364, 0xdb00b7c0, 0xf2033c00, 0x07b7c00d, +0x037c22df, 0x43c02df0, 0x00000620, 0x00000000, 0x007ba800, 0x26cc01f3, +0x5fc03ff2, 0xfc81ff00, 0xc01f7027, 0x13f304fd, 0x1f3007cc, 0xf3007fc0, +0x3004cc01, 0x087fc09f, 0x87cc93f3, 0x00c01f30, 0x00000400, 0x00000000, +0x08399015, 0x828408ab, 0x2840aed0, 0xf400ed02, 0x400f10c3, 0x10e10038, +0x4e501384, 0xe1213b40, 0x10008404, 0x013b40ce, 0x038c00fb, 0x54680e14, +0x00000460, 0x00000000, 0x063d0000, 0x428400e1, 0x184006d8, 0xb4002d00, +0x400e5003, 0x00e10031, 0x0e110384, 0xe1203b40, 0x9020a420, 0x083b440a, +0x03a400e1, 0x00400e10, 0x00000400, 0x00000000, 0x10332004, 0x02040201, +0x004000d0, 0x3406cd00, 0x440c1007, 0x45c14030, 0x3c521704, 0xc100f340, +0x92012540, 0x00334208, 0x0b0000c9, 0x10400c11, 0x00000400, 0x00000000, +0x0079a035, 0x074d0041, 0x24d00df0, 0xfc00df00, 0xc00f7003, 0x03f3003d, +0x8d344f4d, 0xf3063fc1, 0xb0036c00, 0x003fc235, 0x1fed00f3, 0x54d00f20, +0x00000620, 0x00000000, 0x00370001, 0x037c00df, 0x31c024f0, 0x3c001f00, +0xc40cf003, 0x20cf0037, 0x0df0037c, 0xcf0037c0, 0x72475c00, 0x0037c059, +0x031c00df, 0x07c00df0, 0x00000c00, 0x00000000, 0x803f0080, 0x02cc1173, +0x2cc03ff0, 0xcc002300, 0xc00f0043, 0x00df003d, 0x0f38030c, 0xff003cc0, +0xf2131c00, 0x0037c00f, 0x434c00f3, 0x13c00f30, 0x00000c22, 0x00000000, +0x00362081, 0x02540351, 0x554011d0, 0x440b1102, 0x400db003, 0x00dd0834, +0x0c100344, 0xdd083440, 0x10024400, 0x0037411d, 0x036c80db, 0x17400d10, +0x00000802, 0x00000000, 0x0034a001, 0x07440611, 0x044009d0, 0x4402d110, +0x400d5003, 0x00dd2035, 0x1d520774, 0xdd083440, 0x90005600, 0x0033401d, +0x034400c1, 0x07400d10, 0x00000200, 0x00000000, 0x00342810, 0x03140091, +0x204000d0, 0x04200500, 0x400cd003, 0x00cd0030, 0x1c100735, 0xcd003052, +0x10010400, 0x00334008, 0x032600c9, 0x43400c10, 0x000000a0, 0x00000000, +0x0036a000, 0x024c0001, 0x04c001f0, 0x4d001340, 0xc00d7003, 0x00ff003d, +0x0d54037c, 0xdf083c40, 0xf0005c00, 0x003fc009, 0x034c40f3, 0x03c00e34, +0x00000ac0, 0x00000000, 0x003fa805, 0x02fc003f, 0x0fd002f0, 0xfc003b00, +0x800fb003, 0x40ff103f, 0x0ff003cc, 0xef003bc0, 0xf100fc00, 0x003fc00b, +0x03fc00ff, 0x17c00ff0, 0x00000e20, 0x00000000, 0x023fa003, 0x50ed003f, +0x37c083f0, 0xfc04d300, 0xc2cdb002, 0x06df01bf, 0x0f3019fc, 0xf3003cc0, +0xf023dc04, 0x023cd08e, 0x12fc04ff, 0x0cc04b31, 0x00000e00, 0x00000000, +0x01370801, 0x1845021d, 0xbb4081d2, 0x7402fb22, 0x40cf7032, 0x8edd00bf, +0x0d100874, 0xf106b440, 0xd003441e, 0x0130404d, 0x227402dd, 0x0c402d10, +0x00000c20, 0x00000000, 0x0033a011, 0x4134020d, 0x334000d0, 0x3416c10d, +0x400cd140, 0x00cd0333, 0x1c101914, 0xc1813444, 0xd0131404, 0x0030600c, +0x023408cd, 0x4c428c12, 0x00000e80, 0x00000000, 0x0035a803, 0x0154081d, +0x374401d0, 0x7440d910, 0x400dd006, 0x40dd0037, 0x1d100174, 0xd1003442, +0xd0034400, 0x0035410d, 0x027400dd, 0x0c404d11, 0x00000620, 0x00000000, +0x0037a802, 0x0b7d101f, 0x37c03df0, 0x7c00d300, 0xc00db047, 0x00dd0037, +0x0c310938, 0xd30034d0, 0xf0035c00, 0x0034d08c, 0x027c00df, 0x08c00934, +0x00000e22, 0x00000000, 0x203d8007, 0x036c817f, 0x3fc09df2, 0xfc00ff00, +0xc20d5203, 0x80ff003f, 0x0ff000fc, 0xdf003fc0, 0xf00bfc00, 0x003ec01f, +0x163c00df, 0x1fc21ef8, 0x00000600, 0x00000000, 0x02350802, 0x096c009f, +0x31d02134, 0x7c80d308, 0xd00d3401, 0x00d70034, 0x4db0095c, 0xd70037c0, +0xf0435c00, 0x4034c80d, 0x024c00d3, 0x08d00d30, 0x00000420, 0x00000000, +0x087ca013, 0x294500dd, 0x3c40a110, 0x7400f100, 0x000f1003, 0x00f5003c, +0x0d100144, 0xfd103b40, 0xd1034491, 0x003c500d, 0x025400f1, 0x6c40ad10, +0x00000200, 0x00000000, 0x0072a007, 0x000440dd, 0x30400090, 0x3400c100, +0x409c1002, 0x20c50030, 0x2c908104, 0xd5003340, 0xd00b1401, 0x0032500c, +0x830080c5, 0x1c402c10, 0x00000a00, 0x00000000, 0x00788004, 0x048409cd, +0xf8409290, 0xf409e102, 0x409e1037, 0x8de51070, 0x5e1024c4, 0xec007b41, +0xd2878405, 0x007a429e, 0x279421e5, 0x74001e14, 0x00000200, 0x00000000, +0x00301012, 0x2b0c00cf, 0x71c0fcb0, 0x3c85c301, 0xc05c3406, 0x01d70030, +0x9cb0290c, 0xc70173c0, 0xf21f1c01, 0x2832c88c, 0x030c00c7, 0x48c00830, +0x00000040, 0x00000000, 0x003db802, 0x03dc00ff, 0x37e00d70, 0x3c0aff42, +0xc08df083, 0x38df00b7, 0x0ff0a3ae, 0xde0237c0, 0xf0237c10, 0x023dc08f, +0x03fc10fb, 0x0bc00af0, 0x00000660, 0x00000000, 0x0037a015, 0x034c001f, +0x36c00df0, 0x6c08d301, 0xc04d3082, 0x00df033c, 0x2db0034c, 0xd30237c0, +0x311b4c06, 0x0034c04c, 0x02fc01d3, 0x57c00d30, 0x00000e00, 0x00000000, +0x00b98812, 0x03ac006d, 0xb8000ed0, 0x8404c105, 0x42cc1003, 0x04ed1138, +0x0e100094, 0xc1013f41, 0x1053c40c, 0x00bd40ce, 0x02b405e1, 0x4f400e10, +0x00000624, 0x00000000, 0x00790003, 0x0f8503ad, 0x71401ed0, 0x0401e100, +0x40de1046, 0x05ed007a, 0x9e1007c4, 0xe1007b40, 0x5037a401, 0x007a40ff, +0x063400e1, 0x07401a10, 0x00000400, 0x00000000, 0x00332812, 0x436400cd, +0x31400dd0, 0x0400c100, 0x400c1403, 0x00cd0036, 0x4c100314, 0xc1003340, +0x50132440, 0x1037445d, 0x223400c1, 0x4b400810, 0x00000c20, 0x00000000, +0x0015a817, 0x01cc007f, 0x17d0f7f0, 0xac005300, 0xc0053005, 0x005f0016, +0x333409cc, 0x63001fc1, 0x704ced00, 0x40168033, 0x053c0053, 0x5fc00538, +0x00000620, 0x00000000, 0x00070012, 0x007c041f, 0x02c001f0, 0x7c801f00, +0xc001f000, 0x401f0005, 0x2170047c, 0x1f0007c4, 0x90005e00, 0x0005c001, +0x007c001f, 0x4bc021f4, 0x00000c00, 0x00000000, 0x00230810, 0x0a4c0083, +0x24c00930, 0x4c008300, 0xc0283402, 0x00930026, 0x31b0020c, 0x9b2023c0, +0x30804c05, 0x0024c401, 0x024c0083, 0x40d00870, 0x00000c20, 0x00000000, +0x40a62001, 0x0a440091, 0x24540914, 0x44009110, 0x40091002, 0x80910024, +0x01300244, 0x91002744, 0x10004400, 0x00244001, 0x0a440091, 0x04407910, +0x00000800, 0x00000000, 0x00a4a018, 0x0a440091, 0x64401910, 0x44489100, +0x68091002, 0x00810026, 0x01900244, 0x99002740, 0x10040500, 0x00244001, +0x22440091, 0x60404950, 0x00000200, 0x00000000, 0x02202010, 0x22058881, +0x20408810, 0x44048100, 0x40881072, 0x88816320, 0x0c102204, 0x91022340, +0x14220608, 0x02204088, 0x02040081, 0x40400810, 0x00000080, 0x00000000, +0x2586b01d, 0x584c5603, 0x04cb6030, 0x4d011105, 0xc1613018, 0x160320c6, +0x41b0584d, 0x1b0587c1, 0x32584c16, 0x4084d161, 0x514c1413, 0x74c14170, +0x00000ac0, 0x00000000, 0x0927b819, 0x12fc04bf, 0x27c04ff0, 0xfc1c9f45, +0xc249f012, 0xa49f0327, 0x0b7012bc, 0x9e0127c8, 0xf012fc04, 0x01a7c04b, +0x02fc009f, 0x67c00bf0, 0x00000e64, 0x00000000, 0x00afa018, 0x8acdc29f, +0x2fc04bf0, 0x6c3cb308, 0xc86b321a, 0x849f00a7, 0x1bf2224c, 0xa320afc1, +0x301ec014, 0x00a4c879, 0x02fc00b0, 0x63c00b30, 0x00000e00, 0x00000000, +0x0807081c, 0x0844020d, 0x874161d0, 0x04461115, 0x40211000, 0x841d0283, +0x01d0102c, 0x11010340, 0xb0184402, 0x11845000, 0x08740a1b, 0x71c02114, +0x00000c20, 0x00000000, 0x01a3a010, 0x9a04028d, 0xb34088d0, 0x64008100, +0x40685c1a, 0x128d0123, 0x18d00a14, 0x8100a760, 0x5a364414, 0x01a04078, +0x0a340081, 0x4b402c10, 0x00000e80, 0x00000000, 0x0025a818, 0x4245028d, +0x274009d0, 0x44209100, 0x4009500a, 0x009d0027, 0x19d00a74, 0x91202760, +0x99064420, 0x00244019, 0x02740099, 0x61401910, 0x00000620, 0x00000000, +0x0027a805, 0x064d009f, 0x27c039f0, 0x6c809340, 0xc0097402, 0x009f0027, +0x09f0065c, 0x930027c0, 0x70020f00, 0x0024c808, 0x023c0093, 0x17c00930, +0x00000e20, 0x00000000, 0x00258014, 0x167d009f, 0x23c039f0, 0x7c009f10, +0xc009b002, 0x008f0027, 0x09f0266c, 0x9f4027c0, 0xf0427c00, 0x0023c009, +0x427c009f, 0x59c009f0, 0x00000600, 0x00000000, 0x01050814, 0x006c001f, +0x07c06130, 0x7c000704, 0xc0013008, 0x00130006, 0x4130007c, 0x030005c0, +0xf0006c04, 0x0004d001, 0x006c801b, 0x53c01130, 0x00000420, 0x00000000, +0x005ca014, 0x01c4005d, 0x1f403711, 0x34015100, 0x40050401, 0x00510015, +0x071081f0, 0x71019840, 0x7009c402, 0x00144007, 0x09f41151, 0x53400714, +0x00000200, 0x00000000, 0x0072a014, 0x072500cd, 0xb3000c12, 0x3409c580, +0x400c9003, 0x00c10030, 0x28100334, 0xc1013340, 0xd0032408, 0x8032400c, +0x212401c9, 0x53400410, 0x00000a00, 0x00000000, 0x0c388005, 0x0d8404ed, +0x3f402610, 0xb440e100, 0x401a9003, 0x04e10079, 0x161837b4, 0xa1003a60, +0x10038400, 0x0132404e, 0x013400e1, 0x17400610, 0x00000200, 0x00000000, +0x00581015, 0x072c05ef, 0x7fc01a34, 0xbc01a710, 0xc41cb017, 0x07e1007a, +0x1a3007bc, 0xe3007bc0, 0xf007ac01, 0x04fac03e, 0x04ac01eb, 0x57c01e30, +0x00000040, 0x00000000, 0x2015b810, 0x017d38df, 0x37c001f0, 0x7c009f00, +0xc00d714b, 0x02df42b7, 0x00f00b7c, 0xdf0035c0, 0xf0037c00, 0x01b5c04d, +0x007c00df, 0x43c005f0, 0x00000660, 0x00000000, 0x006fa000, 0x86ec4df3, +0x4fc01f30, 0xac81d340, 0xc01f2c47, 0x03f302fc, 0x91202f4c, 0x7f237cc0, +0xf007c801, 0x007cc03f, 0x04fc09b1, 0x03c01ff0, 0x00000e00, 0x00000000, +0x20b98015, 0x80ad08f1, 0x0f400c11, 0x0409e101, 0x484b5003, 0x00f1007d, +0x144007c4, 0x0d007043, 0xf0150404, 0x1038410e, 0x10b400a1, 0x574006d0, +0x00000620, 0x00000000, 0x02090000, 0x02a454e9, 0x0b408e90, 0xe408a020, +0x440e5003, 0x08e10038, 0x4b1803a4, 0x6d203a40, 0xd0638600, 0x0038400e, +0x02b480a1, 0x03400ad0, 0x00000400, 0x00000000, 0x80172804, 0x002500c9, +0x03400c90, 0x44009100, 0x400c582f, 0x00c10031, 0x20582b24, 0x4d003240, +0xd0850400, 0x0030402c, 0x02340081, 0x134000d0, 0x00000c20, 0x00000000, +0x4035a815, 0x036d21fb, 0x37c00db4, 0xec00d300, 0xc00d700b, 0x00f3403c, +0x28342fed, 0x5d0036c0, 0xf0070d00, 0x403cd02f, 0x017c0083, 0x57c005f0, +0x00000620, 0x00000000, 0x28a70001, 0x037c00d7, 0x17e00470, 0x7c00df00, +0xc009e403, 0x00df0837, 0x05f0035c, 0x1f0035c0, 0x70137c00, 0x0037c00d, +0x017c009b, 0x07c085f0, 0x00000c00, 0x00000000, 0x003f0880, 0x08cd00f3, +0xecc01bf0, 0xcc43df30, 0xc00f3043, 0x00fb003b, 0x0b3003bc, 0x132038c1, +0x3012fc01, 0x0038c10f, 0x088d00b3, 0x00d05b30, 0x00000c22, 0x00000000, +0x00e60081, 0x005400d1, 0x86c421d0, 0x6c00d909, 0x400d1083, 0x00d10037, +0x01120374, 0x1b013440, 0x50027403, 0x0036c00c, 0x88440095, 0x04402010, +0x00000800, 0x00000000, 0x4204a001, 0x0b4400c1, 0x344141d0, 0x44009d00, +0x400d1003, 0x40d91037, 0x01100374, 0x51820440, 0x10037434, 0x0034400d, +0x0b440491, 0x04402d14, 0x00000200, 0x00000000, 0x00002010, 0x031580c1, +0x124000d0, 0x24508900, 0x40081003, 0x00c10433, 0x45504334, 0x09030040, +0x50117400, 0x0032400d, 0x03040085, 0x40400410, 0x00000080, 0x00000000, +0x0002a000, 0x000c00f3, 0xa4c001f0, 0xcc0b9f02, 0xc40d1403, 0x00fb007f, +0xd93407f4, 0x538544c9, 0x30367c08, 0x0038c00e, 0x024c0093, 0x00c00130, +0x00000ac0, 0x00000000, 0x000f9805, 0x00fc00ff, 0x4cc113f0, 0xdd41bb01, +0xc11ff047, 0x11ff0037, 0x52b003fc, 0x7f0147c4, 0xf0347c05, 0x003fc00f, +0x02fc00bf, 0x17c003f0, 0x00000e60, 0x00000000, 0x000fa803, 0x02bc00ff, +0x2fc00bb0, 0xfc006300, 0xc04f32a0, 0x20bf022d, 0x02f003cc, 0xef003ec0, +0x7011fc00, 0x010cc003, 0x03cd0473, 0x0fc00fb0, 0x00000e00, 0x00000000, +0x00270801, 0x004400fd, 0x36400810, 0x7400d108, 0x408d1403, 0x009d2033, +0x491013c5, 0xdd003440, 0xd2097400, 0x00b5422d, 0x03540251, 0x07400d10, +0x00000c20, 0x00000000, 0x0803a211, 0x021400c5, 0x314208d2, 0x34800100, +0x400c1013, 0x008d0023, 0x04504304, 0xcd007363, 0x50093401, 0x02015080, +0x03240841, 0x47400c90, 0x00000e80, 0x00000000, 0x0045a803, 0x104400dd, +0x364049d0, 0x74009142, 0x420d1011, 0x00dd0037, 0x45500344, 0xdd887540, +0xd0017021, 0x0035400d, 0x03740811, 0x0f400d90, 0x00000620, 0x00000000, +0x00c78002, 0x1a5400d7, 0x33c409f0, 0x7c21d200, 0xc00d3003, 0x408f0015, +0x0d74034c, 0xdf0037c0, 0x7003fc00, 0x400dc203, 0x036c0253, 0x0bc00db0, +0x00000e20, 0x00000000, 0x000d8007, 0x069800df, 0x7fc00b20, 0xf8037f00, +0xc00df007, 0x02bf101f, 0x1f3083fc, 0xff203cc2, 0xf003fc20, 0x003bc00e, +0x038c015f, 0x1fc00f72, 0x00000600, 0x00000000, 0x00450002, 0x2a6c84d3, +0x75c049b1, 0x5c00df00, 0xd00d300a, 0x00930054, 0x2530039c, 0xdf1136c0, +0x7047cc00, 0x000fc103, 0x034c225f, 0x08c00d70, 0x00000420, 0x00000000, +0x4014a013, 0x064440f1, 0x76c06d50, 0x450e5d00, 0x400fb003, 0x20d14854, +0x050003c4, 0xdd003400, 0x100b441b, 0x0037405d, 0x03ec001d, 0x4c410fb0, +0x00000200, 0x00000000, 0x0012a007, 0x072402d1, 0xb0602800, 0x44400d01, +0x409c1003, 0x52810000, 0x0d100314, 0xcd00f050, 0x90091700, 0x04034020, +0x0304095d, 0x1c401c10, 0x00000a00, 0x00000000, 0x0a788004, 0x068401e1, +0x7a401a51, 0x8401ed10, 0x401e9006, 0x81e10258, 0x1e9007e4, 0xed807840, +0x18059481, 0x007b421e, 0x07a609ed, 0x10400e90, 0x00000200, 0x00000000, +0x00801012, 0x212c10c1, 0x31c0c830, 0x5c080f00, 0xc00c301b, 0x00830080, +0x0430031c, 0xcd043040, 0xb0011c00, 0x0003c000, 0x034c02cf, 0x48c08d70, +0x00000040, 0x00000000, 0x0029b802, 0x20fc00fe, 0x3fe08f70, 0xfc00ff00, +0xc00ff003, 0x00ef401f, 0x0772039c, 0xfd0035c0, 0xf001ac42, 0x003fc00f, +0x43fc00ff, 0x0bc04ff0, 0x00000660, 0x00000000, 0x0053a015, 0x035c88d3, +0xb38009f1, 0x7c009300, 0xc00df801, 0x00934053, 0x1db013fc, 0xc70037c0, +0x30034c0a, 0x600fc003, 0x834c00d3, 0x57c00df2, 0x00000e00, 0x00000000, +0x00398012, 0x03840cfb, 0x3b400ad0, 0xb4406102, 0x405ed003, 0x00e1081b, +0x0e814bf4, 0xed03bb40, 0x50030400, 0x003b400e, 0x138480e5, 0x4b402ed0, +0x00000620, 0x00000000, 0x04490003, 0x059625e1, 0x7b403a50, 0xb401a100, +0x449ed007, 0x01a1005f, 0x061013b4, 0xed007b60, 0x10079507, 0x00434012, +0x078401c1, 0x0f401e50, 0x00000400, 0x00000000, 0x00332812, 0x091420d9, +0x73441cd0, 0x74124942, 0x4a0cd010, 0x00c10413, 0x14100374, 0xcd003340, +0x501b1400, 0x0037400c, 0x030402d5, 0x4b400cd0, 0x00000c20, 0x00000000, +0x001da817, 0x0ddc0051, 0x13c01572, 0x74135300, 0xc005f80d, 0x005330df, +0x27b001fc, 0x2f040bc0, 0x10095c00, 0x0017c005, 0x014c0273, 0x5fc00570, +0x00000620, 0x00000000, 0x01070012, 0x3024001f, 0x87c021d0, 0x7c401700, +0xc001f020, 0x001f1207, 0x91f0007c, 0x1f0007c0, 0xf0002804, 0x000fc002, +0x007d101f, 0x4bc801f0, 0x00000c00, 0x00000000, 0x00270810, 0x024c0097, +0x75c08870, 0x4c008300, 0xc0293102, 0x019f0227, 0x0930026c, 0x134405c0, +0x30064c00, 0x0064d019, 0x024d0093, 0x43c00970, 0x00000c20, 0x00000000, +0x00262001, 0x2254029d, 0xe4405910, 0x6c0191c0, 0xc0191002, 0x818d0025, +0x09b00245, 0x1106c444, 0x10066c20, 0x02644019, 0x02440195, 0x074009b0, +0x00000800, 0x00000000, 0x0064a018, 0x0245109d, 0x256809d0, 0xc6049502, +0x40091002, 0x089d0027, 0x0c180244, 0x11800440, 0x10224406, 0x002c408b, +0x02040491, 0x63404950, 0x00000200, 0x00000000, 0x02202010, 0x0314088d, +0x30408810, 0xa400a100, 0x41481422, 0x008d0021, 0x88902204, 0x91022040, +0x10062488, 0x00a8402a, 0x0a040285, 0x43408890, 0x00000080, 0x00000000, +0x0586a01d, 0x5044021f, 0x05c161f1, 0xcc141510, 0x40413008, 0x005f0287, +0x2130084c, 0x110585c0, 0x34004c16, 0x000cc001, 0x804c0013, 0x77c02170, +0x00000ac0, 0x00000000, 0x092f9819, 0x02fc869f, 0x27c04bf2, 0x7c00bf00, +0xc009f012, 0x00bf006f, 0x4bf01a7c, 0xbf012f40, 0xf00a7c04, 0x00a7c029, +0x0a7c02bf, 0x67c069f0, 0x00000e60, 0x00000000, 0x050ba018, 0x02ec18ab, +0x2cc14b30, 0x4c14bf00, 0xc9083022, 0x00a30027, 0x2830025c, 0xa200ecc0, +0xf016fc15, 0x04e8c059, 0x1a4c44b3, 0x63c0cbb0, 0x00000e00, 0x00000000, +0x00c7081c, 0x00444211, 0x06c06110, 0x44021d00, 0x40211000, 0x00112103, +0x01101044, 0x11004440, 0xd1087407, 0x01854160, 0x00440015, 0x73400110, +0x00000c20, 0x00000000, 0x4023a010, 0x0f240481, 0x20410850, 0x24009d00, +0x40081416, 0x20814c23, 0x48024a14, 0x8900a540, 0xd10a3410, 0x01205028, +0x1a241689, 0x43406890, 0x00000e80, 0x00000000, 0x2125a818, 0x02440091, +0x26621954, 0x64009d10, 0x40091a02, 0x01910023, 0x09920244, 0x51040553, +0xd0827604, 0x10254009, 0x0264009d, 0x63400998, 0x00000620, 0x00000000, +0x0467a805, 0x0a6c0093, 0x24c01976, 0x6c819f08, 0xe2093102, 0x00930027, +0xa834025c, 0x1b4001c0, 0xf8027c12, 0x0024c209, 0x026c049b, 0x17c009b2, +0x00000e20, 0x00000000, 0x00458014, 0x025c0097, 0x27c00830, 0x5d438f02, +0xcc0df092, 0x109f0037, 0x1970025c, 0x1f0206ca, 0xf0027c01, 0x4027d00d, +0x025c0597, 0x53c00970, 0x00000600, 0x00000000, 0x40850814, 0x047c0003, +0x47c081f0, 0x4d011f00, 0xc001f000, 0x001b0004, 0x21b0006c, 0x194106c4, +0x30004c32, 0x4400c180, 0x004c0013, 0x53c001f0, 0x00000420, 0x00000000, +0x0048a014, 0x09840571, 0x1f4027d0, 0x44067d00, 0x4105d001, 0x05615054, +0x07100144, 0x31080c00, 0x5001ec80, 0x041c5015, 0x01441151, 0x534005d0, +0x00000200, 0x00000000, 0x0032a014, 0x013440c1, 0x33412cd0, 0x4401cd00, +0x401cd003, 0x03c90070, 0x0c980344, 0xc500f240, 0x9403240b, 0x0030400c, +0x034400d1, 0x534008d0, 0x00000a00, 0x00000000, 0x15388005, 0x018400e1, +0x3b420ed0, 0x8400ed24, 0x401ed203, 0x00f95438, 0x0f121304, 0xf501bc40, +0x9806a601, 0x00380006, 0x23840181, 0x17400ad0, 0x00000200, 0x00000000, +0x01f81011, 0x05bd01e3, 0x7bc01ef0, 0x8d01ef00, 0x409ef017, 0x01eb0078, +0x3eb10f8d, 0xe7157ac1, 0xb0072c41, 0x1068c01a, 0x078d00e3, 0x57e01ef2, +0x00000040, 0x00000000, 0x0035b010, 0x815c00df, 0x37c005f1, 0x7c00cf20, +0xd26df003, 0x00d701b7, 0x2d70075c, 0xd21133c0, 0x72017808, 0x0027d921, +0x037c009f, 0x43c00bf0, 0x00000660, 0x00000000, 0x007ba000, 0x07fd01fc, +0x7fc097b0, 0xcc09ff20, 0xc01f7047, 0x217300ff, 0x1f3007fc, 0xff0c7cc0, +0xb007fc0b, 0x226dc0db, 0x0fcc01ff, 0x03c01bf0, 0x00000e00, 0x00000000, +0x04398815, 0x03ac06fb, 0x3b4086b0, 0xc41ced10, 0x400e4003, 0x00e1022b, +0x4e1003f4, 0xe9243840, 0x1002b44c, 0x0308408e, 0x039400bd, 0x57400a7a, +0x00000620, 0x00000000, 0x00390000, 0x019400e1, 0x3b600610, 0x8600ed00, +0x400e100b, 0x00e1003b, 0x0e5003b4, 0xed003800, 0x1003b400, 0x0028414a, +0x038400ed, 0x03400ed0, 0x00000400, 0x00000000, 0x10732820, 0x092400d9, +0x53420090, 0x0402cd00, 0x42085007, 0x40c10263, 0x2c500334, 0xcd8c7040, +0x90013402, 0x00006008, 0x031442cd, 0x13400840, 0x00000c20, 0x00000000, +0x00f5a811, 0x2f5c00d3, 0x17400531, 0xcd01df00, 0xc00d7003, 0x00534057, +0x2e7403fc, 0xdf00f4c0, 0xb0037c18, 0x01f4c011, 0x03c41bcf, 0x57c009d0, +0x00000620, 0x00000000, 0x04370001, 0x0f3d025f, 0x17c035f0, 0x7c02df02, +0xc00de023, 0x085f2007, 0x0d92033c, 0xdb0037d0, 0x740b7c22, 0x0137d101, +0x037c809f, 0x07c00970, 0x00000c00, 0x00000000, 0x003b0880, 0x074d01f3, +0x1c800f30, 0xcc50fd00, 0xc00ef003, 0x0073001c, 0x0fb003c4, 0xf3407ec3, +0x3015fc20, 0x002fc053, 0x03cd01f3, 0x00d00af0, 0x00000c22, 0x00000000, +0x00362085, 0x036501d5, 0x95481550, 0x4402cd14, 0x400dd003, 0x02411091, +0x0d100344, 0xd104b040, 0x10023402, 0x00234121, 0x03440195, 0x04401990, +0x00000802, 0x00000000, 0x0074a001, 0x62650491, 0x5540a410, 0x4403dd80, +0x408dd003, 0x065100d4, 0x0d900354, 0xd1013640, 0x1c0b6408, 0x00274801, +0x034404d1, 0x044089d0, 0x00000200, 0x00000000, 0x00302014, 0x03240045, +0x11400450, 0x04204d20, 0x400cc003, 0x00510005, 0x0d100314, 0xc1083040, +0x10033640, 0x40034000, 0x03040091, 0x40400890, 0x00000088, 0x00000000, +0x0036b000, 0x036c0093, 0x15c00531, 0x4d00df00, 0xc00df003, 0x00130010, +0x0fb0035d, 0xd30036c0, 0x30017c00, 0x2027c000, 0x03cc0093, 0x00c009f9, +0x00000ac0, 0x00000000, 0x003fb805, 0x03dc00ff, 0x0f8007f0, 0xfc002f00, +0xc00ff003, 0x003f001f, 0x0ef003ec, 0xff003fc0, 0xf002bc00, 0x000fc603, +0x83fe00bf, 0x97c00bf0, 0x00000e60, 0x00000000, 0x003da003, 0x20cc002b, +0x3ec08330, 0xfc08f310, 0xc40f7053, 0x00f7023c, 0x8e3053cc, 0xf3023cc0, +0xf0034c08, 0x333cc00f, 0x73fc08fb, 0x0cd003b0, 0x00000e00, 0x00000000, +0x20330801, 0x18040011, 0xbcc88530, 0xf424f105, 0x48cd10cb, 0x56f1013c, +0x0d100bc4, 0xf10037c0, 0x92535424, 0x11bc480d, 0x0b7484d5, 0x04410110, +0x00000c20, 0x00000000, 0x40332011, 0x091414c1, 0x33620050, 0x1600c122, +0x410c1413, 0x08c42032, 0x5d110305, 0xc5113048, 0xd0072404, 0xd1b0401d, +0x033404c1, 0x44404190, 0x00000e80, 0x00000000, 0xa035a803, 0x00550011, +0x364465d0, 0x7600d120, 0x430d1003, 0x00d51036, 0x1d180344, 0xd508b740, +0x90077400, 0x0034401d, 0x037400dd, 0x0c400910, 0x00000620, 0x00000000, +0x00378802, 0x4e5c8013, 0x36403170, 0x5e40d340, 0xd03d3003, 0x60d70036, +0x0c34034c, 0xd74074c0, 0xf0036c00, 0x0034d00c, 0x037c00db, 0x08c011b0, +0x00000e20, 0x00000000, 0x003d8007, 0x24ac103f, 0x3dc01730, 0x7c00ff04, +0xc886b303, 0x00fb0035, 0x0ff003fc, 0xfb0a3ec0, 0xf003dd00, 0x0037c00f, +0x03bc00f2, 0x1fc033f0, 0x00000600, 0x00000000, 0x00350802, 0x035c0017, +0x37c00930, 0x2c01df20, 0xc02d3413, 0x00c34030, 0x0d30435c, 0xd300b5c0, +0xb0237c00, 0x4037c80d, 0x037c00db, 0x0bc001f0, 0x00000420, 0x00000000, +0x00348013, 0x004406d9, 0xfe80bd71, 0xc500fd00, 0x4005000f, 0x08f3403c, +0x0d108b84, 0xfb083440, 0xd0877420, 0x003f40dd, 0x03f400f1, 0x4f4008d0, +0x00000200, 0x00000000, 0x0036a007, 0x40250019, 0xf404a890, 0x1400c900, +0x400c1003, 0x00c90033, 0x0c900716, 0xd1003640, 0xd0873400, 0x0037400c, +0x033400c1, 0x1f4000d0, 0x00000a00, 0x00000000, 0x02788004, 0x07a50139, +0x7a441b50, 0x8409cd24, 0x401c5247, 0x05c1027a, 0x1e9067c4, 0xe9027f50, +0xc087b401, 0x027b421e, 0x07b401e1, 0x134012d0, 0x00000200, 0x00000000, +0x00301012, 0x013d00c3, 0x31c28990, 0x1e00cf00, 0xd00c1023, 0x40cb0033, +0x0cb0031c, 0xc30232c0, 0xb0073c00, 0x0033c20c, 0x033c00c3, 0x4bc004f0, +0x00000040, 0x00000000, 0x483db802, 0x23dc8837, 0x3fc889f0, 0x5c00ff06, +0xc08fb043, 0x24ff003d, 0x0d70033c, 0xdf0230c8, 0xf00b7808, 0x003fc00f, +0x23fc10f7, 0x0bc08ff0, 0x00000660, 0x00000000, 0x0037a015, 0x064c0003, +0x77c20530, 0x4d04db04, 0xc001f01b, 0x12db0134, 0x4d30730c, 0xd20134c0, +0x320f4c04, 0x0034807d, 0x034c00d3, 0x54c001f0, 0x00000e00, 0x00000000, +0x003d8812, 0x03050021, 0x3b020610, 0x8412f100, 0x500ad053, 0x04e103bc, +0x2e100394, 0xe144b841, 0xb01b8510, 0x0332c0ce, 0x039400e1, 0x4bc003d0, +0x00000620, 0x00000000, 0x00790003, 0x87840131, 0x7b421e58, 0xa405e101, +0x6400d007, 0x09e98878, 0x5e9407e4, 0xc1297b40, 0x9887c4ad, 0x0378409e, +0x27b405c1, 0x0c4016d0, 0x00000400, 0x00000000, 0x10332812, 0x030433c1, +0x33412c58, 0x2400c110, 0x4109d203, 0x00cd8030, 0x0d900334, 0xc101b340, +0x98030400, 0x0032400c, 0x033400c1, 0x4b400dd0, 0x00000c20, 0x00000000, +0x0015a817, 0xa9cd0763, 0x17407774, 0x6c005b40, 0xd237d001, 0x007b0014, +0x03b001ec, 0x6301cfc0, 0xb000cd00, 0x0014c003, 0x017c4053, 0x5cc007f0, +0x00000620, 0x00000000, 0x60070012, 0x007c001f, 0x87c001b3, 0x5c000f00, +0xc881f000, 0x00111003, 0x1170001c, 0x1f000450, 0xf0847e00, 0x4003c011, +0x0050001f, 0x4ac001f0, 0x00000c00, 0x00000000, 0x00270810, 0x064d0193, +0x27c809f0, 0x6c009f00, 0xc009f006, 0x43830026, 0x0134024c, 0x931007c0, +0xf0004c20, 0x0027c001, 0x824c0083, 0x40c409f3, 0x00000c20, 0x00000000, +0x00262001, 0x06444191, 0xa74009d0, 0x44029d00, 0x4009d026, 0x01918024, +0x01102e54, 0x9b000040, 0x30004400, 0x00274001, 0x02540091, 0x050009d0, +0x00000800, 0x00000000, 0x0020a018, 0x12454499, 0xa7420950, 0x66109d00, +0x4009d002, 0x00916026, 0x01100244, 0x99080660, 0x50005400, 0x00274011, +0x02460091, 0x604009d0, 0x00000200, 0x00000000, 0x02202010, 0x22040899, +0x234088d0, 0x040c8d02, 0x41c8d002, 0x0c818120, 0x88102216, 0x99022440, +0x18220404, 0x02234098, 0x32140881, 0x414088d0, 0x00000080, 0x00000000, +0x0086b01d, 0xd84c021b, 0x83c36172, 0x6c031f05, 0xc061d050, 0x030102c6, +0x6130594c, 0x1b0586c5, 0x60585c0b, 0x4587c161, 0x0c4c0213, 0x74c021f0, +0x00000ac0, 0x00000000, 0x6127b819, 0x12fc04b7, 0x27c04bf0, 0x7c0c9f01, +0xc04bf052, 0x2c9f0a67, 0x4af0127c, 0x9f012ad0, 0x3012bd09, 0x0127c04a, +0x327c069f, 0x67c04bf0, 0x00000e60, 0x00000000, 0x00a7a018, 0x0acc02a3, +0x2cc04bf0, 0xcc5cbf03, 0xc0c9f00a, 0x0cb70326, 0x9d3012e8, 0x9101e4c1, +0x3117cc14, 0x01a4c07f, 0x524c0893, 0x634008f0, 0x00000680, 0x00000000, +0x4003001c, 0x08448011, 0x0451e1d0, 0x04420d01, 0x4060d088, 0x46110184, +0x20100c44, 0x11010440, 0x50004c06, 0x01004051, 0x10440811, 0x734041d0, +0x00000c20, 0x00000000, 0x0123a010, 0x1a1444c1, 0xa04008d0, 0x04048d00, +0x5088d01a, 0x028500a0, 0x48105b24, 0x8100e040, 0x105a2410, 0x41a0407d, +0x42040481, 0x434118d0, 0x00000c80, 0x00000000, 0x4021a818, 0x02540181, +0x246008d0, 0x44009d00, 0x4009d002, 0x20910024, 0x28100044, 0x91006040, +0x501a0400, 0x00244011, 0x02440091, 0x634009d0, 0x00000620, 0x00000000, +0x0027a805, 0x065d0693, 0x24c049f0, 0x4d009f00, 0xd009f002, 0x00970024, +0x0934002c, 0x93402490, 0x30026d00, 0x0024d001, 0x02450093, 0x17c019f0, +0x00000e20, 0x00000000, 0x00258014, 0x226c009f, 0x27c009f0, 0x7c008f08, +0xc009f042, 0x008f0021, 0x99f0007c, 0x8f0027c4, 0xf0027c20, 0x0023c001, +0x827c009f, 0x53c039f1, 0x00000600, 0x00000000, 0x00050814, 0x104c0313, +0x04c121f0, 0x4c041f02, 0xc0003000, 0x081f0004, 0x2132044c, 0x1f0004c0, +0xf1104c00, 0x4005c011, 0x004c0013, 0x50c001f0, 0x00000420, 0x00000000, +0x4014a014, 0x19c40071, 0x5c4007d0, 0xc4007d00, 0x4005100d, 0x03790015, +0x071000c4, 0x5d001c40, 0xd201c500, 0x00174002, 0x01540051, 0x504005d0, +0x00000200, 0x00000000, 0x0032a014, 0x070500c1, 0x32402cd0, 0x0400cd00, +0x400c100f, 0x10cd0030, 0x0c100304, 0xcd003340, 0xd00b0500, 0x0031400c, +0x030400d1, 0x50400cd0, 0x00000a00, 0x00000000, 0x21388805, 0x038480b1, +0x3a400cd0, 0x8402ad00, 0x401e100f, 0x00fd0139, 0x4e101304, 0xed013b40, +0x90038400, 0x013b404e, 0x039404c1, 0x14409fd0, 0x00000200, 0x00000000, +0x45780015, 0x078c01e3, 0x5ad01ef0, 0x8d01ef10, 0xc04e3007, 0x01ef08f8, +0x5e34178c, 0xcf02fbd0, 0xd0078c05, 0x00f9c01e, 0x178c05e3, 0x54d05ef0, +0x00000040, 0x00000000, 0x03b5a810, 0x837d009f, 0x35c00df0, 0x7c009f00, +0xc02df483, 0x00cb0037, 0xedf42b7d, 0xdf0034c0, 0xf0037c68, 0x0037c14d, +0x277c08df, 0x43c01df0, 0x00000660, 0x00000000, 0x407fa000, 0x04cc01e3, +0x6cd01730, 0xdc01f300, 0xc5bd3005, 0x01e3007c, 0x3f300fcc, 0xf3007cc0, +0x3027dc01, 0x00ffc11f, 0x13fc01f3, 0x03c01f30, 0x00000e00, 0x00000000, +0x003d8815, 0x00ecca61, 0x38c18710, 0xc400e100, 0x401f1001, 0x00610238, +0x0e100384, 0xf1063850, 0xf023ec00, 0x023bc10e, 0x03fc00e1, 0x57400eb0, +0x00000620, 0x00000000, 0x00390000, 0x009410b1, 0x02400610, 0x94086100, +0x400e9001, 0x00b10030, 0x0e100304, 0xe1003a40, 0x1803b400, 0x003b400e, +0x03b400e1, 0x03400e10, 0x00000400, 0x00000000, 0x40372804, 0x0c340301, +0x30400414, 0x24004120, 0x400c9001, 0x00110034, 0x8c100304, 0xc100b240, +0x500f2480, 0x8035403c, 0x031400d1, 0x13412d10, 0x00000c20, 0x00000000, +0x003da815, 0x475c0153, 0x36400d30, 0x5c005300, 0xd00fb403, 0x00d3403c, +0x0e34030c, 0xf3407ed0, 0x10273c00, 0x403f402c, 0x03f400f3, 0x57c02f10, +0x00000620, 0x00000000, 0x00370001, 0x096c121f, 0x07c020f0, 0x5c001f20, +0xc10d7081, 0x00df0037, 0x0df0037d, 0xcf0435c3, 0xf0227c00, 0x0037c00d, +0x037c00df, 0x07c80df0, 0x00000c00, 0x00000000, 0x003f0880, 0x46cc0073, +0x58c00ff0, 0xcc004300, 0xc00ef007, 0x00f6003c, 0x0f3407cc, 0xf3003cc0, +0x300fcd00, 0x003cc10f, 0x03cc00f3, 0x00c00ff0, 0x00000c22, 0x00000000, +0x00362081, 0x60556001, 0xc44191d2, 0x450a1102, 0x420d7024, 0x00db8834, +0x0d104f6c, 0xd1083140, 0x311e4420, 0x0035400d, 0x035400db, 0x05400dd0, +0x00000802, 0x00000000, 0x0034a001, 0x034440d1, 0x24400dd0, 0x40029104, +0x400dd003, 0x00d12034, 0x0d161344, 0xd1003444, 0x10014620, 0x0034401d, +0x034400d5, 0x04400dd0, 0x00000200, 0x00000000, 0x00302010, 0x01140051, +0x004000d0, 0x04408120, 0x510c5085, 0x00490030, 0x0d100364, 0xc1003160, +0x10804400, 0x0030401c, 0x030400c9, 0x41000cd0, 0x00000080, 0x00000000, +0x003eb000, 0x024c0091, 0x04400dd0, 0x4c001300, 0xc05ff003, 0x00910034, +0x0f10034c, 0xd34038c0, 0x31294c00, 0x2038d00d, 0x034d00d7, 0x00c40df0, +0x00000ac0, 0x00000000, 0x403fb805, 0x00fc003f, 0x0fc003f0, 0xfc003f00, +0xc20ff081, 0x003b003f, 0x0ef003fc, 0xff003f80, 0x7004fc00, 0x003fc00f, +0x03fc00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x030b8003, 0x10cc0433, +0x1fc00a38, 0xfc043301, 0xc00f3030, 0x0473000b, 0x0f3000cc, 0x33400ec0, +0x3003bc80, 0x001fc28f, 0x02ec007f, 0x0ec00fb0, 0x00000e00, 0x00000000, +0x02870801, 0xab6c0ad1, 0x97400d18, 0x34b8d104, 0x400d501b, 0x02d10087, +0xad100144, 0x91243440, 0x1003f400, 0x0017404f, 0x024428dd, 0x04400c10, +0x00000c20, 0x00000000, 0x8103a011, 0x00040001, 0x12400810, 0x34040103, +0x400c1020, 0x08c10203, 0x0c500204, 0x45001640, 0x50033400, 0x0013404c, +0x0224c01d, 0x47400c90, 0x00000e80, 0x00000000, 0x0005a803, 0x036400d1, +0x17420d10, 0x7400d500, 0x000d5003, 0x00d14047, 0x0d514304, 0x95003440, +0x50037428, 0x0117400d, 0x0644301d, 0x0d400d90, 0x00000620, 0x00000000, +0x402fa802, 0x008c0033, 0x1ec09510, 0xfc003301, 0x400d3400, 0x0b530147, +0x0d740e4c, 0x170086c0, 0x74037c03, 0x0407c00d, 0x4e6c014f, 0x0bc00db0, +0x00000e20, 0x00000000, 0x002d8007, 0x03fc10ff, 0x1fc10ff0, 0xfc00fb28, +0xc00ff003, 0x004f000b, 0x0cb017fc, 0xbb083fc0, 0xb043fc00, 0x043fc00f, +0x02bc087f, 0x1ec00e70, 0x00000600, 0x00000000, 0x01610802, 0x005c0013, +0x10c61d32, 0x1c001300, 0xc00d3020, 0x00530024, 0x0d340a4c, 0x570014d0, +0x30030c08, 0x0007c00d, 0x0865005f, 0x09c00d34, 0x00000420, 0x00000000, +0x00a4a013, 0x474420d1, 0x14403d12, 0x6c0ad100, 0x400f100b, 0x005104a4, +0x2f112f44, 0x91017041, 0xb003c001, 0x0037400f, 0x2c540bdd, 0x4c40af10, +0x00000200, 0x00000000, 0x0002a007, 0x48340019, 0x30402c99, 0x06000000, +0x409c5044, 0x20c10121, 0x1c100305, 0x0449c041, 0x000b0406, 0x0213400c, +0x0204004d, 0x0d408c10, 0x00000a00, 0x00000000, 0x024c8004, 0x87a401e9, +0x70481e14, 0xa611c100, 0x401e1007, 0x21a10269, 0x1e107784, 0x61005c40, +0x90878405, 0x025b401e, 0x468419ed, 0x00401e10, 0x00000200, 0x00000000, +0x00001012, 0x003c041b, 0x30c00cb1, 0x1c000300, 0xc00c7000, 0x00c10021, +0x4c30334c, 0x471810c0, 0x30030c00, 0x1213e80c, 0x024c001f, 0x59c00c30, +0x00000040, 0x00000000, 0x080db802, 0x239c00f7, 0x3f800f72, 0xfc80ff00, +0xd10ef003, 0x00bf082e, 0x0ff033fc, 0x6f021bc0, 0xf003fd04, 0x021fc10f, +0x02dc20bf, 0x0bc18ff0, 0x00000660, 0x00000000, 0x1027aa15, 0x006d0013, +0x37c405b4, 0x4c001300, 0xc00db000, 0x005f4037, 0x0d32034c, 0x930004c0, +0xf0136c01, 0x0024c07d, 0x024d2013, 0x54c00d34, 0x00000e00, 0x00000000, +0x002d8812, 0x038400e1, 0x3b420e14, 0x9500e100, 0x400e5403, 0x80210038, +0x5e1003c4, 0xe1003850, 0xd04b8540, 0x002ec14f, 0x038400a1, 0x48404f10, +0x00000620, 0x00000000, 0x40690003, 0x04250101, 0x7b601e14, 0x84012100, +0x405c9104, 0x01650061, 0xde900784, 0xf1005862, 0xd017a401, 0x00f8401e, +0x05040101, 0x8e401e10, 0x00000400, 0x00000000, 0x00232812, 0x030600c1, +0x33681c14, 0x1400c140, 0x400c5403, 0x200104b0, 0x0d900305, 0xc1003460, +0xd0032400, 0x0072400c, 0x07040081, 0x4a420c10, 0x00000c20, 0x00000000, +0x0095a017, 0x816c8053, 0x17c02734, 0x4c405300, 0xc005b001, 0x0275009d, +0x05b409cc, 0x73001cd0, 0xf0016e28, 0x40d8c005, 0x49cc0373, 0x5ed00530, +0x00000620, 0x00000000, 0x04030012, 0x00bc002f, 0x03c18174, 0xfc003f00, +0xc001f000, 0x01170087, 0x0170407c, 0x1f4107c0, 0xe0005c00, 0x0047c001, +0x407c091f, 0x49c801f0, 0x00000400, 0x00000000, 0x00270010, 0x0e5c8997, +0x27c00930, 0x2d009340, 0xc0083002, 0x00930062, 0x0930025d, 0x932824c0, +0xf0027c41, 0x0027c009, 0x024c0097, 0x40c00930, 0x00000c20, 0x00000000, +0x50662001, 0x06440791, 0xa740a910, 0x45009120, 0x4019b002, 0x828142e4, +0x09140244, 0x9100a440, 0xd00274c1, 0x00274009, 0x02450091, 0x04500914, +0x00000080, 0x00000000, 0x0224a018, 0x42d400b5, 0x27600910, 0xc420a144, +0x40891002, 0x10d18426, 0x09500214, 0xd101b540, 0xd0027448, 0x00274009, +0x02040085, 0x60400810, 0x00000200, 0x00000000, 0x12202010, 0x028408a1, +0x23601810, 0x8408a102, 0x40089022, 0x04910120, 0x08d00204, 0xc1022150, +0xc0023008, 0x00274028, 0x42040881, 0x40404810, 0x00000080, 0x00000000, +0x0584301d, 0x50dc9615, 0x83c40130, 0xac960305, 0xc1413058, 0x0b030046, +0xe070285c, 0x134585c1, 0xf0517c02, 0x0287c541, 0x004c5015, 0x74c0d130, +0x00000ac0, 0x00000000, 0x212fb019, 0x027c849f, 0x27400be4, 0x7c049f41, +0xc009f012, 0x09bf822f, 0x193106fc, 0xaf212ac0, 0xf0027e04, 0x012fc009, +0x42fc00bf, 0x67c0c9f0, 0x00000e60, 0x00000000, 0x056f8018, 0x06cc09af, +0x27c00b30, 0x4e029306, 0xc04bb41a, 0x0093053f, 0x69300a4c, 0xb3052fc0, +0xf052fc02, 0x0224c129, 0x0a7c069f, 0x63c02930, 0x00000e00, 0x00000000, +0x0487001c, 0x8044201d, 0x43400130, 0xfd0b1100, 0x4081100c, 0x15010084, +0x52140444, 0x11410740, 0xd0087401, 0x01004071, 0x08740e1d, 0x7340a3b1, +0x00000c20, 0x00000000, 0x0023a010, 0x0a0502cd, 0x23400810, 0x8500a101, +0x400814a2, 0x08810061, 0xca1002c4, 0x85452340, 0xd0023600, 0x00284048, +0x1ab404ad, 0x43404a10, 0x00000e80, 0x00000000, 0x1025a818, 0x024480dd, +0x27402910, 0xf580b100, 0x40091002, 0x20818824, 0x0b1042c5, 0x95002748, +0xd2827480, 0x02244009, 0x0af408bd, 0x63400b90, 0x00000620, 0x00000000, +0x0827a805, 0x024d009f, 0x27c80934, 0x44009340, 0xc009b002, 0xa49340e5, +0x0930820c, 0x170027c0, 0xf1027640, 0x0024d009, 0x2e7c139f, 0x17c00930, +0x00000e20, 0x00000000, 0x00258214, 0x027c009f, 0x27c20970, 0x1d008f00, +0xc008f402, 0x809f0121, 0x09f0027c, 0x1b08a3c8, 0xf0027c02, 0x0027c009, +0x063c009f, 0x53c409f0, 0x00000600, 0x00000000, 0x01050814, 0x002c8003, +0x03c01134, 0xfc003700, 0xc001b000, 0x02130306, 0x033408cc, 0x132004c0, +0x74047c98, 0x000cc000, 0x00fc0237, 0x53c003f0, 0x00000420, 0x00000000, +0x0c1ca014, 0x01c50871, 0x17002710, 0x4d005100, 0x40271001, 0x0053205c, +0x05100144, 0x30201c40, 0x100dd501, 0x00144005, 0x01740051, 0x534005d0, +0x00000200, 0x00000000, 0x00f2a034, 0x212400c1, 0x33001c1a, 0x1580c500, +0x41081483, 0x00c100a2, 0x0c500304, 0xc1027040, 0x50071420, 0x0030400c, +0x033400c5, 0x53400cd0, 0x00000a00, 0x00000000, 0x00788005, 0x03840061, +0x3b402218, 0xa4002180, 0x400c1000, 0x00e10020, 0x125000c4, 0xf1000c40, +0x10c58005, 0x0048404e, 0x00b42021, 0x174002d0, 0x00000200, 0x00000000, +0x40600015, 0x072c81e3, 0x5bc01614, 0x1d01f720, 0xc01a3007, 0x0163086a, +0x1e72078d, 0xe32058d0, 0x74079c81, 0x0078d05f, 0x07bc01e7, 0x57c01ef2, +0x00000040, 0x00000000, 0x0035b810, 0x027c005f, 0x17c001e1, 0x5d001f00, +0xc2097000, 0x005f4827, 0x01b0003c, 0x4f6013c0, 0xf0011c40, 0x4007c09d, +0x007c001f, 0x43c001f0, 0x00000660, 0x00000000, 0x406fa000, 0x05cc01b3, +0x7fc49772, 0xec01f300, 0xc01b3007, 0x21ff006c, 0x1f3007cc, 0x73406cc0, +0x3026ec91, 0x087c881f, 0x07fc01ff, 0x03c01fe0, 0x00000e00, 0x00000000, +0x003d8815, 0x1b848821, 0x3b402210, 0x84082141, 0x400e1220, 0x00ed0029, +0x031000c4, 0xe1000850, 0x10308404, 0x0008400e, 0x40b4082d, 0x574002d0, +0x00000620, 0x00000000, 0x00290000, 0x028400a1, 0x1b480612, 0xa400e180, +0x400a1003, 0x106d0228, 0x0e904384, 0x61001a40, 0x1080a500, 0x0038400c, +0x03b400ed, 0x03400ed0, 0x00000400, 0x00000000, 0x10332804, 0x0a040001, +0x1348201a, 0x450a0160, 0x40081028, 0x474d0021, 0x01900424, 0x41205248, +0x10000405, 0x03c4400c, 0x4c34100d, 0x134000c0, 0x00000c20, 0x00000000, +0x0035a815, 0x17452553, 0x37c04130, 0x6c00d300, 0x400d308b, 0x03cf0064, +0x0db0074d, 0xc30152c0, 0x34006c07, 0x0034c00f, 0x0b7c80cd, 0x57400de0, +0x00000620, 0x00000000, 0x00370001, 0x433c324f, 0x37c021f0, 0x7c100f00, +0xc00df010, 0x00df00e7, 0x0170005c, 0xdf0415c0, 0xf0083c80, 0x0007c00d, +0x007c801f, 0x07c001f0, 0x00000c00, 0x00000000, 0x403f0880, 0x03cc0097, +0x1fc10370, 0x4400f310, 0xc09f7003, 0x005b003b, 0x0f3003cd, 0xf70144c0, +0xb0024c00, 0x003cc00e, 0x038c00fb, 0x83c00f30, 0x00000c22, 0x00000000, +0x41f22081, 0x0a442311, 0x1f40b112, 0xc4003160, 0x411d1480, 0x007100b7, +0x031000c4, 0x5b21c448, 0x112c4c01, 0x0004440f, 0x00548011, 0x07400110, +0x00000802, 0x00000000, 0x4064a001, 0x03440195, 0x33400550, 0x5500d900, +0x40055103, 0x20d10627, 0x0d180304, 0x55000440, 0x10094408, 0x4834400d, +0x034400d1, 0x07400d10, 0x00000200, 0x00000000, 0x00102010, 0x03040001, +0x33400014, 0x15000900, 0x40041000, 0x00d00003, 0x00100004, 0xc9000440, +0x10012500, 0x0000400c, 0x00160011, 0x43400010, 0x00000080, 0x00000000, +0x0026b000, 0x024c0097, 0x13c00170, 0x5c80cb00, 0x40055403, 0x00530037, +0x0d30034c, 0x574004d0, 0xb4014e20, 0x8034c80d, 0x034c00d3, 0x03e00d32, +0x00000ac0, 0x00000000, 0x101fb805, 0x82bc003f, 0x1fc003f0, 0xed003608, +0xc006f000, 0x007f602f, 0x03f000fc, 0x6f000fc0, 0xf001dc00, 0x000fc00f, +0x00fc0037, 0x17c003f0, 0x00000e60, 0x00000000, 0x023fa003, 0x20ec023b, +0x3bc023f0, 0x8c0c3700, 0xc00f3022, 0x0073012f, 0x4fd000cd, 0xf3401fc0, +0xb003ec0c, 0x083cc88f, 0x02cc0073, 0x0cc00ff0, 0x00000e00, 0x00000000, +0x21370801, 0x1844025d, 0x3748a5d0, 0x444edb00, 0x480d5009, 0x121b04a7, +0x8fd0024c, 0xd100b440, 0x50abc488, 0x2475405e, 0x564515d1, 0x04400cd0, +0x00000c20, 0x00000000, 0x1033a011, 0x1804460c, 0x17404850, 0x24000500, +0x414c1009, 0x26458323, 0x0cd02330, 0xc1021344, 0x90032484, 0x0131504c, +0x022400c9, 0x44400cd0, 0x00000e80, 0x00000000, 0x0835a803, 0x0044104d, +0x174005d0, 0x6400c901, 0x402d5081, 0x00190063, 0x0dd10344, 0xc0011448, +0xd0034400, 0x0131400d, 0x426480c9, 0x0c400dd2, 0x00000620, 0x00000000, +0x0037a802, 0x164c029b, 0x478025f0, 0x6d071700, 0x80a93001, 0x88c30047, +0x0df1016c, 0xd30153c2, 0xb0036c00, 0x4035c40d, 0x086404db, 0x08d00df0, +0x00000e20, 0x00000000, 0x003d8007, 0x09dd40bf, 0x4fc005f0, 0xdc04df00, +0xc01fd003, 0x411f400f, 0x0eb103d0, 0xdf080fc0, 0x7003fc00, 0x007fc10f, +0x02dc00f7, 0x1fc00ef0, 0x00000600, 0x00000000, 0x00350802, 0x025c025f, +0x24d0adb0, 0x5c0c1300, 0xc00d3003, 0x06ff0027, 0x0df0a34c, 0xd300d4c9, +0x70030c20, 0x0034c00c, 0x084c00df, 0x08800d32, 0x00000420, 0x00000000, +0x0134a013, 0x0174025d, 0x2040a511, 0x4401d500, 0x444d1083, 0x059903a7, +0x5fd08f4c, 0xfb2806c0, 0x1003ec00, 0x0134405f, 0x3a4400dd, 0x4c400f10, +0x00000200, 0x00000000, 0x0072a007, 0x0134500d, 0x21401012, 0x64010180, +0x400c9102, 0x02cd2822, 0x2cd04c34, 0xc5040140, 0x50030400, 0x04f0401c, +0x060407cd, 0x1e400c10, 0x00000a00, 0x00000000, 0x00788004, 0x07b4056d, +0x6d401e10, 0xe4012500, 0x401e9005, 0x496d007b, 0x9cc00684, 0xed206f40, +0x1107a401, 0x0078101e, 0x06c411ed, 0x12401e11, 0x00000200, 0x00000000, +0x02301012, 0x015c008f, 0x0140a832, 0x35000310, 0x40048401, 0x22cf0022, +0x0cf0233c, 0xc70001c0, 0x70034c0c, 0x0030c00c, 0x100d00df, 0x4ac04c30, +0x00000040, 0x00000000, 0x003db802, 0x23fc24ff, 0x0ee20f70, 0xdc003f00, +0xc00f7301, 0x00fb003f, 0x0ff003fc, 0xfa000ac0, 0xf003fc38, 0x003fd00f, +0x00bc00ff, 0x09c00ff0, 0x00000660, 0x00000000, 0x0037a015, 0x064c219f, +0x15c00530, 0x4c00df00, 0xc00df201, 0x00530005, 0x0de0014d, 0xdf2014c0, +0xb0337c40, 0x64b7c14d, 0x487c12db, 0x54c00d30, 0x00000e00, 0x00000000, +0x00398812, 0x030640ad, 0x1b400e30, 0xb400ed00, 0x400ed003, 0x0001001f, +0x6ed00384, 0xed003ac1, 0xb043b404, 0x803f400e, 0x02b400e1, 0x48403e10, +0x00000620, 0x00000000, 0x80790003, 0x068401cd, 0x7b681e98, 0xb401e510, +0x601ed007, 0x0161407b, 0x1ed00785, 0xed005840, 0x90073405, 0x007b409e, +0x04b641e1, 0x0c405e90, 0x00000400, 0x00000000, 0x00332812, 0x130610cd, +0x33609c10, 0x348acd00, 0x401dd00b, 0x07011023, 0x0cd02704, 0xcd008240, +0x90037400, 0x0227400d, 0x083412c9, 0x48400c90, 0x00000c20, 0x00000000, +0x0015a817, 0x05cd057f, 0x1bc007b4, 0xec017f00, 0xc067f005, 0x1373001f, +0x07d005cc, 0x5f0098c0, 0xb0017c00, 0x2057c007, 0x017c0253, 0x5cd005b4, +0x00000620, 0x00000000, 0x00070012, 0x207c001f, 0x47c00170, 0x7c901f00, +0xc011f020, 0x000f0287, 0x00f0007c, 0x1f0407c8, 0xf0007c00, 0x0087c001, +0x187c0217, 0x4bc00170, 0x00000c00, 0x00000000, 0x01270810, 0x067c029f, +0x27c009f0, 0x7c009300, 0xc0893442, 0x019304e4, 0x09b0020c, 0x9300e5c0, +0x34027c00, 0x0036c009, 0x074c08d3, 0x40c00930, 0x00000c20, 0x00000000, +0x00a62001, 0x3274039d, 0x274029d2, 0x3c029500, 0x40191002, 0x01914024, +0x0910026c, 0x95006440, 0x10827400, 0x01654029, 0x26440091, 0x06c00914, +0x00000800, 0x00000000, 0x0424a018, 0x02540299, 0x26402990, 0x74029180, +0x40091002, 0x08910020, 0x19901a44, 0x91002540, 0x10027400, 0x91265009, +0x02050081, 0x60400818, 0x00000200, 0x00000000, 0x02202010, 0x2234088d, +0x274088d8, 0x74088580, 0x50081052, 0x08c50230, 0xd8102224, 0x85006440, +0x10123604, 0x80a1502c, 0x0a040281, 0x42416810, 0x00000080, 0x00000000, +0x0086b01d, 0x585c161f, 0x074161f0, 0x7c161300, 0xc0013010, 0x16130584, +0x31b0584d, 0x130505c0, 0x302c740b, 0x0006c141, 0x010c0053, 0x74d00032, +0x00000ac0, 0x00000000, 0x01279819, 0x12fc04bf, 0x2fc04ff0, 0xdc24bf00, +0xc14bf053, 0x04bb012f, 0xc8f012dc, 0x9f282fc0, 0xf0267c09, 0x4026c009, +0x027c009f, 0x67c009f0, 0x00000e60, 0x00000000, 0x822fa018, 0x8afc02b3, +0x30c00b30, 0x4c22b300, 0xc00bf01a, 0x00b300ac, 0x0bd032dc, 0x9f0834c0, +0x300a6c02, 0x0024c049, 0x024c0093, 0x60d049f0, 0x00000e00, 0x00000000, +0x0107081c, 0x08740e1b, 0x06514110, 0x490e1b00, 0x4141f008, 0x0e1b2084, +0xb1d02c44, 0x1d108540, 0x12084402, 0x40004001, 0x01c40011, 0x714000d0, +0x00000c20, 0x00000000, 0x0023a010, 0x1a340481, 0x266028d2, 0x24008528, +0x601cd932, 0x108121a0, 0x48d01216, 0x8d00a241, 0x901a2406, 0x0828502a, +0x02a400a1, 0x424028d0, 0x00000e80, 0x00000000, 0x0021a818, 0x02740299, +0x264009d1, 0x44009d00, 0x4049d012, 0x00990034, 0x01d00444, 0x9d002740, +0x90020400, 0x002c4008, 0x02e402b1, 0x634009d0, 0x00000620, 0x00000000, +0x0027a805, 0x027c0393, 0xa6c029f4, 0x64009701, 0xd009f90e, 0x109344e4, +0x01f0445c, 0x9f0466c0, 0xb4026c20, 0x0024c009, 0x0a6d0093, 0x16d009f2, +0x00000e20, 0x00000000, 0x00258014, 0x023c449f, 0x25c00930, 0x7c009b00, +0xc0097006, 0x009f0127, 0x00f0007c, 0x9f00b5c0, 0x70027c40, 0x0267c109, +0x025c109f, 0x51c009f0, 0x00000600, 0x00000000, 0x01050814, 0x844c8217, +0x07c121f0, 0x7c001300, 0xc0013008, 0x12130004, 0x01f0c04e, 0x130884c9, +0x30007c00, 0x1004c001, 0x084c0013, 0x50c00134, 0x00000420, 0x00000000, +0x085ca014, 0x01c4047d, 0x134037d1, 0x74407400, 0x40051001, 0x107106dc, +0x13d00ccd, 0x5f011540, 0xf081f400, 0x0296c005, 0x01449251, 0x50400510, +0x00000200, 0x00000000, 0x0232a014, 0x230401cd, 0x33600d90, 0x3400d010, +0x400d9003, 0x021140c6, 0x3cd02726, 0xd5001040, 0x91033480, 0x000c4012, +0x3c840331, 0x50400d10, 0x00000a00, 0x00000000, 0x08288005, 0x038441ed, +0x3f430ad0, 0xb400e500, 0x400b9817, 0x0061083a, 0x4ed01384, 0xedc05d49, +0x9007b408, 0x042a4900, 0x06840031, 0x14404e10, 0x00000200, 0x00000000, +0x00781015, 0x078d01c7, 0x7b4016f1, 0xfc016300, 0xd01e9417, 0x0141004e, +0xdef05fa4, 0xe70268c0, 0xb403fc0d, 0x0058c012, 0x24cc2163, 0x54d0be32, +0x00000040, 0x00000000, 0x0025b810, 0x037c005f, 0x37c009f0, 0x7c00df00, +0xc00c7027, 0x005f0035, 0x0cf1037c, 0xdb0003c0, 0x705b7c02, 0x4017c001, +0x1b7d008c, 0x43c00df0, 0x00000660, 0x00000000, 0x007fa000, 0x25fc01bf, +0x78c01ff2, 0xce01f720, 0xc01f7007, 0x01b7205c, 0x1ec007c9, 0xf30048c1, +0xb047cc01, 0x0848c016, 0x85cc01b3, 0x00c01f34, 0x00000e00, 0x00000000, +0x02398815, 0x01b440a7, 0x384086d0, 0x8c0aeb00, 0xc00b0013, 0x04ed0218, +0x4ed00384, 0xff001ac0, 0x12239400, 0x0098400a, 0x00cc0861, 0x54400e10, +0x00000620, 0x00000000, 0x00090000, 0x40b400ad, 0x3c4006d8, 0x24004d00, +0x400e5143, 0x202d000a, 0x0fd00b84, 0xe1002c40, 0x50038400, 0x400c400f, +0x038400e1, 0x00400e10, 0x00000400, 0x00000000, 0x00072804, 0x10360815, +0x304004d8, 0x0408c940, 0x402d1043, 0x2b4d0070, 0x0cd00304, 0xcd04c040, +0x10031420, 0x00304000, 0x0b0400d1, 0x10400c10, 0x00000c20, 0x00000000, +0x0015a815, 0x0e7c015f, 0x34d07df0, 0xcc01df00, 0xd02d7007, 0x009f2466, +0x0cf08b4c, 0xf3449450, 0x7203cc80, 0x0144c008, 0x08450213, 0x54d00f30, +0x00000620, 0x00000000, 0x00070001, 0x027c0057, 0x37c108f0, 0x5c461f00, +0xc019f003, 0x045f0017, 0x0df0033c, 0xdb4007c0, 0xf2037c00, 0x0037c021, +0x005c101f, 0x07c00df0, 0x00000c00, 0x00000000, 0x003f0880, 0x01cc00b3, +0x3cc037f0, 0xfc017300, 0x010ff003, 0x05430029, 0x0f3003cd, 0xf31088c0, +0x3003dc00, 0x006cc053, 0x53c804b3, 0x00c00ef0, 0x00000c22, 0x00000000, +0x00262081, 0x25442111, 0x30501910, 0x74001500, 0xc60dd003, 0x03510016, +0x0db00345, 0xd1408440, 0x10032c00, 0x40354039, 0x1e440751, 0x05400dd0, +0x00000802, 0x00000000, 0x0414a001, 0x0a041851, 0x34400910, 0x7404d100, +0x4019d003, 0x02910014, 0x1d100754, 0xc0041440, 0x50034400, 0x02204109, +0x00540051, 0x04400dd0, 0x00000200, 0x00000000, 0x40102010, 0x02050041, +0x34400010, 0x36000580, 0x520cd003, 0x00c10010, 0x1d900715, 0xc1200050, +0x11032400, 0x0021400d, 0x03140081, 0x41400cd0, 0x00000080, 0x00000000, +0x00043000, 0x00440093, 0x34c00074, 0x7c005100, 0x400df003, 0x00534024, +0x0d30035c, 0xf30004d0, 0x3003dc40, 0x0014c009, 0x015d00c3, 0x00c00ef0, +0x00000ac0, 0x00000000, 0x000fb005, 0x00fc003f, 0x3fc003f0, 0xfc003f00, +0xc00ff003, 0x007f001e, 0x0fb003ec, 0xff000fc0, 0xf003bc00, 0x003ec00f, +0x03ec00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x133fa003, 0x02cc00bf, +0x0cc00ff0, 0xcc607f00, 0xc0cff081, 0x00f3023c, 0x0ff003ec, 0x63003fc0, +0x30028c04, 0x223cc00b, 0x53cc08fb, 0x0fc0cf30, 0x00000e00, 0x00000000, +0x03270801, 0x004400dd, 0x054004d0, 0x44205d08, 0x406dd103, 0x00d100b4, +0x0dd00944, 0x5b053f41, 0x10094412, 0x03b6c024, 0x0b6c04c1, 0x0740c914, +0x00000c20, 0x00000000, 0x0033a011, 0x060400cd, 0x01400cd0, 0x1400dd00, +0x408cd007, 0x00cd01a0, 0x0cd00f24, 0x41013364, 0x5e201402, 0x20304088, +0x830484c9, 0x47440c10, 0x00000e80, 0x00000000, 0x0035a803, 0x024400dd, +0x054005d0, 0x5400dd00, 0x4005d103, 0x00dd0024, 0x0dd00344, 0x59003760, +0x50035480, 0x30364005, 0x036420d1, 0x0f408d10, 0x00000620, 0x00000000, +0x0067a802, 0x034c12cf, 0x64d04df1, 0x5c00df01, 0xd069f002, 0x00ff006c, +0x0df0026c, 0xf30037c0, 0x7208dd00, 0x0034e20b, 0x034c00db, 0x03c00d30, +0x00000e20, 0x00000000, 0x026d8007, 0x03fc80ff, 0x6ec08ff0, 0xed00ff01, +0xc003f001, 0x00e34267, 0x0ff002fc, 0xff0037c0, 0xb003ac00, 0x0033c407, +0x033c00ff, 0x1fc01bf1, 0x00000602, 0x00000000, 0x02250802, 0x037c03df, +0x07c0acf0, 0x7c084f00, 0xd028f003, 0x04d34034, 0x0df0077c, 0xc34034d0, +0x30084c00, 0xc034c018, 0x134800db, 0x0bc00d34, 0x00000420, 0x00000000, +0x0024a013, 0x037409dd, 0x44400fd0, 0x74005d02, 0x4401d022, 0x06f10134, +0x0fd00344, 0xd1603c40, 0x10036c0a, 0x003c4005, 0x1bec00f1, 0x4f400d10, +0x00000200, 0x00000000, 0x0032a007, 0x0234200d, 0x01400cd0, 0x34014d00, +0x406cd00b, 0x03c90024, 0x0cd00334, 0x41003640, 0x90002402, 0x00324008, +0x0b0400d1, 0x1f400c10, 0x00000a00, 0x00000000, 0x00688004, 0x06b401ed, +0x48441ed0, 0xb4016d10, 0x4014d007, 0x01e90068, 0x1ed02785, 0x61827a40, +0x9007a401, 0x087a4026, 0x07a400e1, 0x1b409a10, 0x00000200, 0x00000000, +0x01301012, 0xa23c084f, 0x01c20cf0, 0x3c00cf14, 0x504cf021, 0x00db4120, +0x8df0233c, 0x43003240, 0xb4082c00, 0x0232c088, 0x030c08c3, 0x4bc00c30, +0x00000040, 0x00000000, 0x00bdb802, 0x0afc08ff, 0x0fc08ff0, 0xfc48df02, +0xc007f003, 0x00f7202f, 0x0df0a37c, 0x7f003dc0, 0x7003fc80, 0x083dc0d7, +0x03fc80ff, 0x0bc00bf0, 0x00000660, 0x00000000, 0x0037a015, 0x0b7c00d3, +0x60c009f0, 0x5800d300, 0xc049f002, 0x00d30034, 0x0d30020c, 0xdb0135c0, +0x26004c00, 0x2034c009, 0x034ca0df, 0x54c01ff2, 0x00000e00, 0x00000000, +0x10318812, 0x03b400e1, 0x285028d0, 0xbc00e100, 0x4502d003, 0x00f10038, +0x4e100285, 0xc302b9c0, 0xb0032c00, 0x017ac006, 0x17ac04fd, 0x48400bd0, +0x00000620, 0x00000000, 0x00790003, 0x47b401e5, 0x6e415ad0, 0xb4016100, +0x401ad007, 0x01e10078, 0x9e100785, 0xe9017b40, 0xd204a401, 0x027a6018, +0xa7840ded, 0x0d521ed2, 0x00000402, 0x00000000, 0x00332812, 0x033400c5, +0x706008da, 0x34004100, 0x4090d120, 0x00c10274, 0x0c100704, 0xc1003740, +0xd00b2480, 0x00324004, 0x032420cd, 0x494008d0, 0x00000c20, 0x00000000, +0x4015a817, 0x0d7c1077, 0xded027f0, 0x1c024341, 0xd435f005, 0x00534014, +0x05349dcc, 0x5b0017c0, 0xf0016c02, 0x8816c007, 0x014c005f, 0x5dc005f0, +0x000004a0, 0x00000000, 0x00030012, 0x107c0119, 0x47c041f0, 0x5c521f00, +0xc101f004, 0x001f0007, 0x01f0047c, 0x1f0005c8, 0x9000bc10, 0x0007c002, +0x007c001f, 0x4a4001f0, 0x00000c00, 0x00000000, 0x00370810, 0x037c0097, +0x27c008f0, 0x6c259f00, 0xc00d3022, 0x00830024, 0x09b0020c, 0x970025c0, +0x30424c01, 0x0024c009, 0x025c008f, 0x43c009f0, 0x00000420, 0x00000000, +0x20262001, 0x1a740191, 0x264019d1, 0x4c009d00, 0x44291116, 0x04912024, +0x09100244, 0x81202740, 0xb002540a, 0x0026c009, 0x2244009d, 0x07400970, +0x00000800, 0x00000000, 0x0064a018, 0x02740195, 0x25408950, 0xc4029d01, +0x40281002, 0x04910024, 0x09900645, 0x95002540, 0x1202c410, 0x0026601b, +0x0274009d, 0x634009d0, 0x00000200, 0x00000000, 0x02202010, 0x023400c1, +0x224088d0, 0x8700ad00, 0x40881902, 0x00814a20, 0x08101204, 0x81012340, +0x90229400, 0x0322509a, 0x2624208d, 0x43408850, 0x00000080, 0x00000000, +0x0d86b01d, 0x007c4017, 0x15c16170, 0xcc801f20, 0xd1603400, 0x9e110580, +0xe1b00444, 0x1522c1c1, 0x30084c0a, 0x0186c023, 0xc0740e1f, 0x77c161f0, +0x00000ac0, 0x00000000, 0x21279819, 0x027c00bf, 0x3dc04bf0, 0x7c00df00, +0xc049f103, 0x019f012f, 0x19f022fc, 0x9f0267c0, 0xf01a7c03, 0x01a7c069, +0x025c099f, 0x67c04b70, 0x00000e60, 0x00000000, 0x40b7a018, 0x82fca0b3, +0x2cc029f0, 0xbc80bf00, 0xd069f002, 0x01bb00ec, 0x89f042dc, 0xb30024c1, +0x30164c07, 0x01a4c05b, 0x1afc049f, 0x67c0c930, 0x00000e00, 0x00000000, +0x0083081c, 0x00740011, 0x044040d0, 0x74001d00, 0x5060d000, 0x02130044, +0x01d0094c, 0x1b150440, 0x1038440a, 0x01844000, 0x1074240d, 0x73400110, +0x00000c22, 0x00000000, 0x20a3a010, 0x86348085, 0x205029d0, 0x36008d00, +0x4028d002, 0x168120a0, 0x48d00216, 0x8100a150, 0x14420400, 0x03204168, +0x1a34128d, 0x43406810, 0x00000e80, 0x00000000, 0x0021a818, 0x027404d5, +0x646009d0, 0x74409d00, 0x4108d102, 0x80992020, 0x09d00264, 0x99002040, +0x128a4400, 0x00244009, 0x0276009d, 0x63400910, 0x00000620, 0x00000000, +0x0227a805, 0x8a7c0197, 0xe4c008f0, 0x7c009f00, 0xc809f006, 0x00934064, +0x09f0065c, 0x830025c0, 0x30024d00, 0x9024c009, 0x827c009f, 0x17c01930, +0x00000e20, 0x00000000, 0x00358034, 0x027c009b, 0x27c00df0, 0x7c009f10, +0xd00df066, 0x00870127, 0x08f0265c, 0x9f0027c0, 0xf0023c00, 0x0027d109, +0x027c009f, 0x53c099f4, 0x00000600, 0x00000000, 0x80050814, 0x107c0113, +0x04c001f0, 0x7c021f00, 0xd0213208, 0x00130004, 0x01b0084c, 0x130007c0, +0x30804c01, 0x0004c000, 0x004c0017, 0x50c001f0, 0x00000420, 0x00000000, +0x4414a014, 0x19f40370, 0x1d6805d0, 0xf4007d14, 0x4885b011, 0x0c712014, +0x0510014c, 0x71001740, 0x1001c402, 0x00154017, 0x45d40051, 0x505005d0, +0x00000200, 0x00000000, 0x0072a014, 0x063409c1, 0x724084d0, 0x3408cd00, +0x400c100b, 0x03c10070, 0x0c900325, 0xc5003340, 0x54030400, 0x0030401c, +0x076400c5, 0x50500cd0, 0x00000a00, 0x00000000, 0x00388005, 0x02b40021, +0x3b4006d0, 0xb420ed10, 0x480e9007, 0x00e100a8, 0x4e100704, 0xe5013b40, +0x50038400, 0x0179002a, 0x03b009e1, 0x14401cd0, 0x00000200, 0x00000000, +0x04781015, 0x06bc01a1, 0x7a4016f0, 0xbc016f00, 0xc01e3007, 0x01f3407c, +0x3eb086ac, 0xf740f3c0, 0x701fcc01, 0x00fcc01a, 0x05ac0de7, 0x54c07ef0, +0x00000040, 0x00000000, 0x01b5b810, 0x027c801f, 0x35c045f0, 0x7c801f00, +0xc22df001, 0x001f2427, 0x1df0037c, 0xdb05b7c0, 0xb0037d00, 0x0077c80d, +0x035c02df, 0x43c00ff0, 0x00000660, 0x00000000, 0x007fa000, 0x27bc0133, +0x7c003730, 0xcc09fd02, 0xc03ff007, 0x01f3006c, 0x1ff017dc, 0xf3007cc0, +0x3207cc01, 0x007dc01f, 0x07fc01ff, 0x0bc01ff0, 0x00000e00, 0x00000000, +0x28398815, 0x03b41021, 0x38504210, 0x8408ed00, 0x402e7011, 0x04710038, +0x0fd00390, 0xe1023ac2, 0xb0039400, 0x0139400f, 0x23b404ed, 0xd7414ed0, +0x00000620, 0x00000000, 0x04310000, 0x01f40021, 0x38400412, 0x84086d00, +0x408cd002, 0x80e90068, 0x0ed01294, 0xc9003840, 0x50038400, 0x1038408e, +0x01b640ed, 0x43600ed0, 0x00000400, 0x00000000, 0x00632804, 0x41341001, +0x20400010, 0x04000d00, 0x400c5020, 0x00894030, 0x0cd00b04, 0x49003240, +0xd0331400, 0x0031409c, 0x033400cd, 0x1b401cd0, 0x00000c20, 0x00000000, +0x0065a815, 0x0b7c0293, 0x74d02510, 0x4c005f00, 0xd02df02f, 0x00db4034, +0x0ff0231c, 0xdb403cd0, 0x700bcc00, 0x003dc02d, 0x027c00ff, 0x77c01ff0, +0x00000620, 0x00000000, 0x002f0001, 0x837c029f, 0xf7c105f4, 0x7d025f00, +0xc06df003, 0x00d70027, 0x0df0037c, 0xd70031c0, 0xb0033c82, 0x0037c04d, +0x027c80df, 0x07c00df0, 0x00000c00, 0x00000000, 0x023f0880, 0x03fc0033, +0x1fc00330, 0xfc017f00, 0xc00ff202, 0x00f30038, 0x0f3002fc, 0xb30037c0, +0x30034c05, 0x003cc01e, 0x02cc00ef, 0x07c00f10, 0x00000c20, 0x00000000, +0x08362081, 0x07340011, 0x37409110, 0x74411d08, 0x402dd004, 0x0111002c, +0x0d10037c, 0x850035c0, 0x50035401, 0x0037c07d, 0x060400dd, 0x07440d14, +0x00000802, 0x00000000, 0x0020a001, 0x23740411, 0xb7480510, 0x74941d00, +0x400cd0a3, 0x01d11024, 0x0d900374, 0xd5003740, 0x10034410, 0x0034404d, +0x476400dd, 0x07400d50, 0x00000200, 0x00000000, 0x40202010, 0x03340001, +0x33400010, 0x34000d00, 0x4004d001, 0x00012031, 0x0c940354, 0xc5003140, +0x50031400, 0x00335004, 0x022400cd, 0x43400c10, 0x00000080, 0x00000000, +0x0036b000, 0x017c0013, 0x17400134, 0x74001f00, 0xc00df002, 0x00d34028, +0x0db00274, 0x97003fc0, 0x30034c00, 0x0034c005, 0x026c00ff, 0x07c00d70, +0x00000ac0, 0x00000000, 0x002fb805, 0x01fc003f, 0x2fc003f2, 0xfc402f00, +0xc00fd000, 0x00bf003e, 0x0f7003fc, 0xbf003fc0, 0xf003fc00, 0x003fc40f, +0x02dd00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x003f8003, 0x33fc1c33, +0x3fc08ff0, 0xad00f300, 0xc00f3001, 0x0033200f, 0x0fb083c4, 0x7f053cc1, +0x3212fc00, 0x133cc84b, 0x33c40c73, 0x0cc00ff0, 0x00000e00, 0x00000000, +0x12b70801, 0x33740251, 0x3740edd0, 0x4400d140, 0x400d1201, 0x00510007, +0x2f900344, 0x5d00b440, 0x5021744a, 0x00b54085, 0x0b440291, 0x04400dd0, +0x00000c00, 0x00000000, 0x0833a011, 0x03140481, 0x33420c50, 0x4400c100, +0x480c1501, 0x0045c007, 0x0c108305, 0x4d103048, 0x10003440, 0x0b344008, +0x0a240c11, 0x44400cd0, 0x00000e80, 0x00000000, 0x2835a803, 0x03760091, +0x37400dd0, 0x4401d100, 0x400d1001, 0x00450c07, 0x0d901304, 0x5d003440, +0x50037410, 0x00354005, 0x03640ad1, 0x0c400dd0, 0x00000620, 0x00000000, +0x4037a802, 0x037c2213, 0x37c00df0, 0x2c019300, 0xc01d3001, 0x03d700e7, +0x0d34134c, 0x7f0034d0, 0x3018fc02, 0x0034c00b, 0x036c0043, 0x08d00cf0, +0x00000e20, 0x00000000, 0x00318007, 0x03fc247f, 0x3fc00ff2, 0xfc108f00, +0xc49ff063, 0x42fb015f, 0x0d7007fc, 0x7f003fc0, 0xf003bc00, 0x003fc004, +0x03dd01ff, 0x1fc00ff0, 0x00000600, 0x00000000, 0x00350802, 0x037c409b, +0x37c00df0, 0x5c029300, 0xc00d7401, 0x46d340b4, 0x0c30034c, 0x534035c0, +0xb0085c02, 0x0035d009, 0x124d8213, 0x0bc00d72, 0x00000420, 0x00000000, +0x203ca013, 0x13f40091, 0x3f400fd0, 0x6c009140, 0x400d1001, 0x02d10070, +0x0f164344, 0x5b007c48, 0xb0176c00, 0x20384205, 0x8a440091, 0x4f400f10, +0x00000200, 0x00000000, 0x0032a007, 0x07240009, 0x36600c90, 0x04094100, +0x480c1101, 0x004c0020, 0x6c100614, 0xc1a1b140, 0x90001100, 0x00314008, +0x0b040041, 0x1f400c54, 0x00000a00, 0x00000000, 0x00788004, 0x87b405e1, +0x7b401ed8, 0xa401e108, 0x401e1005, 0x01e90458, 0x1e1027a7, 0xc906784c, +0x90072429, 0x007c4016, 0x0784c131, 0x13401e10, 0x00000200, 0x00000000, +0x00301012, 0x033c884b, 0x37c08cfa, 0x1c04c302, 0x404c5021, 0x025d0034, +0x0c30005c, 0xc30035c0, 0xb0001c08, 0x0031c008, 0x024c8083, 0x4bc00c74, +0x00000000, 0x00000000, 0x003db802, 0x43fc0cff, 0x37c20ffa, 0xfc00df00, +0xd00cf001, 0x08f70237, 0x8ff203dc, 0xff0c3fc0, 0xf003fc08, 0x003bc017, +0x23fc08ef, 0x0bc00ff0, 0x00000660, 0x00000000, 0x0037a015, 0x036c011f, +0x36c08df0, 0x4c20df22, 0xc01df001, 0x01c30064, 0x2dbc036c, 0xd30034c0, +0xa4004c00, 0x3074d009, 0x034c0013, 0x54c00db2, 0x00000e00, 0x00000000, +0x00f98812, 0x038400fd, 0x3b404ed0, 0x8400ed00, 0x400ad083, 0x00e10018, +0xae500384, 0xc1013840, 0x10030400, 0x11fc4006, 0x03c40031, 0x48504f14, +0x00000620, 0x00000000, 0x01790003, 0x17840165, 0x7b4c1ed0, 0x8401cd21, +0x401ed005, 0x01f90078, 0x5e100f84, 0xe1007850, 0x90048501, 0x01784018, +0x068400e1, 0x0c409ed4, 0x00000400, 0x00000000, 0x20332812, 0x030480cd, +0x33400dd2, 0x0400cd00, 0x501cd104, 0x01c14070, 0x0cd00704, 0xd1403040, +0x10030400, 0x20304004, 0x020401c1, 0x48400c50, 0x00000c20, 0x00000000, +0x0015a817, 0x014d007f, 0x16c005f0, 0x8d046f00, 0xd005f049, 0x127b00dc, +0x07b10d8d, 0x530014c0, 0xb0090d00, 0x001cc007, 0x054c0173, 0x5cc005f0, +0x00000620, 0x00000000, 0x08070012, 0x087c401f, 0x07c201f0, 0x7c101f08, +0xc081f00c, 0x811f0247, 0x0070485c, 0x1f0007c0, 0xf040fc00, 0x4007c802, +0x287c080f, 0x4bc021a0, 0x00000c00, 0x00000000, 0x00270810, 0x063c009f, +0x24c00970, 0x4d009f00, 0xc0893816, 0x019f2027, 0x0930027c, 0x930024c6, +0x70064c00, 0x0025c009, 0x020ca09f, 0x40c00871, 0x00000c20, 0x00000000, +0x10262001, 0x9674409d, 0x24400910, 0x44429d00, 0x40291002, 0x809d00a7, +0x29101274, 0x9b08a458, 0x04266c40, 0x00244009, 0x0a44009d, 0x04512910, +0x00000800, 0x00000000, 0x0024a018, 0x927400dd, 0x26500950, 0x4402dd20, +0x43091002, 0x869d0427, 0x49100274, 0x91052040, 0x1002c400, 0x0025400b, +0x0244009d, 0x60400950, 0x00000200, 0x00000000, 0x07202010, 0x7234008d, +0x20400810, 0x05149d02, 0x40885802, 0x288d0223, 0x881c2274, 0x89222040, +0x1002a408, 0x0220508a, 0x0a05088d, 0x40402810, 0x00000080, 0x00000000, +0x0186b01d, 0x987c941f, 0x84c14170, 0x4c041f05, 0x40201081, 0x021f0083, +0x6430083c, 0x131580c1, 0x74004c02, 0x0585c023, 0x014c561f, 0x74c00170, +0x00000ac0, 0x00000000, 0x2127b819, 0x127c14bf, 0x27c149f0, 0xfc00bf21, +0x404bb402, 0x04bf012f, 0x49f012fc, 0x9f0927c0, 0xf00a7c06, 0x0127c069, +0x8afc24bf, 0x67c029f0, 0x00000e60, 0x00000000, 0x0527a018, 0x0acc0093, +0xa7c049f0, 0x8c048f01, 0xc00b3002, 0x08930024, 0xc9308a7c, 0x9f0324c1, +0x3006cc0d, 0x042ec119, 0x12f40093, 0x60d02ab0, 0x00000e00, 0x00000000, +0x2187081c, 0x08440411, 0x834941d0, 0x45021d01, 0x0340b000, 0x00110d04, +0x21000074, 0x0d0008c0, 0xb1285402, 0x08844141, 0x90749515, 0x71404110, +0x00000c20, 0x00000000, 0x4423a010, 0x0a041281, 0x234028d0, 0x44088d03, +0x40091002, 0x04a10028, 0x4a0102b4, 0x8d01a950, 0x1402040c, 0x05224008, +0x4a344081, 0x40484890, 0x00000e80, 0x00000000, 0x0025a818, 0x02640291, +0x274009d0, 0x44209d00, 0x58099042, 0x00b1402c, 0x0b108af4, 0x8d082d40, +0x92025400, 0x00244009, 0x02744495, 0x61500910, 0x00000620, 0x00000000, +0x0027a805, 0x024d0193, 0x27c009f0, 0x0c129f00, 0xc008302e, 0x40932124, +0x0934027c, 0x9f1825c0, 0x30424c00, 0x4026c809, 0x267c8493, 0x14d009b4, +0x00000e20, 0x00000000, 0x00258014, 0x025c099f, 0x27c009f0, 0x7d00cf00, +0xc099f042, 0xe89f0667, 0x08f0027c, 0x9f0024c0, 0xf0027c00, 0x0027c009, +0x463c009f, 0x53d009f4, 0x00000600, 0x00000000, 0x00050814, 0x104c0003, +0x07c00134, 0x4c001300, 0xc1013c00, 0x001f0084, 0x01f0084c, 0x130004c0, +0x30084c00, 0x0004c800, 0x007c0213, 0x50c011f0, 0x00000420, 0x00000000, +0x0014a014, 0x05c40051, 0x17400510, 0xc4085100, 0x40051041, 0x535d0094, +0x05d04144, 0x5b081400, 0xb0010400, 0x10544005, 0x4df40055, 0x504076d1, +0x00000200, 0x00000000, 0x0036a014, 0x0f0500c1, 0x33400c10, 0x25000100, +0x403c1007, 0x13cd0234, 0x1cd04744, 0xc1003060, 0x10030400, 0x2074540c, +0x023000c1, 0x50400cd0, 0x00000a00, 0x00000000, 0x01308005, 0x478404e1, +0x7b408e10, 0xa4006101, 0x442e1003, 0x016d1028, 0x2ed00284, 0xe9206860, +0x94028404, 0x0438404e, 0x02b440e5, 0x14400ed1, 0x00000200, 0x00000000, +0x41781015, 0x07cc03f3, 0x7bc25e31, 0xac11a341, 0xd0161006, 0x01fd0058, +0x17f2078c, 0xe32458d0, 0x30078d05, 0x007ce47c, 0x06bc41e3, 0x54d016f0, +0x00000040, 0x00000000, 0x0235b810, 0x027c00df, 0xb7c00df0, 0x5c06df02, +0xc005f002, 0x00df0007, 0x0dd0027d, 0xdf0037c0, 0xf002241c, 0x0037c00d, +0x007c01fe, 0x43c005f0, 0x00000660, 0x00000000, 0x007fb000, 0x06cc0dff, +0xffc01f30, 0x9c012300, 0xc81f2007, 0x21fa027f, 0x1b3007cd, 0xf2007cc0, +0x34078c01, 0x006fc21f, 0x07fc01f3, 0x03801332, 0x00000e00, 0x00000000, +0x00398015, 0x028418ed, 0x3b400e10, 0x950a6100, 0x044eb0a3, 0x0061002b, +0x2a3402c4, 0xf100a840, 0x5002bc00, 0x002b400e, 0x23b400e1, 0x56408210, +0x00000620, 0x00000000, 0x02390000, 0x018604ed, 0x3b408e90, 0xd400b100, +0x09021003, 0x12e1043b, 0x06904384, 0xe1001a40, 0x1003a400, 0x002b400e, +0x03b418e1, 0x03400214, 0x00000400, 0x00000000, 0x00332804, 0x000606cd, +0x33400c90, 0x1406c100, 0x44109023, 0x015100e3, 0x0c100204, 0xc1003040, +0x54363400, 0x0027402d, 0x017406c1, 0x12400010, 0x00000c20, 0x00000000, +0x003da815, 0x014d03ff, 0x3fc00fb4, 0x1d021340, 0xc00d300f, 0x41934077, +0x0cb00d4c, 0xf30034f0, 0x300b6d00, 0x4037c07f, 0x077c01f3, 0x57c00930, +0x00000620, 0x00000000, 0x00370001, 0x017c00df, 0x37c20d70, 0x7c005f00, +0xc00df41b, 0x80170037, 0x2df0417c, 0xdf20a7c0, 0xf0037c00, 0x1037c04d, +0x077c00df, 0x06c009f0, 0x00000c00, 0x00000000, 0x003f0880, 0x01fc00df, +0x34c00ef0, 0xcd00a300, 0xe01f3002, 0x08b6061f, 0x173201cc, 0xf32050c0, +0x70028c00, 0x0134c00f, 0x037c00f3, 0x03c00371, 0x00000c22, 0x00000000, +0x00362081, 0x007400dd, 0x35400dd0, 0x6c02d100, 0x601d5108, 0x00910017, +0x35100904, 0xd108c440, 0x10027c00, 0x0034400d, 0x057400df, 0x07400110, +0x00000802, 0x00000000, 0x0034a001, 0x027400dd, 0x30400dd8, 0x64009120, +0x498d1043, 0x00d100b3, 0x89192b44, 0xc1223440, 0x50234400, 0x0034400d, +0x057420d1, 0x07401550, 0x00000200, 0x00000000, 0x00302010, 0x063400cd, +0x31400cd0, 0x65004100, 0x400c5003, 0x00410823, 0x09180245, 0xc1002040, +0x14033700, 0x0030400c, 0x053400cd, 0x43400410, 0x00000080, 0x00000000, +0x0036a000, 0x013c00df, 0x3cc00df0, 0x4c009300, 0x40053003, 0x80834013, +0x0014014c, 0xf30014c0, 0x72024c00, 0x0034d00d, 0x017c00d3, 0x03c00570, +0x00000ac0, 0x00000000, 0x003f8805, 0x00fc00ff, 0x3fc00fd0, 0xfc00ff20, +0xc007f003, 0x003b000f, 0x039000fc, 0xff000f80, 0xf002fc00, 0x083fc00f, +0x01fc20fc, 0x17c007f0, 0x00000e60, 0x00000000, 0x002f8003, 0x008c4033, +0x0fc003d2, 0xfc20ff02, 0xc00f3410, 0x80f3043d, 0x8fb013ec, 0x3b053ec0, +0x7000dc28, 0x403fc007, 0x83cc0073, 0x0fc08ff2, 0x00000e00, 0x00000000, +0x24170801, 0x80440051, 0x174105d0, 0x7418df00, 0x400d1148, 0x10f500b4, +0x6e103bc4, 0x11013c40, 0x10004404, 0x0437400d, 0x13c40051, 0x07404dd0, +0x00000c20, 0x00000000, 0x0103a011, 0x00340009, 0x13400050, 0x7404cd00, +0x400c9030, 0x48c50031, 0x2c104324, 0x8d053240, 0x50003400, 0x4137480c, +0x43140051, 0x47404cd0, 0x00000e80, 0x00000000, 0x0875a803, 0x04750019, +0x974005d0, 0x7400d510, 0x400d9042, 0x00c50034, 0x0d900364, 0x95003040, +0x14006400, 0x0117400d, 0x03561050, 0x0f400dd0, 0x00000620, 0x00000000, +0x44b7a802, 0x447c058b, 0x07c8c1f0, 0x7c009d10, 0xc08db000, 0x00d70035, +0x0da4036c, 0x1f0036c0, 0x700c7c01, 0x0037c09d, 0x03590052, 0x0bc20df2, +0x00000e20, 0x00000000, 0x201d8007, 0x00cc0277, 0x3fc007f2, 0x7c00bf10, +0xc00f7000, 0x80ff103f, 0x0f70031c, 0x9b0037c0, 0xf0249c09, 0x087fc80b, +0x03ec023f, 0x1fc00ff2, 0x00000600, 0x00000000, 0x00b50802, 0x007c009f, +0xb5c08970, 0x4d00df00, 0xc01db04b, 0x00df1037, 0x0d70434c, 0x8b4035c0, +0x34284c00, 0x0035c00d, 0x034c025f, 0x0b400d30, 0x00000420, 0x00000000, +0x02b4a013, 0x28740a1d, 0x34400d50, 0x0800dd00, 0x400c500b, 0x00fd043f, +0x0f100fc4, 0x90003ec0, 0x10804400, 0x03b0400d, 0x03ec00cd, 0x4f440f10, +0x00000200, 0x00000000, 0x0026a007, 0x0834000d, 0x03413840, 0x2400cd08, +0x48089000, 0x00cc0077, 0x0d502704, 0x41003140, 0x10070400, 0x00a1400c, +0x0334094d, 0x1f400c10, 0x00000a00, 0x00000000, 0x00788004, 0x04a481ed, +0x7a481812, 0x8421ed00, 0x40be1006, 0x49cd007b, 0x9e502784, 0x61027844, +0x10070401, 0x0068601e, 0x2734017d, 0x13401e10, 0x00000200, 0x00000000, +0x00101032, 0x003c084f, 0x33c00c70, 0x2400df02, 0xc00cb000, 0x00cf0033, +0x0c70030c, 0xcb003140, 0x301b0c00, 0x0091c00d, 0x033c004f, 0x4bc08c34, +0x00000040, 0x00000000, 0x003db002, 0x03fc20ff, 0x3dc00ffa, 0xf400ff02, +0xc00ff002, 0x10ff003f, 0x0db04bfd, 0xff0437c3, 0xf0037d08, 0x021fc08f, +0x03ec006f, 0x0bc30ff0, 0x00000660, 0x00000000, 0x0037a015, 0x0074409f, +0x07c001f0, 0x7c009e05, 0xc00cf002, 0x04d3403c, 0x0d700f6c, 0x530936c1, +0x32135c00, 0x4037c00d, 0x334c0041, 0x57c00db0, 0x00000e20, 0x00000000, +0x84398812, 0x00b420ed, 0x3b4402d0, 0xb400ad00, 0x480ef002, 0x10f1007c, +0x4e011304, 0xe5033040, 0x10438400, 0x003b440e, 0x43c40065, 0x4b404e10, +0x00000620, 0x00000000, 0x94790003, 0x469401ed, 0x7b401ed0, 0xb401ed00, +0x401ed007, 0x05e10078, 0x1c4103a4, 0xc1017b40, 0x90071401, 0x005b401e, +0x13a58169, 0x0f401e51, 0x00000400, 0x00000000, 0x80732812, 0x963404cd, +0xb3402cd0, 0x3401cd04, 0x528c40a7, 0x00c10034, 0x0c100304, 0xc5003140, +0x90030412, 0x1057407c, 0x0324424d, 0x4b400c50, 0x00000c20, 0x00000000, +0x00d5a817, 0x05bc027f, 0x97c007f0, 0xfc105f00, 0xc014d00d, 0x40533014, +0x0570016c, 0x530017c0, 0xb6015d00, 0x51dfc037, 0x016c106b, 0x5fc005f0, +0x00000600, 0x00000000, 0x04870012, 0x007c400f, 0x07c041f0, 0x7c001f00, +0xc001f20c, 0x000f0007, 0x01f0007c, 0x1f0000c8, 0x74003c00, 0x00c7c081, +0x005c3017, 0x4bc001a0, 0x00000c00, 0x00000000, 0x00770810, 0x025c0893, +0x34c01870, 0x7c009f08, 0xc009f006, 0x00930024, 0x08300a5c, 0x870024f0, +0x30026c00, 0x0024c809, 0x020c0591, 0x43c00930, 0x00000c00, 0x00000000, +0x43262001, 0x22540391, 0x24405950, 0x74209d00, 0x4009d00a, 0x00935024, +0x09101a04, 0x91002444, 0x10024400, 0x01654008, 0x026c4095, 0x07400910, +0x00000800, 0x00000000, 0x0020a018, 0x02541091, 0x24408950, 0x74009d00, +0x5019d062, 0x00910024, 0x09100254, 0x95002440, 0x1002e500, 0x0064500d, +0x02440095, 0x63400910, 0x00000200, 0x00000000, 0x02202010, 0x02040081, +0x20508810, 0x34088d02, 0x4018d022, 0x08810020, 0x48104204, 0xa1032040, +0x10029406, 0x90204089, 0x32260491, 0x43400814, 0x00000080, 0x00000000, +0x0086b01d, 0x505c1413, 0x84c16153, 0x7c561f00, 0xc141f058, 0x10130384, +0xc130005c, 0x17050440, 0x1010ec04, 0x4004c001, 0x0c4c0417, 0x77c1e130, +0x000008c0, 0x00000000, 0x0137b819, 0x02fc00bf, 0x27c24bf0, 0xfc04bf01, +0xc00bf012, 0x009f0327, 0xc9f04274, 0x9f0127c0, 0xf4226426, 0x042bc01b, +0x327c08bf, 0x67c019f0, 0x00000ce0, 0x00000000, 0x18afa018, 0x02cc009f, +0xa6c92bf0, 0xcc029f10, 0xd60bf01a, 0x009b0368, 0x49b052cc, 0x9b00a4c1, +0x3042ec00, 0x082bc809, 0x027c14b3, 0x60c2c9b0, 0x00000e00, 0x00000000, +0x0007081c, 0x00442a1d, 0x044021d0, 0x44021d01, 0x4021d001, 0x04050184, +0x61501844, 0x51208040, 0x10084406, 0x01174441, 0x00740251, 0x70406150, +0x00000c20, 0x00000000, 0x0123a010, 0x0a24008d, 0x204008d0, 0x24228d00, +0x4028501a, 0x128140b0, 0x08904234, 0x8820a041, 0x50022532, 0x04234108, +0x0a340081, 0x40402810, 0x00000e80, 0x00000000, 0x0125a818, 0x5264209d, +0x264028d2, 0x64009d14, 0x4009d002, 0x00950024, 0x08100244, 0x81002440, +0x50024400, 0x00274069, 0x02742895, 0x60400950, 0x00000620, 0x00000000, +0x00a7a805, 0x8265009f, 0xe6d019f1, 0x6d089f20, 0xc088f00e, 0x00930024, +0x09b1025d, 0x9b0024d0, 0x54026c02, 0x40e7c009, 0x027c0193, 0x14d009b4, +0x00000e20, 0x00000000, 0x00658014, 0x025c08cf, 0x25c029f0, 0x5c008f22, +0xc019f012, 0x008f2027, 0x09f0027c, 0x9f0023c0, 0xb0025c00, 0x0167c009, +0x023c009b, 0x53c009f1, 0x00000600, 0x00000000, 0x44850814, 0x047c1013, +0x04c10150, 0x4d001f00, 0xc0013020, 0x00130044, 0x01f0404c, 0x170005e0, +0x31006d02, 0x0087cc01, 0x004c0013, 0x50c00130, 0x00000420, 0x00000000, +0x105ca014, 0x09f40051, 0x14423750, 0xc4005d10, 0x40071029, 0x0051001c, +0x05900dd4, 0x51c01442, 0x00110400, 0x24df4005, 0x016c1051, 0x504005b0, +0x00000200, 0x00000000, 0x0032a014, 0x243400c1, 0x32402c50, 0x2400cd00, +0x50041007, 0x80c10034, 0x0cd00144, 0xc1003148, 0x90072400, 0x0487480c, +0x030401c1, 0xd2400d10, 0x00000a00, 0x00000000, 0x04588005, 0x04240061, +0x38402e50, 0x8400ed22, 0x490c1005, 0x08e10038, 0x4ed00984, 0xe1013840, +0x14028404, 0x801f40ce, 0x13a401f1, 0x16404e90, 0x00000200, 0x00000000, +0x005c1015, 0x05bc1123, 0x78d01c72, 0x8c85ff00, 0xc0163405, 0x05e30078, +0x7cf0058c, 0xc1017940, 0xb0072c07, 0x505bc01e, 0x0fcc01a3, 0x56d05f30, +0x00000040, 0x00000000, 0x0815b810, 0x817c061f, 0x35e20df0, 0x3408df00, +0xe005f001, 0x10df4036, 0x2db1097d, 0xdf0637d0, 0xf0027d02, 0x0817c00d, +0x077c00cf, 0x41c29df0, 0x00000660, 0x00000000, 0x006fa000, 0x07fc09fb, +0x7bc01fb0, 0xcc01f300, 0xc0133005, 0x01f3005f, 0x3f320dcc, 0xf3207ec0, +0xf206ec03, 0x006fc01f, 0x07fc01f3, 0x00c01f30, 0x00000e00, 0x00000000, +0x01298811, 0x09bc00e1, 0x3b4006f0, 0xa400f100, 0x408e1001, 0x00f1480b, +0x4f1001c4, 0xf1003c40, 0x10028400, 0x001f400e, 0x03f400eb, 0x55400e10, +0x00000620, 0x00000000, 0x90b90000, 0x03b60021, 0xbf400ad0, 0x8400e100, +0x40061001, 0x00e9001b, 0x0e900184, 0xe1023842, 0x50021400, 0x008b400e, +0x03b410a1, 0x00400e10, 0x00000400, 0x00000000, 0x00332804, 0x49140301, +0x37409150, 0x6400c100, 0x40041011, 0x00c90003, 0x0c900005, 0xd1403448, +0x10021502, 0x0017408c, 0x033403c1, 0x11400c10, 0x00000c20, 0x00000000, +0x4021a815, 0x11740613, 0x3f400dd0, 0x4d00f340, 0xc0003403, 0x00f90017, +0x0fb4014c, 0xf1003cd2, 0x70037d02, 0x0087403e, 0x03fc03d1, 0x54c00f34, +0x00000620, 0x00000000, 0x00670001, 0x097c021f, 0x77c009f0, 0x5c00df00, +0xc019f001, 0x00d70007, 0x0d70097c, 0xdf0035c0, 0x70026c90, 0x0017c10d, +0x033c30df, 0x07c40df0, 0x00000c00, 0x00000000, 0x00cf0880, 0x01fc1933, +0x3cc84770, 0xfa00f300, 0xe02370a4, 0x00e70014, 0x0f7001dc, 0xf7003fc0, +0xb0038c00, 0x000cc00f, 0x03c000e3, 0x03c00d30, 0x00000c22, 0x00000000, +0x00062081, 0x0d740311, 0x354131d1, 0x7400d100, 0x50011000, 0x00dd8004, +0x0d100144, 0xd10037c0, 0x50026c00, 0x0054400d, 0x036c00d1, 0x07480d10, +0x00000802, 0x00000000, 0x0c24a001, 0x11741011, 0x34400dd0, 0x7440d100, +0x40015000, 0x00dd2074, 0x0d545114, 0xd5003740, 0x90024400, 0x00c4400d, +0x03540891, 0x07400d10, 0x00000200, 0x00000000, 0x00242010, 0x01340001, +0x30500090, 0x2400c100, 0x40081000, 0x00cd8070, 0x0c100304, 0xd1003140, +0x90022400, 0x0010400d, 0x033600c1, 0x43400c14, 0x00000080, 0x00000000, +0x0022b000, 0x017c0013, 0x34c00170, 0x7400d340, 0x48017200, 0x20ff2034, +0x0f7002dc, 0xf7003f40, 0xb0024c00, 0x0004c00d, 0x035c0093, 0x03c00d30, +0x00000ac0, 0x00000000, 0x002fb805, 0x01fc003f, 0x3fc003f0, 0xbc00ff10, +0xc003f000, 0x00ff003f, 0x0ff003fc, 0xff003fc0, 0x7002fc00, 0x001fc00f, +0x03ec00fd, 0x17c00ff0, 0x00000e48, 0x00000000, 0x001fa003, 0x23fc00f3, +0x3cc40f70, 0xbc08ff02, 0xc007b003, 0x003f000d, 0x033000fc, 0xf3400cc0, +0x3013fc00, 0x003cc402, 0x02ed0033, 0x0cc04f30, 0x00000e00, 0x00000000, +0x00170801, 0x035c0adb, 0x36c00fd8, 0x7684dd00, 0xc40dd203, 0x001d2806, +0x01300074, 0xd1083442, 0x100b7410, 0x00344001, 0x0304005b, 0x06c20c10, +0x00000c00, 0x00000000, 0x4033a011, 0x103400c5, 0x31480cd3, 0x14444d01, +0x4000d003, 0x00451011, 0x08100234, 0xc1100142, 0x540b3408, 0x00304005, +0x03448041, 0x44502c10, 0x00000e80, 0x00000000, 0x0035a803, 0x427400d5, +0xa5600dd1, 0x7400dd00, 0x410dc082, 0x085d0415, 0x09900270, 0xd1063540, +0x50077400, 0x40704105, 0x03440051, 0x0c400d10, 0x00000620, 0x00000000, +0x20b38802, 0x0a7c20d5, 0xf4500d78, 0x7c01df10, 0x402db007, 0x059f00f5, +0xad340b38, 0xd31045d0, 0x70037c40, 0x0074d095, 0x260c0151, 0x084c0d34, +0x00000e20, 0x00000000, 0x003d8007, 0x8edc00db, 0x3e880ff1, 0xfc04ff09, +0xc10fe122, 0x819d023e, 0x0d7057fc, 0xfe0046c0, 0xb003bc00, 0x003fc007, +0x03fc09ff, 0x1fc00ef2, 0x00000608, 0x00000000, 0x00750802, 0x025d00db, +0x36c80cf0, 0x7c01df08, 0xc0093403, 0x08970094, 0xac300b4d, 0xd74100c0, +0x70027c00, 0x0034c00d, 0x036c005b, 0x08c00d30, 0x00000420, 0x00000000, +0x20748013, 0x8a4400f1, 0xb4c00ff0, 0x7402dd00, 0x400c1082, 0x01910054, +0x0d100b44, 0xf1000440, 0xe2037400, 0x02b6c20c, 0x2b440ad1, 0x4c40af10, +0x00000200, 0x00000000, 0xc192a007, 0x120400c1, 0x20404c90, 0x3406cd02, +0x40345023, 0x27c50060, 0x14500914, 0xc100b062, 0xd0033400, 0x0030400c, +0x23260951, 0x1c408c90, 0x00000a00, 0x00000000, 0x00788004, 0x268401e1, +0xea489cd0, 0xf481ed20, 0x511c5005, 0x81e1066c, 0x16502511, 0xe102786c, +0xd00fb401, 0x007a401f, 0x0784a1f1, 0x10401e90, 0x00000200, 0x00000000, +0x00b01012, 0x621c00c3, 0x20d00cf0, 0x3c084f20, 0xc2007003, 0x6445aa34, +0x00700014, 0xc70030d0, 0x70033800, 0x4030c00c, 0x032c0243, 0x48d00cb4, +0x00000000, 0x00000000, 0x00b9b802, 0x22fc08f7, 0x1dc00d71, 0x7e28ff02, +0xc80db221, 0x007f8a37, 0x03b480ec, 0xdf203fe0, 0x72037c00, 0x043bc00f, +0x23fc08e7, 0x0bc08f70, 0x00000660, 0x00000000, 0x00f7b015, 0x027c00d3, +0x71c32d38, 0x3c809b00, 0xc005a003, 0x00d30824, 0x0d30035c, 0xd34037c0, +0xf0037c81, 0x0034c00d, 0x034c005f, 0x54c00d31, 0x00000e00, 0x00000000, +0x00398012, 0x02f405e1, 0x38402f1a, 0xb400a100, 0xc00e1003, 0x00c10822, +0x0e108380, 0xe1043f40, 0xd803b402, 0x0079400f, 0x03c400ed, 0x48485f10, +0x00000620, 0x00000000, 0x00790003, 0x079409e1, 0x7d425e14, 0xb481e900, +0x48129847, 0x03e18078, 0x1e100705, 0xe900fb40, 0x5007b480, 0x0178401e, +0x0784116d, 0x0c40de10, 0x00000400, 0x00000000, 0x00322812, 0x073420d1, +0x10600c10, 0x340fd100, 0x410c114f, 0x10c10432, 0x9c140704, 0xc900f740, +0xd0033420, 0x0a71404d, 0x830441cd, 0x48400c11, 0x00000c20, 0x00000000, +0x0095a817, 0x09dc0051, 0x1dc00532, 0xfc827b04, 0xc025b005, 0x415350d0, +0x25301d5d, 0x5b30d7c0, 0xf0117c40, 0x000cc107, 0x014d027f, 0x5cd00534, +0x00000620, 0x00000000, 0x41070012, 0x247c001f, 0x07c001f0, 0x7c001f00, +0xc021f200, 0x020f0207, 0x01f0007c, 0x170187c9, 0xf0087c00, 0x2007c401, +0x007c011f, 0x4bc001f2, 0x00000c00, 0x00000000, 0x00370810, 0x8e4c809f, +0x24c08850, 0x5cc09f00, 0xc009300e, 0x01930064, 0x0930420c, 0x800027c0, +0x30024c00, 0x0004c019, 0x024c0093, 0x40c00930, 0x00000c20, 0x00000000, +0x02662001, 0x0604409d, 0x64400910, 0x04019d20, 0x42281002, 0x05930065, +0x09120a44, 0x91412700, 0x111a0480, 0x4027c889, 0x02450091, 0x04540914, +0x00000800, 0x00000000, 0x0024a018, 0x0245009d, 0xe4400910, 0x56019d00, +0x40095003, 0x04b0022c, 0x8f1043c4, 0x95002f00, 0x10024400, 0x00204009, +0x02040081, 0x60400810, 0x00000200, 0x00000000, 0x00202010, 0x03441c8d, +0x20508c54, 0x04018d08, 0x400c5402, 0x00a10029, 0x0a100284, 0x81822b40, +0x10024408, 0x40a34088, 0x0a040281, 0x40402810, 0x00000080, 0x00000000, +0x0006a01d, 0x804c261f, 0x04c16152, 0x5c001f00, 0xc1417001, 0x14130d04, +0x41345044, 0x17058fc5, 0x30504c16, 0x0504c021, 0x004c0003, 0x74c00130, +0x00000ac0, 0x00000000, 0x0823a819, 0x52fc049f, 0x2fc849b0, 0xfc14bf0d, +0xc00ab002, 0x00d74027, 0x09f0027d, 0x9a0127c0, 0xfc02fc04, 0x000fc04b, +0x02fc00bf, 0x67c029f0, 0x00000e60, 0x00000000, 0x002fa018, 0x0abc149f, +0x2cc02bf0, 0xfc40bb02, 0xc04bd002, 0x04a3012c, 0x083212fc, 0x9300afc0, +0x30520c00, 0x006cc02b, 0x224c009f, 0x63c40932, 0x00000e00, 0x00000000, +0x8017081c, 0x0074861d, 0x06e021d0, 0x74941101, 0x40a1d200, 0x02110005, +0xa1102870, 0x110187c0, 0x10085400, 0x00454001, 0x0445011d, 0x73487312, +0x00000c20, 0x00000000, 0x0023a010, 0x1234108d, 0x244028d0, 0x34008900, +0x4208d002, 0x088900a0, 0x08100234, 0x8101a340, 0x10024400, 0x05a04408, +0x128614ad, 0x43412a10, 0x00000e80, 0x00000000, 0x2025a818, 0x4274009d, +0x244009d0, 0x74049100, 0x4049d002, 0x00990524, 0x09100274, 0x91002540, +0x10025400, 0x10654029, 0x02c400bd, 0x63400b11, 0x00000620, 0x00000000, +0x0027a805, 0x0a7c009d, 0xa45009d0, 0x7c059b01, 0xc009f002, 0x439b4024, +0xd934167c, 0x93402740, 0x30060c00, 0x0024c009, 0x024c009f, 0x17c00934, +0x00000e20, 0x00000000, 0x00258014, 0x4e7c809f, 0xe7c109f0, 0x7c809f08, +0xd019f012, 0x45970063, 0x0df0527c, 0x9f0027c0, 0xfc277c00, 0x0027c059, +0x267c059f, 0x53c009f0, 0x00000600, 0x00000000, 0x00850814, 0x007c801f, +0x04c00034, 0x7c021700, 0xc040f020, 0x03130005, 0x0130004c, 0x1f0200c0, +0x30007c00, 0x2004c081, 0x00cc003f, 0x50d00330, 0x00000420, 0x00000000, +0x001ca014, 0x45e4005d, 0xd8400710, 0xf4387100, 0x4037d10d, 0x02714114, +0x05140585, 0x59001c43, 0x10297400, 0x2016c005, 0x0144005d, 0x51400510, +0x00000200, 0x00000000, 0x01b2a014, 0x0b2400cd, 0xf0403c10, 0x3442c526, +0x4038d04f, 0x01c90071, 0x0c100d24, 0xdd807040, 0x10003400, 0x0020401d, +0x036400dd, 0x50400c10, 0x00000a00, 0x00000000, 0x00288005, 0x06f40ced, +0x384a2410, 0xb480e100, 0x4027d003, 0x11e10020, 0x1e1041b4, 0xe9881840, +0x1600f400, 0x200a400e, 0x00a4402d, 0x15401010, 0x00000200, 0x00000000, +0x00781015, 0x07bc85ef, 0x7cd01234, 0xbc01e720, 0xc013f007, 0x01e30079, +0x1a320724, 0xef0058d0, 0x3044bc15, 0x0068e01f, 0x07ed01ff, 0x54d01e34, +0x00000040, 0x00000000, 0x0035b810, 0x036c00df, 0x37c001f0, 0x7c00df20, +0xc005f003, 0x009f0037, 0x2df0034c, 0xdb0017c0, 0xf0187c8e, 0x0007e00d, +0x005c001f, 0x42c001f0, 0x00000660, 0x00000000, 0x007fb000, 0x068c01f3, +0x78c213f0, 0xfc49f320, 0xc0dbc004, 0x01f3007c, 0x3f3006cc, 0xf3007cc0, +0xf024dc81, 0x006cc01e, 0x07fc01f3, 0x00c21ff2, 0x00000e20, 0x00000000, +0x00a98015, 0x228408e1, 0x1ac28a10, 0x9c88e106, 0x418ad041, 0x00630038, +0x0e100284, 0xe1021c40, 0xd0308400, 0x0008400e, 0x00b4102b, 0x544083d0, +0x00000620, 0x00000000, 0x02390000, 0x03d500e1, 0x0c400650, 0xb600e940, +0x4040d020, 0x00e10238, 0x0e900284, 0xe1001a40, 0xd000b400, 0x002c400a, +0x63b400e1, 0x00400ed0, 0x00000400, 0x00000000, 0x40332804, 0x410400c1, +0x90700410, 0x16848900, 0x4000d004, 0x00010030, 0x0c900204, 0xc1421242, +0xd2002400, 0x00004009, 0x80344409, 0x104000d1, 0x00000c20, 0x00000000, +0x00a5a815, 0x824480f1, 0x64400c70, 0x3c008b02, 0xd029f00c, 0x80535034, +0x0db4024d, 0xf30016d0, 0xf0447c00, 0x0024c00d, 0x077c00d3, 0x54d00df0, +0x00000600, 0x00000000, 0x00070001, 0x027c00df, 0xa7c005f2, 0x7c009700, +0xc045f040, 0x00572033, 0x0c70027c, 0xdf0415c0, 0xf1085c00, 0x2047d00d, +0x007c001f, 0x07c001f0, 0x00000c00, 0x00000000, 0x04ff0880, 0x0ffc20d3, +0x2cc003b0, 0xcc00bb00, 0xc0033003, 0x04d30074, 0x4df0164c, 0xd3005cc0, +0x7000cc00, 0x002cc00b, 0x03cc00f3, 0x00c00f30, 0x00000c02, 0x00000000, +0x00d22081, 0x073400d1, 0x64500110, 0x44059d04, 0xc004100b, 0x03811876, +0x1dd00244, 0xd142d050, 0x10082c00, 0x00084009, 0x00c5003b, 0x04400310, +0x00000802, 0x00000000, 0x0024a001, 0x027440d1, 0x64400190, 0x54819900, +0x5009102a, 0x90194234, 0x0dd04344, 0xc1008460, 0x50084400, 0x0024404d, +0x030400c5, 0x04400c10, 0x00000200, 0x00000000, 0x00202010, 0x033400c1, +0x20400810, 0x44009d00, 0x40091402, 0x00010030, 0x0cd00304, 0xc1000040, +0x10022400, 0x0004500c, 0x0004000d, 0x40400014, 0x00000080, 0x00000000, +0x0036a000, 0x037400d3, 0x04c005b0, 0x5d009b00, 0x40013001, 0x00930034, +0x0dd0014d, 0xf30004c0, 0x70804c00, 0x1024c009, 0x034c00d7, 0x00d00d30, +0x00000ac0, 0x00000000, 0xc03ba805, 0x81fe00ff, 0x0fc006f0, 0xfc003f08, +0xc003f081, 0x003f403f, 0x0ff003fc, 0xff000fc0, 0xf000fc00, 0x000bc00b, +0x00fc003b, 0x17c003f1, 0x00000e60, 0x00000000, 0x401fa003, 0x008c003b, +0x2cd00bb0, 0xfc00f300, 0xc00f1103, 0x80f7003c, 0x0f3093fc, 0x33309cc0, +0x3000cc48, 0x1038d02b, 0x03e4c8f3, 0x0cc40ff2, 0x00000e00, 0x00000000, +0x08170801, 0x034640d1, 0x2c440911, 0x7402c140, 0x440d1000, 0x00d0103c, +0x8f512bf0, 0xc1001050, 0x50432c04, 0x40b44024, 0x03440a81, 0x04420fd0, +0x00000c20, 0x00000000, 0x0013a011, 0x00240009, 0x20420cd2, 0x7402c140, +0x480c1203, 0x40c12030, 0x0c100330, 0x01011040, 0x10002400, 0x60b540c4, +0x03040481, 0x44400cd0, 0x00000e80, 0x00000000, 0x0415a803, 0x036420d1, +0x64500d90, 0x7000d100, 0x40181042, 0x00d12834, 0x0d500374, 0xd1001440, +0x50036400, 0x44754005, 0x03440081, 0x0c400dd0, 0x00000620, 0x00000000, +0x009aa802, 0x80e1003b, 0x64c809f0, 0x3800d340, 0xd03d300f, 0x21d72034, +0x0d30037c, 0x13403484, 0x30006c00, 0x20f5c01d, 0x036d0093, 0x00d00de0, +0x00000e20, 0x00000000, 0x401d8007, 0x03df00ef, 0x3bc08b70, 0xfc80ff00, +0xc00ff411, 0x89ff103b, 0x0ef2037c, 0xff003fc4, 0xf003fc00, 0x003ec857, +0x03fd00ff, 0x1fc00ff0, 0x0000060a, 0x00000000, 0x00950802, 0x006c0013, +0xa4404d21, 0x4c00d704, 0xc005300b, 0x48d30034, 0x0d72234c, 0x074033c8, +0x70001c00, 0x2234c015, 0x034d00d3, 0x09c00c10, 0x00000420, 0x00000000, +0x0114a013, 0x0b4c00d1, 0xf4402d10, 0x4400f104, 0x40a5106b, 0x0ad106bc, +0x0f102bc4, 0xd1003740, 0x10834420, 0x00704014, 0x03c400db, 0x4c400f10, +0x00000200, 0x00000000, 0x00728007, 0x48540001, 0x60408d14, 0x1440c540, +0x480c1007, 0x01c14870, 0x0c500744, 0x05001348, 0x50001400, 0x08724804, +0x030400c1, 0x1d400c54, 0x00000a00, 0x00000000, 0x00708004, 0x87b401e1, +0x78409e11, 0xd401e100, 0x431f1007, 0x05e01070, 0x1e102784, 0xe1025b40, +0x1007860d, 0x00ca4016, 0x278401a9, 0x18401e50, 0x00000200, 0x00000000, +0x00301012, 0x001c0003, 0xa4c88d30, 0x1c00c740, 0xc00c3603, 0x00d20030, +0x0c50030d, 0x070013c0, 0x70001c04, 0x4022c064, 0x034c0083, 0x49c00d70, +0x00000040, 0x00000000, 0x023db802, 0x03cd80ef, 0x3fd08ff0, 0xad00ff02, +0x000ef023, 0x24ff003f, 0x0ff083fc, 0xfe281f40, 0xf023fc04, 0x001dd007, +0xc3fc00bf, 0x0bc00fb0, 0x00000648, 0x00000000, 0x0037a015, 0x044c0013, +0x22c00d30, 0x4c00d304, 0xc20d3003, 0x409323b4, 0x0db0134c, 0x130037e1, +0x34004c00, 0x0024c005, 0x834c009f, 0x57c12db6, 0x00000e00, 0x00000000, +0x00318832, 0x03c500c1, 0x38500e12, 0x8401e100, 0x520e1083, 0x00e1253c, +0x2e135304, 0xe10033c0, 0x10030400, 0x00285006, 0x03c400ed, 0x4b402e10, +0x00000620, 0x00000000, 0x08790203, 0x04840121, 0x4e401c11, 0xa409e100, +0x501c9007, 0x01e14178, 0x1e9027a4, 0x29087b40, 0x90048421, 0x00784116, +0x178401cd, 0x0f405e10, 0x00000402, 0x00000000, 0x00372812, 0x032400c1, +0xb2500c16, 0x2520c100, 0x409c9007, 0x09c10030, 0x0c100324, 0xc9403540, +0x90834400, 0x00304414, 0x030500dd, 0x4b480c90, 0x00000c20, 0x00000000, +0x4015a817, 0x014c0043, 0x5ec00510, 0xec805353, 0xc027b585, 0x00510014, +0x05b00165, 0x5b009740, 0x98014d00, 0x221cc017, 0x0144415f, 0x5fc005b0, +0x00000620, 0x00000000, 0x00870012, 0x80dc203f, 0x05c021f0, 0x5c001f00, +0xc5017020, 0x001f0007, 0x0070005c, 0x270407c0, 0x7a00fc40, 0x0007c021, +0x007c081f, 0x4bc00170, 0x00000c00, 0x00000000, 0x00270810, 0x024c0093, +0x24d888f0, 0x4c009340, 0xc0093002, 0x00930024, 0x0930024c, 0x930024c0, +0x73024c00, 0x0164d009, 0x020c009f, 0x43c00930, 0x00000c20, 0x00000000, +0x40262001, 0x8a448091, 0x244239d0, 0x44029b00, 0x5809b402, 0x00934024, +0x0915024d, 0x9b002440, 0x10026c40, 0x10244429, 0x0244009d, 0x07400910, +0x00000800, 0x00000000, 0x00648018, 0x1ac600b1, 0x205009d2, 0x04308140, +0x400c1202, 0x00890820, 0x09100204, 0xb1102660, 0x5902c400, 0x20244018, +0x0244009d, 0x63400914, 0x00000080, 0x00000000, 0x02202010, 0x228400a1, +0x205888d0, 0x06188902, 0x40881022, 0x08890320, 0xc8182204, 0xa9002460, +0x180aa408, 0x1220400c, 0x2205108d, 0x43402810, 0x00000080, 0x00000000, +0x0596b01d, 0x58cd1411, 0x945165d2, 0x0c020305, 0x41611008, 0x020b4184, +0x3132484c, 0x030706e8, 0x7850cc14, 0x241448e0, 0x084e001f, 0x77c00130, +0x00000a40, 0x00000000, 0x0127b819, 0x127c009f, 0x2fc84bf8, 0xfd049f41, +0xc04bf012, 0x04b781a7, 0xc9f0927e, 0x9f0127f0, 0xf0127c24, 0x002fc0cf, +0x1a7e30bf, 0x67c029f0, 0x00000e60, 0x00000000, 0x016f8018, 0x16cc01b3, +0x2cc14bb0, 0x6c069302, 0xc0c9f00a, 0x088b8324, 0x29300a7c, 0x9300a4c0, +0x30226800, 0x003cc00b, 0x0a4c00b3, 0x67c029f0, 0x00000e00, 0x00000000, +0x0307081c, 0x00448013, 0x14444190, 0x440f1103, 0x40351014, 0x011392c4, +0xa01104f4, 0x11000440, 0x50004404, 0x01044041, 0x1c440411, 0x734060d0, +0x00000c22, 0x00000000, 0x0523a010, 0x4b050281, 0x20414810, 0x84008101, +0x40ca5102, 0x04a10128, 0x48101ab4, 0x81002050, 0x10122410, 0x44a04128, +0x0a051281, 0x434068d0, 0x00000e80, 0x00000000, 0x00258818, 0x02448099, +0x24504910, 0xc5009100, 0x404b1042, 0x00b9202c, 0x081082f0, 0x91002442, +0x50024460, 0x0924520c, 0x02442091, 0x634009d0, 0x00000620, 0x00000000, +0x0023a805, 0x024c8093, 0x24c019b4, 0x6c009340, 0xc829f002, 0x21914024, +0x0934027c, 0x934024c8, 0x30026c00, 0x2024c089, 0x024d0193, 0x17c009f0, +0x00000e20, 0x00000000, 0x44258014, 0x027c0087, 0x23c009f6, 0x7c008f40, +0xd008f002, 0x05970027, 0x09f0023c, 0x9f0027c0, 0xf4023c00, 0x0463c019, +0x027c049f, 0x53c009f0, 0x00000600, 0x00000000, 0x08050814, 0x040c001e, +0x05d1813a, 0xfc001300, 0xd023f008, 0x0033000c, 0x013000cc, 0x170000c0, +0x30004c00, 0x2004cd01, 0x004d0013, 0x50c00130, 0x00000420, 0x00000000, +0x049c8014, 0x01ec005d, 0xdc402738, 0x5c005b04, 0x4005d001, 0x00530814, +0x05100144, 0x51001440, 0xb0016c00, 0x005c4137, 0x01440051, 0x50400510, +0x00000200, 0x00000000, 0x00f28014, 0x8004c8cd, 0x3140319a, 0x7600c141, +0x400cd003, 0x00c14034, 0x0c1c8314, 0xd5003060, 0x10030500, 0x4270403c, +0x030409d1, 0x50400c94, 0x00000a00, 0x00000000, 0x00388005, 0x022600ed, +0x38440810, 0x9600e900, 0x4002d000, 0x01390000, 0x4e180094, 0xe1003840, +0x90132404, 0x0030400c, 0x078400c1, 0x14405e90, 0x00000200, 0x00000000, +0x00581015, 0x078c81ef, 0x71d01a94, 0xbc016300, 0x401ff007, 0x01e12078, +0x5e30071d, 0xc70bfcd1, 0x30478c05, 0x0078c812, 0x07cc01e3, 0x54d05eb0, +0x00000040, 0x00000000, 0x2015b810, 0x037c40cf, 0x37c00bf0, 0x7c005f00, +0xd001f000, 0x00170007, 0xedf0006c, 0xdf01b740, 0xf0337c02, 0x0037d001, +0x037c10df, 0x43c8ad70, 0x00000660, 0x00000000, 0x004fa000, 0x26fc09b3, +0x7cc01bb0, 0xcc01fb00, 0xc01f3007, 0x01fb007c, 0x1f3007fc, 0xf3007cd0, +0x3007cc13, 0x087cc01b, 0x07cd01b3, 0x08c03ff0, 0x00000e00, 0x00000000, +0x020d8815, 0x32b40ca1, 0x28402a12, 0xc400e102, 0x40821400, 0x4020400c, +0x0f1000b4, 0xf1003c40, 0x50438400, 0x0038400e, 0x039400a1, 0x54400ed0, +0x00000620, 0x00000000, 0x00090000, 0x829400a5, 0x30400a90, 0x84006940, +0x400e1003, 0x10e10038, 0x0e9003b4, 0xe1423a40, 0x90230400, 0x0438508a, +0x038401a1, 0x00400ed0, 0x00000400, 0x00000000, 0x08072804, 0x06340a81, +0xa4500812, 0x05204102, 0x40001004, 0x00110000, 0x0c900074, 0xc1003240, +0xd213040b, 0x00f44018, 0x03550891, 0x18400cd0, 0x00000c20, 0x00000000, +0x4035a815, 0x4e7c0097, 0xf4c00db0, 0x4c20db40, 0x500d3047, 0x01d30034, +0x0fb40374, 0xf3003ec0, 0xb00fcd02, 0x40749055, 0x03cc0093, 0x54d00ff0, +0x00000620, 0x00000000, 0x00370001, 0x127c108f, 0x37c02df0, 0x7c00df00, +0xc081f010, 0x001f0007, 0x0d72807c, 0xdf0031c0, 0x70037c04, 0x01b7c265, +0x037c009f, 0xa7c00df1, 0x00000c00, 0x00000000, 0x011f0880, 0x038c05b3, +0x1c800930, 0xcc004346, 0xd00c3003, 0x00f3803c, 0x0f3003cc, 0xe3003cc0, +0x32038c00, 0x0034c008, 0x03cc08b3, 0x07c00d30, 0x00000c20, 0x00000000, +0x10462081, 0x03440091, 0xf4403810, 0xc4007100, 0x40031000, 0x0031000c, +0x0d1500c5, 0xdb003440, 0xb2036c00, 0x00f44029, 0x83ec0091, 0x07400d14, +0x00000802, 0x00000000, 0x4004a001, 0x02440091, 0xb7401d10, 0x0400d100, +0x420d1203, 0x00d10030, 0x0c100344, 0xd1003444, 0x10034400, 0x04765009, +0x034400d1, 0x07400d10, 0x00000200, 0x00000000, 0x00002010, 0x06050081, +0x33500810, 0x0400c110, 0x40000200, 0x00012000, 0x0c180006, 0xc9003040, +0x98032500, 0x4012400c, 0x032500c1, 0x43400c10, 0x00000080, 0x00000000, +0x0006b000, 0x024c2093, 0x37c00934, 0x4c005340, 0x480d1403, 0x40d14034, +0x0f30030e, 0xf3403c48, 0x3003cc00, 0x1036d009, 0x034d00e3, 0x07c00f30, +0x00000ac0, 0x00000000, 0x000fb805, 0x02bc00bf, 0x3cc00bf0, 0xfce07f00, +0xe203f200, 0x003f000f, 0x0ff000fc, 0xff003fc0, 0xd003fc80, 0x002dc00b, +0x03fc00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x000f8003, 0x0bcc20b3, +0x3cc40fb4, 0xec00f300, 0xc00f7081, 0x04b3412d, 0x0331538c, 0x37100fc0, +0x30218c00, 0x010fc14e, 0x00fc00a3, 0x0cc00bf0, 0x00000e00, 0x00000000, +0x00071801, 0x2b441019, 0xb4496d30, 0x4402d020, 0x440d1203, 0x0a9b00a4, +0x09500bc4, 0x91251740, 0x100b6430, 0x0007402d, 0x00740091, 0x06c009d0, +0x00000c20, 0x00000000, 0x0013a011, 0x13240409, 0xb2488c10, 0x2442c100, +0x440cd001, 0x01810221, 0x08910374, 0x41016346, 0x54226420, 0x0203400c, +0x02340981, 0x444008d0, 0x00000e80, 0x00000000, 0x0115a007, 0x01640019, +0x36400914, 0x54005104, 0x401d9007, 0x01590225, 0x29d00374, 0xd1407740, +0x50076480, 0x0087400d, 0x037401d1, 0x0c4009d0, 0x00000620, 0x00000000, +0x01538882, 0x476c0193, 0xb6d03d32, 0x2c01d340, 0xc02cf01d, 0x01934025, +0x25b0037c, 0xd72037c0, 0x70070d02, 0x80c7800d, 0x247c2193, 0x084009f0, +0x00000e20, 0x00000000, 0x245d8007, 0x0f5c0237, 0x59c09ff1, 0x4c09ff01, +0xc00f7003, 0x000f006e, 0x8f7003cc, 0xef003fc4, 0xb003dd04, 0x214b880f, +0x00fc00bf, 0x1fd00bf0, 0x00000600, 0x00000000, 0x00950802, 0x033c881f, +0xb4d024b2, 0x4c018b00, 0xc01d3029, 0x059f8024, 0x2db0034c, 0xd32437c0, +0x34034d00, 0x0284c10d, 0x034c081f, 0x08c009f0, 0x00000420, 0x00000000, +0x0834a013, 0x0174421d, 0x56d11110, 0x68009104, 0x40ad1003, 0x02dd0024, +0x2d502bc0, 0xd1403740, 0xb43f4402, 0x0056c04f, 0x174480dd, 0x4ec00dd0, +0x00000200, 0x00000000, 0x80122003, 0x0334139d, 0x70401c18, 0x0400c920, +0x04041000, 0x42cd24a4, 0x2c500750, 0xc10a3745, 0x80852410, 0x8001423c, +0x8024908d, 0x1c4009d0, 0x00000a00, 0x00000000, 0x085a0804, 0x06b4016d, +0x6a42dc18, 0xa42bc100, 0x40971024, 0x01ad0268, 0x16506794, 0x410a7b62, +0x9437e009, 0x006b401e, 0x04a441ad, 0x12401ad0, 0x00000200, 0x00000000, +0x00121012, 0x033c000f, 0x34d06c34, 0x0c08cb00, 0xc08c3020, 0x00cd1230, +0x8cf00315, 0xc3023780, 0xb0332502, 0x0011c04c, 0x932c208f, 0x48c009f0, +0x00000040, 0x00000000, 0x001da802, 0x02fc007f, 0x2fc04f72, 0x7c887f02, +0xc08ef822, 0x00ff223f, 0x0ff04b6c, 0x7f023fc0, 0xf033dc20, 0x003ec10f, +0x03dc00ff, 0x0bc08bf0, 0x00000660, 0x00000000, 0x0013a014, 0x037c8493, +0x34c01df1, 0x0c04d300, 0xc00d7101, 0x00d30024, 0x0934030d, 0x934007c0, +0x33034c12, 0x2024c81c, 0x484c009f, 0x54c019f2, 0x00000e00, 0x00000000, +0x00399912, 0x839c4863, 0x30c20ef1, 0x9410e520, 0xc40a1403, 0x0021102e, +0x02309384, 0xa1001b40, 0x12038500, 0x0028404e, 0x00ad00ad, 0x49400b70, +0x00000620, 0x00000000, 0x40d90000, 0x07b40125, 0x79441cd0, 0xc621c100, +0x601f5105, 0x01c50078, 0x1a1017c4, 0xe1006340, 0x1007c403, 0x0078401e, +0x078403cd, 0x0c401ad0, 0x00000400, 0x00000000, 0x00332016, 0xa7140105, +0xb0404c51, 0x1686c502, 0x78081013, 0x04c50032, 0x88100304, 0xd1003340, +0x123b0422, 0x1230400c, 0x030400cd, 0x49400c50, 0x00000c20, 0x00000000, +0x009ba817, 0x017c0057, 0x55d025d0, 0xcd075340, 0xd0267009, 0x00776014, +0x0530018c, 0x532017c2, 0x348dcc01, 0x009cd005, 0x8144036f, 0x5cd055f0, +0x00000620, 0x00000000, 0x00c58012, 0x001c0813, 0x05c001f1, 0x7c000f21, +0xc011f100, 0x001b0007, 0x0170007c, 0x1f00c380, 0xf4007c10, 0x0047c001, +0x007c101f, 0x4bc00170, 0x00000c00, 0x00000000, 0x40650810, 0x024d00d3, +0x63c00930, 0x4c089300, 0xc0093602, 0x08830060, 0x09300244, 0x930024c0, +0x30024c09, 0x00a7c089, 0x034c009f, 0x43c00932, 0x00000c20, 0x00000000, +0x01262001, 0x02440081, 0x67400914, 0x04019108, 0x40291002, 0x40910ae4, +0x08500244, 0x9100e440, 0x140a4512, 0x00e74209, 0x0a040e9d, 0x07400910, +0x00000800, 0x00000000, 0x0124a01c, 0x02440091, 0x2f480b10, 0x4400b102, +0x581d1002, 0x009100a4, 0x29100654, 0xb1012c50, 0x10020400, 0x0c274809, +0x0a45809d, 0x63400910, 0x00000200, 0x00000000, 0x20202814, 0x52840080, +0x2b414a10, 0x0415a145, 0x40191053, 0x15814520, 0x89502615, 0xa1422840, +0x10220408, 0x00234018, 0x0204008d, 0x43408814, 0x00000080, 0x00000000, +0x0006b01d, 0x104c0012, 0x07c04130, 0x4d043301, 0xc0013010, 0x04130104, +0x6130585c, 0x230594c5, 0x34594c16, 0x0507c541, 0x004c141f, 0x77c02130, +0x00000ac0, 0x00000000, 0x002fa919, 0x5374149d, 0x27c149d0, 0xfc409f05, +0xc00ef003, 0x00bf052f, 0x4bf0126c, 0x9f0127c8, 0xf012bd84, 0x002fc009, +0x027c80bd, 0x67c06bf2, 0x00000e60, 0x00000000, 0x002f8018, 0x8a4c22a3, +0x2dc06b70, 0xcc04b303, 0xc00f3013, 0x00b700ac, 0x29300acc, 0x930137c0, +0x50124c84, 0x012cc04a, 0x03fc04bf, 0x60c669f0, 0x00000e00, 0x00000000, +0x1007181c, 0x18460411, 0x044021b1, 0x54221121, 0xc0013108, 0x02114186, +0x25100c6c, 0x014d8340, 0xb100c400, 0x00044001, 0x0074001d, 0x704041d0, +0x00000c20, 0x00000000, 0x00250012, 0x1a240499, 0xa340c8d0, 0x24028100, +0x40099022, 0x028108a0, 0x68140a44, 0xa1022b40, 0x515aa416, 0x08a24228, +0x0634228d, 0x404028d0, 0x00000e80, 0x00000000, 0x00e50018, 0x02640499, +0x26482951, 0x74029100, 0x50091402, 0x03910026, 0x09100064, 0xa100b740, +0x1613e500, 0x00665c09, 0x067000dd, 0x604019d2, 0x00000620, 0x00000000, +0x00e78805, 0x0a65129b, 0x274839d0, 0x6c429340, 0xc829a10a, 0x01974024, +0x2930004c, 0x930027c0, 0x50126c01, 0x0066c009, 0x0e78069f, 0x14c009f0, +0x00000e20, 0x00000000, 0x40258012, 0x025c2097, 0x21c059a0, 0x1c189f00, +0xc2097002, 0x008f0127, 0x0df0007c, 0x9f0827c0, 0xf0021c04, 0x0025c008, +0x027c499f, 0x53d009f0, 0x00000600, 0x00000000, 0x02450810, 0x084c021f, +0x04d021b2, 0x4c020310, 0xc2113000, 0x00130004, 0x2130107c, 0x130004d0, +0x31284c80, 0x0204c801, 0x204c0b13, 0x50d001f0, 0x00000420, 0x00000000, +0x0898a014, 0x0144056d, 0x16d01550, 0xec907b00, 0x40261045, 0x0a71011c, +0x855404b4, 0x51601440, 0x10854523, 0x005c4087, 0x05c40371, 0x505005d0, +0x00000200, 0x00000000, 0x0072a014, 0x0346514d, 0x32421d98, 0x3401c100, +0x400c5005, 0x0ac12134, 0x0c100734, 0xc1003540, 0x10032491, 0x00304228, +0x971500c1, 0x50400dd0, 0x00000a00, 0x00000000, 0x80388801, 0x138600ad, +0x2a41085c, 0x3600e910, 0x50075001, 0x00e10010, 0x1e5053b4, 0x61403940, +0x1101a402, 0x107c4006, 0x029401c1, 0x14404ed0, 0x00000200, 0x00000000, +0x00781011, 0x0fcd016e, 0x72d01eb2, 0xbc014300, 0xc8167005, 0x01a34068, +0x1c30373c, 0xe30049c0, 0x2407ec01, 0x0878d016, 0x079c01a3, 0x54c03ff0, +0x00000040, 0x00000000, 0x0035a810, 0x077c009f, 0x37c009d0, 0x6c00df20, +0xc004b401, 0x009f2007, 0x2df0037c, 0xdf0036c0, 0xf0135c00, 0x4023c005, +0x026c005f, 0x43c00df0, 0x00000660, 0x00000000, 0x005d2000, 0x07fc01bf, +0x7cc01b30, 0x8c21e300, 0xc0167005, 0x0973004c, 0x3f30478c, 0xf3007cc0, +0x302fbc01, 0x007bc013, 0x07fc01f3, 0x00c01ff0, 0x00000e00, 0x00000000, +0x02391815, 0x03b400ed, 0x28c00b10, 0x8406e502, 0x08061000, 0x00230008, +0x2e100394, 0xe1023840, 0x1131a400, 0x02bb4006, 0x4bb418e1, 0x54408ed0, +0x00000620, 0x00000000, 0x00b90000, 0x233400ad, 0x38400a54, 0xc440e100, +0x41075001, 0x10290008, 0x8c1003c4, 0xe1000840, 0x58c3f400, 0x003f4006, +0x03b400e9, 0x00400ed0, 0x00000400, 0x00000000, 0x20312004, 0x2f3407dd, +0xb0400850, 0x0410c500, 0x40141118, 0x03410000, 0x0c100314, 0xd1003042, +0x500f6402, 0x46334004, 0x07340149, 0x10400dd0, 0x00000c20, 0x00000000, +0x00b5a815, 0x0ffc03df, 0xf4d01d74, 0x0c02d300, 0xc01c700a, 0x08db4034, +0x2d34034c, 0x935174d0, 0x70867c02, 0x00b7c009, 0x033c005b, 0x54d05ff0, +0x00000620, 0x00000000, 0x00370001, 0x437c0adf, 0x27c08ca0, 0x7c02df06, +0xd005f20a, 0x00df0017, 0x6df0037c, 0x1f00b3c0, 0xb00b7c00, 0x0037c005, +0x027c0257, 0x07c40df0, 0x00000c00, 0x00000000, 0x003f0884, 0x038c00ff, +0x20c05f30, 0xcc05b302, 0xc81f3004, 0x01d34014, 0x0f302788, 0xf30034c0, +0x3042cc10, 0x001cc006, 0x07fc1033, 0x00c00d30, 0x00000c22, 0x00000000, +0x01360085, 0x034507dd, 0x26c00d10, 0x4400cb08, 0x4071b040, 0x0b410250, +0x2d508f54, 0xd102b6c0, 0x122f4409, 0x00804e05, 0x0a740251, 0x06c00d12, +0x00000802, 0x00000000, 0x82348001, 0x034601dd, 0x34480914, 0x4400d100, +0x404d1113, 0x42510404, 0x0c100b54, 0xd1003440, 0x11034400, 0xe0344001, +0x217420d1, 0x04400d10, 0x00000200, 0x00000000, 0x00302810, 0x030620cd, +0x22400c12, 0x0400c908, 0x40049002, 0x80410000, 0x0c500314, 0xc1403242, +0x10030400, 0x00304004, 0x007420c1, 0x42400c10, 0x00000080, 0x00000000, +0x0026b000, 0x034c00dd, 0x24c00930, 0x4400d300, 0xc00d3001, 0x00530004, +0x0d30035c, 0xd30034c0, 0x34034500, 0x0014c405, 0x037c00d3, 0x00d00d34, +0x00000ac0, 0x00000000, 0x002fa805, 0x03f000ff, 0x2fc00ff4, 0xfc00ff00, +0xd003e100, 0x007f000f, 0x07d003fc, 0xff003fc0, 0xf003fd00, 0x001fd007, +0x02fc007f, 0x17c00ff0, 0x00000e60, 0x00000000, 0x492fa003, 0x02cd0033, +0x3cc00730, 0xcc203301, 0xc00ff000, 0x003f023c, 0x07f001fc, 0x3f000cc0, +0xf020ec00, 0x000ec083, 0x0bcc02f3, 0x0fc04730, 0x00000e08, 0x00000000, +0x02270801, 0x02840a91, 0xb4480512, 0x44281500, 0x400fd000, 0x001140b0, +0x05d20174, 0xdd203549, 0xd0034480, 0x1034444d, 0x03d400f5, 0x07402550, +0x00000c20, 0x00000000, 0x0023a011, 0x022494c1, 0xb0400011, 0x24000100, +0x4008c100, 0x80cd01b0, 0x44d08134, 0x0d000000, 0xd0102400, 0x00024000, +0x932484c1, 0x47408410, 0x00000e80, 0x00000000, 0x0025a803, 0x226440d1, +0x34400114, 0x44041500, 0x5249d010, 0x40516434, 0x05d00174, 0xd9003502, +0xd0034400, 0x0034600d, 0x037400d5, 0x1f408550, 0x00000620, 0x00000000, +0x0067a802, 0x036c0053, 0x34d12534, 0x6d009300, 0xc80cf001, 0x121f40b4, +0x27f0017c, 0x3f000c40, 0xd000ec00, 0x200ec003, 0x036c00d3, 0x0bc01538, +0x00000e20, 0x00000000, 0x00a58007, 0x039c80ef, 0x37c00ef0, 0xfc813f20, +0xc01df005, 0x053f013f, 0x07f021fc, 0xef003fc8, 0xf203fc00, 0x8037c80e, +0x035c00ff, 0x0fc017f0, 0x00000600, 0x00000000, 0x00350802, 0x027c00d3, +0x35800132, 0x4c009309, 0xd009b200, 0x025742b4, 0x35b0a14c, 0x130004d0, +0x70005404, 0x4007c001, 0x034c00d3, 0x08d00534, 0x00000420, 0x00000000, +0x4034a013, 0x1b7400d1, 0x78451d10, 0x45018104, 0x4029d80d, 0x2b5142f4, +0x05110144, 0xdb003440, 0x10834483, 0x0073400d, 0x03cd00f1, 0x4c400110, +0x00000200, 0x00000000, 0x0036a007, 0x1e340001, 0xf1432410, 0x14090108, +0x4138d21c, 0x00454014, 0x0d900305, 0x01000140, 0x5800140a, 0x20034000, +0x036480d1, 0x1c400410, 0x00000a00, 0x00000000, 0x02788004, 0x26b401a1, +0x78405614, 0x84090160, 0x411ad044, 0x11712058, 0x9c1005c4, 0xc9007840, +0x1007040d, 0x047b401e, 0x078429e1, 0x90401610, 0x00000200, 0x00000000, +0x00301012, 0x023c08c3, 0x31c00030, 0x1c000308, 0xc80cd228, 0x08474020, +0x2cb0010c, 0x03100540, 0x70101c04, 0x1003c800, 0x032c00d3, 0x48c00531, +0x00000040, 0x00000000, 0x003db802, 0x02f400ff, 0x3fc24af0, 0xfc003f10, +0xc08ff000, 0x006d403f, 0x0ff000b4, 0xff003ec4, 0xd003f804, 0x003f400f, +0x03fc10ff, 0x0bc003f2, 0x00000660, 0x00000000, 0x0037a015, 0x127c0057, +0x34ca04b0, 0x4c00d300, 0xc069f018, 0x80530034, 0x0df0817c, 0x134846c0, +0x30004401, 0x4046c001, 0x374c14d3, 0x47c00fb2, 0x00000e00, 0x00000000, +0x003d8812, 0x433400e1, 0x78440e14, 0x84006501, 0x0508d021, 0x80610038, +0x0fd001b4, 0xf1003940, 0x10039400, 0x0030420f, 0x239400f1, 0x4b400e10, +0x00000620, 0x00000000, 0x00790003, 0x0eb401e5, 0x78401390, 0x8601f102, +0x081ed005, 0x01690062, 0x1ed005b4, 0x21804040, 0x11842401, 0x004a4012, +0x178405e9, 0x0f421e90, 0x00000400, 0x00000000, 0x00372812, 0x833000d1, +0x30448c10, 0x0400c500, 0x400cd007, 0x02490136, 0x0cd00834, 0xc1003060, +0x18037640, 0x0030400c, 0x031600c9, 0x4b400c90, 0x00000c20, 0x00000000, +0x0055a817, 0x017c1175, 0x145027b0, 0x4c407300, 0xd005f01d, 0x007b401e, +0x05f029fc, 0x532014c2, 0x35816c00, 0x0016c005, 0x014c005b, 0x5fc077b0, +0x00000620, 0x00000000, 0x02070012, 0x087c001f, 0x07c101f6, 0x7d011f20, +0xc001f240, 0x00175805, 0x00f0007c, 0x3f000dd0, 0xf000dd40, 0x200fc802, +0x007c0007, 0x4be01170, 0x00000c00, 0x00000000, 0x00270830, 0x024c0197, +0x20800834, 0x4f009300, 0xe008f017, 0x12930024, 0x0930024c, 0x9f0024c0, +0x70027c40, 0x0026c009, 0x024c009f, 0x41c008f0, 0x00000c20, 0x00000000, +0x00262001, 0x0a440291, 0x24400912, 0x44028100, 0x4009d006, 0x02814224, +0x09340604, 0x9d0024c0, 0x30025c02, 0x00244009, 0x0244009d, 0x044009d0, +0x00000800, 0x00000000, 0x00242018, 0x0a040a95, 0x25400910, 0x44819100, +0x4829d042, 0x00910024, 0x19100745, 0xbd002c50, 0x5202d410, 0x002c400b, +0x0245009d, 0x61400dd0, 0x00000200, 0x00000000, 0x02202010, 0x22040881, +0x21500810, 0x04008108, 0x4048d002, 0x0081c230, 0x88110204, 0xad202860, +0x10229408, 0x0028502a, 0x9204048d, 0x404008d8, 0x00000080, 0x00000000, +0x0586901d, 0x584d5617, 0x05c14130, 0x44105101, 0x5051f000, 0x94514504, +0x6131004c, 0x1f3504c1, 0x70505c56, 0x050cc141, 0x2c0c0b1f, 0x75c1e1f0, +0x00000ac0, 0x00000000, 0x812fb819, 0x12f404bf, 0x22c00bf2, 0x7c00af42, +0xc08bf042, 0x00be012f, 0x49f002fc, 0x9f0425c0, 0x70125c04, 0x0027c249, +0x267c099f, 0x67c40bf0, 0x00000e60, 0x00000000, 0x01a7a018, 0x1acc02b7, +0x26c10830, 0x7c10f301, 0xc04bf002, 0x00b7042c, 0x5bf002cc, 0xb304e4c1, +0x30264c19, 0x016c0419, 0x42441493, 0x63c04930, 0x00000e00, 0x00000000, +0x0187081c, 0x18440611, 0x0c400118, 0x74021100, 0x4021d050, 0x00110584, +0x01d10044, 0x11018440, 0x10004402, 0x080442c1, 0x08440611, 0x73414110, +0x00000c20, 0x00000000, 0x00a3a010, 0x0b240285, 0xaa400b10, 0x34008100, +0x4088d002, 0x0185c060, 0x48d00604, 0x81092049, 0x141a0404, 0x42204108, +0x52051081, 0x43402814, 0x00000e80, 0x00000000, 0x0125a818, 0x86640091, +0x2c484910, 0x74009148, 0x408dd00a, 0x60912064, 0x09d00345, 0x91402400, +0x10824400, 0x08244209, 0x0264a081, 0x63401910, 0x00000620, 0x00000000, +0x00678805, 0x026c0797, 0x26806914, 0x74109300, 0xd019f006, 0x02974024, +0x09f10a44, 0x930024d0, 0x30024c00, 0x2024c009, 0x024c2093, 0x17c00930, +0x00000e20, 0x00000000, 0x00658014, 0x025d049f, 0x22c009f6, 0x7c009f00, +0xc118f00b, 0x098f0023, 0x09f00a7c, 0x8f0023c0, 0xf1023d00, 0x1023d008, +0x025c009f, 0x53c009f0, 0x00000600, 0x00000000, 0x00050814, 0x047c0407, +0x0c400330, 0x7c2a1310, 0xd0013000, 0x00132004, 0x01f0a47c, 0x112004c2, +0xf0006d00, 0x1007c001, 0x004c001f, 0x53c00130, 0x00000420, 0x00000000, +0x0014a014, 0x01f40371, 0x16c00410, 0x74097100, 0x4507102d, 0x0971001c, +0x05d109dc, 0x71001452, 0xd0016d11, 0x00574005, 0x0144005d, 0x53400510, +0x00000200, 0x00000000, 0x8036a014, 0x037400c5, 0x31400c10, 0x3442c120, +0x400c1824, 0x41c904a0, 0x0dd80334, 0xc1403140, 0xd0032406, 0x0073600c, +0x034400cd, 0x53400c10, 0x00000a00, 0x00000000, 0x02388005, 0x03b482e1, +0x03480214, 0xf480a100, 0x400a1006, 0x1029401c, 0x0ad00394, 0xe9103940, +0xd003a440, 0x043b40ce, 0x13044ced, 0x17408e10, 0x00000200, 0x00000000, +0x007c1015, 0x073c81e7, 0x79421e30, 0xbc016308, 0x40143407, 0x012b1858, +0x1bf006bc, 0xf301f1c4, 0xf007ac01, 0x007bc05e, 0x178d05cf, 0x57c05e34, +0x00000040, 0x00000000, 0x0035b810, 0x037c00dd, 0x06c201f0, 0x3c405f40, +0xd001f003, 0x00170033, 0x0df1037c, 0xd70036d0, 0xf1277d00, 0x0037c00d, +0x2b7c16df, 0x43c00df0, 0x00000660, 0x00000000, 0x007fa000, 0x07cd01f3, +0x7cc2df32, 0xfc013300, 0xc012f00e, 0x01330869, 0x1ff005fc, 0xfd107cc0, +0x1007e809, 0x007f849f, 0x0fcc01f3, 0x02c01f20, 0x00000e00, 0x00000000, +0xa03d8815, 0x03c40aeb, 0x08418214, 0xb4002100, 0x4002d013, 0x02212038, +0x4bd001a0, 0xfd013848, 0x1003c41c, 0x002a418e, 0x038400e1, 0x44400e10, +0x00000620, 0x00000000, 0x40390000, 0x23840021, 0x38044e10, 0xb4002102, +0x4002d843, 0x0029023c, 0x0ad061b4, 0xe9303a60, 0x90038400, 0x0033408e, +0x032500e1, 0x02400e10, 0x00000400, 0x00000000, 0x00332804, 0x05000401, +0x01403010, 0x34020100, 0x4000d003, 0x00094030, 0x0dd00134, 0xcd00b242, +0x942b0402, 0x0322402c, 0x032400c1, 0x10400c10, 0x00000c20, 0x00000000, +0x003da815, 0x474802d1, 0x34900d30, 0x7c02d300, 0xd00cf006, 0x0a1b0034, +0x0de00c3c, 0xdb21fec2, 0xb003cc4b, 0x40b7c0bf, 0x03e400f1, 0x56c00e34, +0x00000620, 0x00000000, 0x00370001, 0x837c20df, 0x06c801f0, 0x7c429f40, +0xc029f002, 0x42170227, 0x09f0106c, 0xcf0131c0, 0x70431d02, 0x0036c00c, +0x035d00df, 0x07c01df0, 0x00000c00, 0x00000000, 0x003f0880, 0x07bc40ff, +0x34c10c30, 0xfe30ff20, 0xc0073057, 0x44338038, 0x0b1084fc, 0xfb003ce2, +0xb003cc00, 0x017c800f, 0x03cc00df, 0x00d00df0, 0x00000c22, 0x00000000, +0x00362081, 0x077422dd, 0x04400114, 0x7402dd08, 0x4001104f, 0x031b0634, +0x0d100874, 0xd10036c0, 0x30034441, 0x0034420d, 0x036c80d7, 0x04400dd0, +0x00000802, 0x00000000, 0x0034a001, 0x2374211d, 0x34400d14, 0x34021d00, +0x40011003, 0x00111034, 0x0d501274, 0xd9103450, 0x50034403, 0x0035400d, +0x034400cd, 0x04400dd0, 0x00000200, 0x00000000, 0x00302010, 0x0234000d, +0x00400010, 0x36000d00, 0x40001402, 0x80012000, 0x08560234, 0x01003040, +0x1c030600, 0x0031640c, 0x032600cd, 0x40400dd0, 0x00000080, 0x00000000, +0x1036b000, 0x037c000f, 0x34c00d14, 0x74801f00, 0xd0013003, 0x00110014, +0x0954027c, 0xdb003440, 0x30034d00, 0x0035d00d, 0x03cc00ff, 0x00c00df0, +0x00000ac0, 0x00000000, 0x203fb205, 0x00fc003c, 0x0fc403f1, 0xfc003f00, +0xc20bf003, 0x003f203f, 0x0fb102fc, 0xbf003fd0, 0x7003fc00, 0x003ec00f, +0x03fc00f7, 0x17c00ff0, 0x00000e60, 0x00000000, 0x000ba003, 0x03cc82e3, +0x3fc00f34, 0x8c04f308, 0xc0033000, 0x34b320bf, 0x4f3203cc, 0x33003fc0, +0x3073ec04, 0x030cc023, 0x73cc083f, 0x0fc28f30, 0x00000e00, 0x00000000, +0x20070801, 0x0b442051, 0x97400d10, 0x452ad11a, 0x420d1103, 0x029b003f, +0x4d143b44, 0x11203349, 0x100bf428, 0x03a54c01, 0x1b54449d, 0x07484d14, +0x00000c20, 0x00000000, 0x4203a011, 0x23148491, 0x33480c58, 0x0400c100, +0x40001000, 0x808d0133, 0xcc104351, 0x01403340, 0x10132400, 0x00214049, +0x0304004d, 0x47400c55, 0x00000e80, 0x00000000, 0x1045a803, 0x03560611, +0x37423d5c, 0x5600d100, 0x400d5003, 0x03dc0037, 0x0d100344, 0x51003344, +0x10037424, 0x10254029, 0x0354081d, 0x0f400d50, 0x00000620, 0x00000000, +0x0047a802, 0x071d0113, 0x27c62d70, 0x4d005300, 0xc4013000, 0x019f0037, +0x0d30035c, 0x130037c0, 0x32036c05, 0x2025d020, 0x014c201f, 0x0bc00d74, +0x00000e20, 0x00000000, 0x082d8007, 0x936c01bf, 0x17880fb2, 0xec894f08, +0xc30fb003, 0x008a203f, 0x0ff083fc, 0x3f083fc0, 0xf003fc00, 0x212fcb33, +0x0bfe01bf, 0x1fc00fb2, 0x00000600, 0x00000000, 0x00050802, 0x234d0013, +0x28c05d30, 0x4c00f302, 0xc01131a0, 0x069b0037, 0x0d30035c, 0xd34036c0, +0x32034c02, 0x0024d02d, 0x034d0153, 0x0bc00d30, 0x00000420, 0x00000000, +0x20308013, 0xaf400081, 0xb4002d24, 0x050ad102, 0x403d1007, 0x0250403f, +0x0f340390, 0xd1003c42, 0x1003c5a0, 0x0064001d, 0x03542391, 0x4f400d00, +0x00000200, 0x00000000, 0x0022a007, 0x0e240001, 0x70422c10, 0x22088100, +0x48200080, 0x00c50037, 0x0c900304, 0x01003200, 0x10034448, 0x03804004, +0x13448415, 0x1f400c00, 0x00000a00, 0x00000000, 0x60488004, 0x27a441b1, +0x58401614, 0xa485e100, 0x601e120f, 0x0165427b, 0x9e108794, 0x40407840, +0x10070405, 0x02604016, 0x261481a5, 0x13001e10, 0x00000200, 0x00000000, +0x02a01012, 0x022c20c3, 0x30c00c30, 0x2c008100, 0xc0003020, 0x208f4037, +0x0db0234c, 0x830032c0, 0x32030c00, 0x0220d14d, 0x230c8257, 0x4bc04c34, +0x00000040, 0x00000000, 0x200db802, 0x03dc00ff, 0x3fc80af0, 0xdc04ff40, +0xc80fd083, 0x407b00bf, 0x0ff003fc, 0xbf203fc1, 0xd003fc84, 0x223fc00f, +0xa3fc80bb, 0x0bc00ff4, 0x00000660, 0x00000000, 0x0027a015, 0x025c01df, +0x2dc20d70, 0x4c201340, 0xd011f000, 0x21d30af5, 0x1df0034c, 0x530037c0, +0xf3134c01, 0x0024c80c, 0x014c0013, 0x57c20d30, 0x00000e00, 0x00000000, +0x00298812, 0x038400fd, 0x18600e14, 0x86004108, 0x400e9103, 0x00e1003c, +0x0ed00384, 0x4101bc40, 0xd0c38400, 0x102c400e, 0x02c480b1, 0x4b400e50, +0x00000620, 0x00000000, 0x00790003, 0x0694816d, 0x61401f51, 0x8401a120, +0x4012d004, 0x8181813a, 0x5cd83784, 0xa1007940, 0xd8278451, 0x006b401f, +0x07848321, 0x0f401e58, 0x00000400, 0x00000000, 0x88332812, 0x030682cd, +0x30400850, 0x0424c100, 0x420c9003, 0x10c18032, 0x0cd10305, 0x81403040, +0xd0030423, 0x0037413c, 0x13450191, 0x4b400c50, 0x00000c20, 0x00000000, +0x015da817, 0x015c047f, 0x1dc02670, 0x4dc07301, 0xc005f081, 0x02730017, +0x05f001cc, 0x730015c0, 0xf0014d00, 0x001fd106, 0x01cc0273, 0x5fc00570, +0x00000620, 0x00000000, 0x00470012, 0x007c041f, 0x07c011a0, 0xfc001f10, +0xc203f000, 0x021f5005, 0x01f0007c, 0x1f0005c0, 0xf0007c00, 0x44044421, +0x007c011f, 0x4bc001d0, 0x00000c00, 0x00000000, 0x00670810, 0x023c009f, +0x20c00934, 0x44008300, 0xc009e002, 0x00930223, 0x8900023c, 0x930027e0, +0x20023c10, 0x08a4c009, 0x024c029b, 0x43c20931, 0x00000c20, 0x00000000, +0x22662001, 0x0274009d, 0x24400910, 0x45009140, 0x4029d002, 0x06918827, +0x09340274, 0x91002740, 0xf0027400, 0x08e55849, 0x0a6c0391, 0x074009b0, +0x00000800, 0x00000000, 0x0024a018, 0x2a74009d, 0x24400914, 0x54009548, +0x600bd002, 0x00910027, 0x09500274, 0x91002340, 0x50027440, 0x04246009, +0x06448099, 0x63600910, 0x00000200, 0x00000000, 0x25202010, 0x5234208d, +0x20400910, 0x95148505, 0x621ad002, 0x14810023, 0x48502234, 0x81022341, +0x90123400, 0x02216089, 0x26244981, 0x43402890, 0x00000080, 0x00000000, +0x0106b01d, 0x107c401f, 0x04c00131, 0x5c041501, 0xc002f000, 0x04110007, +0x4170587c, 0x11058740, 0x70047c14, 0x0584c021, 0xd84c821b, 0x77c20130, +0x00000ac0, 0x00000000, 0x082fb819, 0x02bc14bf, 0x2f500af4, 0x6c00bb20, +0xc149f002, 0x00bf05a7, 0x4930127c, 0xbf4127c1, 0xf1727c20, 0x492f404b, +0x12fc84bf, 0x67c009f2, 0x00000e60, 0x00000000, 0x012da018, 0x02fc029f, +0x25c00870, 0x4c049301, 0xc02bf082, 0x04bf01af, 0x6b304a7c, 0xb30127c0, +0xf0124c04, 0x00a8c00b, 0x4acc00b3, 0x63c62930, 0x00000e00, 0x00000000, +0x0087081c, 0x0874045d, 0x84420112, 0xc4021118, 0x4001d000, 0x001d0107, +0x41100874, 0x11158744, 0xd2104402, 0x21044041, 0x8854b411, 0x73400110, +0x00000c20, 0x00000000, 0x02332010, 0x0a36008d, 0x69400b50, 0x8409a112, +0x4048d002, 0x038d00a3, 0x28141234, 0x81422340, 0xd04a0509, 0x21e07108, +0x12248081, 0x4340081c, 0x00000e80, 0x00000000, 0x2225a818, 0x0274019d, +0x2c586910, 0xc000b120, 0x4009d002, 0x049d0027, 0x09100274, 0x91002740, +0xd0824420, 0x00644209, 0x0a741891, 0x63400918, 0x00000620, 0x00000000, +0x0027a805, 0x023c029f, 0xa5c02870, 0x4d029340, 0xc009e002, 0x009f0027, +0x0930027c, 0x934027c0, 0xf1024c2a, 0x4024c109, 0x066c0093, 0x17c20930, +0x00000e20, 0x00000000, 0x00258014, 0x027c009f, 0x27c009f0, 0x7c409e00, +0xc009f002, 0x009f0027, 0x09f0027c, 0x8f0027c0, 0xf0027c01, 0x0427c039, +0x0a5c819f, 0x53c009f0, 0x00000600, 0x00000000, 0x00050814, 0x404c821b, +0x08c00331, 0xcd002340, 0xc001f000, 0x00130004, 0x01f2004c, 0x130007c2, +0xf0007c00, 0x0000d001, 0x404cba13, 0x53c00130, 0x00000420, 0x00000000, +0x229ca014, 0x01c42051, 0x14420510, 0x45005100, 0x4007d001, 0x00714014, +0x05d0016c, 0x51001740, 0xd2017401, 0x301cc017, 0x05d4c271, 0x53400510, +0x00000200, 0x00000000, 0x00b2a014, 0x032400c9, 0x30600c14, 0x1400c100, +0x500cd003, 0x01c10020, 0x0cd00344, 0x81003740, 0xd0033401, 0x00724129, +0x0f4402d1, 0x53400c90, 0x00000a00, 0x00000000, 0x00788005, 0x032405e1, +0x08420318, 0x94002100, 0x411ad800, 0x03410068, 0x0ed01384, 0xe1803340, +0xd013b410, 0x443c410a, 0x43940021, 0x17408e91, 0x00000200, 0x00000000, +0x00781015, 0x07ad05fb, 0x70501e30, 0x9c01c300, 0xc017f007, 0x0163007c, +0x1ef10f85, 0xa3407bc0, 0xf00fbc01, 0x087ac01b, 0x06cc81f3, 0x57c01eb4, +0x00000040, 0x00000000, 0x0031b810, 0x005c89df, 0x07c001f0, 0x6e001f00, +0xc009f000, 0x00df2037, 0x0df0037c, 0xdf07b7c0, 0x90077c00, 0x0017c001, +0x027c003f, 0x43c00d70, 0x00000660, 0x00000000, 0x007fb000, 0x17fc01ff, +0x7cc01f70, 0x8c01f340, 0xc01b3007, 0x01f7006c, 0x1f3007cc, 0xf3007fc0, +0x3007cc01, 0x087cc01f, 0x07cd01ff, 0x03c21f34, 0x00000e00, 0x00000000, +0x00398015, 0x11b480ed, 0x08418210, 0x86082102, 0x480b1000, 0x84610028, +0x0e100390, 0xa3003b48, 0x1003c400, 0x00395006, 0x4bc4002d, 0x57400e10, +0x00000620, 0x00000000, 0x00290000, 0x73b400ed, 0x30400e50, 0xc500c100, +0x40021003, 0x20650030, 0x0e100380, 0xe9003b40, 0xd0238400, 0x0618400f, +0x008408fd, 0x03400e50, 0x00000400, 0x00000000, 0x04e32804, 0x003400cd, +0x00400012, 0x04000100, 0x40281000, 0x40c10034, 0x0c100314, 0x81403742, +0xd0030421, 0x00914404, 0x0c04025d, 0x13400d50, 0x00000c20, 0x00000000, +0x00b5a815, 0x073c00ff, 0xf4d0ac70, 0x4c01d300, 0xc07d310b, 0x09870034, +0x0934038c, 0xdb003fc0, 0xf403cd24, 0x00b4c009, 0x0b4f909f, 0x57c00f70, +0x00000620, 0x00000000, 0x02370001, 0x027c10df, 0x43c001f0, 0x7c011f04, +0xc04df404, 0x039f0037, 0x09f0037c, 0x9f0037c4, 0x10037c00, 0x2027c00d, +0x017c001f, 0x07c00d80, 0x00000c00, 0x00000000, 0x003b0880, 0x037c80f3, +0x3cc00f31, 0xc500e300, 0xc8073043, 0x0593107c, 0x0f30034e, 0xc3003fc0, +0xf003cd03, 0x043cc00b, 0x42cc903f, 0x00c20f30, 0x00000c22, 0x00000000, +0x04b60081, 0x047440d1, 0x06c00110, 0x04001170, 0x400d1000, 0x90950234, +0x1d120344, 0x91003744, 0xd1034400, 0x0057d414, 0x0c04411d, 0x05400d10, +0x00000802, 0x00000000, 0x8014a001, 0x237400d1, 0x34420d11, 0x5500d100, +0x40181003, 0x40d14034, 0x89100344, 0xd1803540, 0xd2034400, 0x0074401d, +0x0644019d, 0x04400d10, 0x00000200, 0x00000000, 0x00102010, 0x003400c1, +0x02400110, 0x54000100, 0x40081000, 0x00c50030, 0x08100344, 0x81103340, +0xd0030400, 0x08335004, 0x0044410d, 0x41400c10, 0x00000080, 0x00000000, +0x4006a000, 0x037c00d3, 0x34c00d34, 0x5c00d300, 0xc8013003, 0x00d31034, +0x0d320344, 0xd34039c4, 0xf0034c00, 0x2014d00d, 0x004d009f, 0x00c00d34, +0x00000ac0, 0x00000000, 0x200fa805, 0x00fc00ff, 0x0fc002f0, 0xec003f00, +0xc00bb080, 0x00ff003f, 0x0ff083fc, 0xb9003fc0, 0xf203fc00, 0x001fc006, +0x00fc002f, 0x17c00ff2, 0x00000e60, 0x00000000, 0x002ba003, 0x0084807b, +0x2ec00f21, 0xec00f348, 0xc00f1501, 0x003300bf, 0x0b3410cc, 0x33040cd0, +0x3023cc00, 0x00bcc02f, 0x03cc06f3, 0x0cc10f30, 0x00000e00, 0x00000000, +0x04a70821, 0x034490dd, 0x24420b10, 0x5400d104, 0x400d1008, 0x140102a3, +0x29100844, 0x1105b4c0, 0x50134416, 0x203d480f, 0x43c402f1, 0x04402d50, +0x00000c20, 0x00000000, 0x0323a011, 0x0025048d, 0x26400c90, 0x6400c101, +0x400c1023, 0x80010923, 0x61902020, 0x41400041, 0x90030408, 0x4930404c, +0x03250cc9, 0x44400c90, 0x00000e80, 0x00000000, 0x0125a803, 0x236400dd, +0x27401d91, 0x7400d180, 0x480d1000, 0x14011033, 0x05900064, 0x41063640, +0xd0034400, 0x0035400c, 0x036400d9, 0x0c400cd0, 0x00000220, 0x00000000, +0x0037a800, 0x4c6c019f, 0x22900db1, 0x6c40d300, 0xc00d3000, 0x00930027, +0x21b83c2d, 0x1300c0c1, 0xb003cd02, 0x0030c00d, 0x034c00db, 0x08c00db0, +0x00000e20, 0x00000000, 0x003d8087, 0x07dc15ff, 0x6cc40a70, 0xdc00ff02, +0xc00fe001, 0x003f002f, 0x0b7000dc, 0x7f080dc8, 0x70033c09, 0x003fc00d, +0x03d800f7, 0x1fc10f72, 0x00000600, 0x00000000, 0x00350802, 0x405cc093, +0x25c08d70, 0x5c00d300, 0xd00d3000, 0x001b2034, 0x3130a04c, 0xbb008ec0, +0xb0034d02, 0x0034d00d, 0x031c00cf, 0x08d00d70, 0x00000420, 0x00000000, +0x0874a013, 0x03444791, 0x24481d10, 0x0400f100, 0x400f1003, 0x301100b4, +0x04100044, 0x5d003440, 0x1083c402, 0x003eca0f, 0x03c400fd, 0x4c402f10, +0x00000200, 0x00000000, 0x11e2a003, 0x045403d1, 0x21400cd1, 0x1400d140, +0x400c1101, 0x000d04b0, 0x00904005, 0x8d800240, 0x90030418, 0x2030400c, +0x031400cd, 0x1c400c50, 0x00000a00, 0x00000000, 0x46688000, 0x548401f1, +0x6940bc90, 0x8401e900, 0x401e1004, 0x01212368, 0xb29404c4, 0xad0a7864, +0x12078401, 0x007a401e, 0x078429ed, 0x10501e10, 0x00000200, 0x00000000, +0x00201016, 0x001c0a83, 0x25c48cf2, 0x5c08d300, 0xc00d3403, 0x080f0120, +0x09b00005, 0xdf0202c0, 0xb0130c0e, 0x0030c04c, 0x035c00df, 0x48c00d70, +0x00000040, 0x00000000, 0x4835b200, 0x117c60cd, 0x26c2ad60, 0x7c30d700, +0xc08df200, 0x001f4137, 0x0d70223c, 0xdf023780, 0xf0037c28, 0x0037c00d, +0x237c60df, 0x0bc18dd0, 0x00000660, 0x00000000, 0x0077a015, 0x007400d3, +0x34d02c21, 0x7c00df00, 0xc00db000, 0x80134023, 0x013000fc, 0x33007ec2, +0xf0037c00, 0x033484cd, 0x334d1cd1, 0x54c00df2, 0x00000e00, 0x00000000, +0x00298810, 0x00f480e1, 0x38400e10, 0xb424ed00, 0x4c4e1001, 0x0061002b, +0x025000b4, 0xa1003d00, 0xd083b400, 0x043c42cf, 0x539400e5, 0x48405ed0, +0x00000620, 0x00000000, 0x00710083, 0x04b401a1, 0x78401f10, 0xf041ed00, +0x449e9904, 0x0121047f, 0x131005b4, 0xe1004840, 0xd007b401, 0x0138401e, +0x078485c1, 0x0c501ed0, 0x00000400, 0x00000000, 0x40732812, 0x813400c1, +0x34420c10, 0x7400cd02, 0x400c103f, 0x21010073, 0x0c100634, 0xc101f040, +0xd0033407, 0x0030420c, 0x031400c5, 0x48400cd0, 0x00000c20, 0x00000000, +0x045da816, 0x01fc1171, 0x54c00634, 0xfc007f00, 0xc005b009, 0x9373201f, +0x273485fc, 0x7341dcc0, 0xf0817c07, 0x0014d005, 0x014c0053, 0x5cc005f0, +0x00000620, 0x00000000, 0x00070012, 0x007c921f, 0x07c021f2, 0x7c001f00, +0xc001f000, 0x011f0007, 0x11f0287c, 0x0f0005d0, 0xf1007c02, 0x0007c000, +0x003e001f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x02260810, 0x067c009f, +0x24c40920, 0x7c009340, 0xc8093002, 0x009b0027, 0x1830027c, 0x9f0026c0, +0xf2024c01, 0x0020c009, 0x024c0093, 0x40d09872, 0x00000c20, 0x00000000, +0x01a62000, 0x2634009d, 0x24420910, 0x74009b08, 0x40091002, 0x80910027, +0xb9b06e34, 0x9d082442, 0xd0024451, 0x08244009, 0x82440091, 0x04403910, +0x00000800, 0x00000000, 0x00248018, 0x0274189d, 0x24401951, 0x74009100, +0x40091002, 0x80990063, 0x09100274, 0x9d002760, 0xd0024404, 0x40245409, +0x02440081, 0x60400950, 0x00000200, 0x00000000, 0x02202010, 0x2676088d, +0x20400850, 0x34088102, 0x44881022, 0x08812263, 0x98902234, 0x8d002048, +0xd0020548, 0x01204048, 0x32040481, 0x40408812, 0x00000080, 0x00000000, +0x0586b01c, 0x087c821f, 0x84d14574, 0x7c161100, 0xc1613458, 0x221b0587, +0x6134587c, 0x1f3787c1, 0xf2784c36, 0x22c4c8b1, 0x0c4d0b13, 0x74c16470, +0x00000ac0, 0x00000000, 0x012fb839, 0x12fc04bf, 0x2fc00bb0, 0xbc049f01, +0x8849f113, 0x84bf412f, 0x4a7012bc, 0xbf106fc0, 0xf0867c24, 0x0267c099, +0x327c099f, 0x67c049f0, 0x00000e60, 0x00000000, 0x01afa018, 0x02c402b3, +0xa4c04b20, 0x0c269348, 0x40693012, 0x02b30227, 0x2be0123c, 0xb111a7c0, +0xf1367c04, 0x0023c009, 0x4a4c1493, 0x60c06a30, 0x00000e00, 0x00000000, +0x0987089c, 0x00440451, 0x0460a112, 0x44261109, 0x40611058, 0x00114283, +0x24710e64, 0x11018700, 0xd0207414, 0x038748a1, 0x18540601, 0x70404110, +0x00000c20, 0x00000000, 0x40a7a010, 0x42240099, 0x22520990, 0x25029100, +0x4028100a, 0x80811127, 0x68d02234, 0x81032340, 0xd0123402, 0x04234148, +0x12241081, 0x40406890, 0x00000e80, 0x00000000, 0x0935a818, 0x02648899, +0x26400994, 0x64009100, 0x40091002, 0x20910827, 0x09512864, 0x91562240, +0xd2027494, 0x08274009, 0x0274a091, 0x62400990, 0x00000620, 0x00000000, +0x0027a801, 0x866d039b, 0x264019b1, 0x6c009300, 0xc0093402, 0x18930067, +0x09f0007c, 0x9300a7c2, 0xf0027c00, 0x4027c809, 0x026c0093, 0x14d009b4, +0x00000e20, 0x00000000, 0x10658014, 0x0a5d0097, 0x25c44970, 0x1c009f10, +0xc009f102, 0x009f0237, 0x39f0056c, 0x9f0067c0, 0xf2027c20, 0x2023c009, +0x021c009f, 0x51d00870, 0x00000600, 0x00000000, 0x04050814, 0x107c821f, +0x06c40131, 0x7c001f00, 0xd0013008, 0x02130404, 0x6130406c, 0x134084c0, +0x60007c2a, 0x0004c001, 0x004c0003, 0x50c00130, 0x00000420, 0x00000000, +0x0498a014, 0x01f4236d, 0x14500610, 0xf6004d00, 0x44051401, 0x00512090, +0x07101d44, 0x71001ec0, 0x10017400, 0x401c4005, 0x816c2051, 0x504207b0, +0x00000200, 0x00000000, 0x00b2a014, 0x03749bcd, 0x34400c11, 0x3400cd20, +0x400c9003, 0x00c100f2, 0x1c140324, 0xc1003040, 0x50033407, 0x0030400c, +0x030400c1, 0x50440492, 0x00000a00, 0x00000000, 0x00288005, 0x02f400ed, +0x38410f10, 0xb404ed11, 0x401c9203, 0x01b100ba, 0x3e102784, 0xe1013040, +0x10073400, 0x0138601e, 0x032501e1, 0x14400e90, 0x00000200, 0x00000000, +0x00781015, 0x07bc81ef, 0x78c41634, 0xb403ef00, 0xc01eb017, 0x01e3404a, +0x1e3027ac, 0x21027850, 0x7017bc01, 0x00f8c15c, 0x1f8c02e3, 0x54c09ab0, +0x00000040, 0x00000000, 0x1005b810, 0x003c001f, 0x35c000f1, 0x7c00df02, +0xc88d5023, 0x028e0885, 0x0ff01b7c, 0x3f0b37c8, 0xf0237c00, 0x01b7d0ed, +0x075c14df, 0x43d069f0, 0x00000660, 0x00000000, 0x406fa202, 0x37cc0973, +0x7f401f21, 0xc801f302, 0xc01ff007, 0x03f300ec, 0x1b7027cc, 0xf3207cc0, +0x100fcc21, 0x40fcc0df, 0x07cc01f3, 0x00c017f0, 0x00000e00, 0x00000000, +0x000d8815, 0xb0848aa1, 0x3f444e10, 0x9400e000, 0x400ed003, 0x84a10128, +0x07507384, 0xe1013941, 0x10038400, 0x0138488e, 0x039400f5, 0x54408ed0, +0x00000620, 0x00000000, 0x00390018, 0x33c60861, 0x3a41061a, 0x8400e150, +0x400ed003, 0xc1e0441a, 0x0b400384, 0x61003840, 0x10038410, 0x9038444e, +0x038400e1, 0x00501ad0, 0x00000400, 0x00000000, 0x00032806, 0x04050101, +0x33401410, 0x0400c100, 0x000cd007, 0x12910012, 0x38100b44, 0x01427140, +0xd0034413, 0x0830400c, 0x031500c4, 0x104008c0, 0x00000c20, 0x00000000, +0x1875a81d, 0x4a448591, 0x3ec01d05, 0xcd00f300, 0xd20ff01f, 0x93934016, +0x15700f4d, 0xd300bcc2, 0x3403cd03, 0x003cd00f, 0x03cc00f3, 0x54c001d0, +0x00000620, 0x00000000, 0x00670001, 0x027c229f, 0x37c42df1, 0x7ca0df00, +0xc00df003, 0x00df0015, 0x2ff0837c, 0xff0033c1, 0x30037c02, 0x2033c20c, +0x032c00df, 0x07c009f0, 0x00000c00, 0x00000000, 0x103f0801, 0x43cc187f, +0x3cd40330, 0xbc00df00, 0xc00c3043, 0x08b30024, 0x2730434c, 0x93043fc0, +0x3003f801, 0x003cc80d, 0x03cc00f3, 0x00c08f30, 0x00000c22, 0x00000000, +0x00e62081, 0x0404030d, 0x34409812, 0x7420dda0, 0x400d1003, 0x00d104a4, +0x65b0e344, 0x11003742, 0x10837405, 0x0034400d, 0x036c00d1, 0x04400d10, +0x00000802, 0x00000000, 0x0074a000, 0x064420dd, 0x34422d10, 0x7480dd00, +0x400d5203, 0x00910800, 0x01100344, 0x11003740, 0x10036408, 0x1034400d, +0x8344a0c1, 0x04400010, 0x00000200, 0x00000000, 0x00202010, 0x0044009d, +0x30400c10, 0x3480cd00, 0x400c5103, 0x00c10820, 0x00900341, 0xc1483340, +0x12033480, 0x0030400c, 0x032480c1, 0x40500810, 0x00000080, 0x00000000, +0x0836b000, 0x034d004f, 0x34c00534, 0x7c80dd00, 0xd00d7403, 0x00a34034, +0x0130034d, 0x13003f40, 0x3403ac00, 0x4038d00f, 0x034c00f3, 0x00c00e34, +0x00000ac0, 0x00000000, 0x000fb804, 0x80bc003f, 0x3fc20fd8, 0xfc20ff00, +0xc20fb003, 0x00ff083f, 0x03f083fc, 0x3f003bc2, 0xe003fc00, 0x403fc00f, +0x03fc00ff, 0x17c00ff0, 0x00000e60, 0x00000000, 0x000ba003, 0x98cc02bf, +0x3cc20ff0, 0xcc88bf20, 0xc00f3032, 0x00f7103c, 0x033003fc, 0xfb203dc0, +0xf002ccc0, 0x012fc00f, 0x03cc04b3, 0x0cc4cbf0, 0x00000e00, 0x00000000, +0x08350801, 0x004482ed, 0x36c825c0, 0x54469d10, 0x400d5033, 0x40c10039, +0x01110274, 0xe9003048, 0xd0024480, 0x02974a0d, 0x01444a91, 0x0440cbd0, +0x00000c20, 0x00000000, 0x00332011, 0x1811068d, 0x21482c58, 0x16868d20, +0x400c1002, 0x80c58832, 0x01100374, 0xc9003140, 0xd0023400, 0x0033400c, +0x03070081, 0x444008d0, 0x00000e88, 0x00000000, 0x00b5a803, 0x085440dd, +0x36401dd0, 0x16018d00, 0x400d5046, 0x00d11037, 0x0d160274, 0xd9003400, +0xd2036440, 0x0037440d, 0x014620d0, 0x0c4089d0, 0x00000620, 0x00000000, +0x0057a800, 0x045c009f, 0x74c01c70, 0x4c879f00, 0xc00f310f, 0xa0d70036, +0xb130007c, 0xdb0035c0, 0xe0227d00, 0x1033c00f, 0x014c0853, 0x08d01df1, +0x00000e20, 0x00000000, 0x00fd8087, 0x15e404ee, 0x7fc40ff0, 0xf8009e21, +0x800ee003, 0x02ff003d, 0x0ff203f8, 0xff10bf44, 0xf002dc40, 0x401fc00f, +0x17fc00ff, 0x1fc01bf0, 0x00000600, 0x00000000, 0x00350802, 0x014c00db, +0x14c10df1, 0x4c021f01, 0xc04d700a, 0x40d34037, 0x61f0024c, 0xd32034c8, +0xf0824c04, 0x20b7c00d, 0x034d00c3, 0x08d00cf0, 0x00000420, 0x00000000, +0x23b4a013, 0x2dc00091, 0x34501dc0, 0x44019d00, 0x423f1082, 0x4491007b, +0x3cd00304, 0xf00aa440, 0xd0034400, 0x0037400f, 0x034401d1, 0x4c420dd0, +0x00000200, 0x00000000, 0x0032a007, 0x22048089, 0xb24008d0, 0x04418d08, +0x443c1041, 0x13cd0033, 0x00d04b04, 0xd0003148, 0xd0830402, 0x0023400c, +0x0224488d, 0x1e4008d0, 0x00000a00, 0x00000000, 0x407c8006, 0x058409e9, +0x7a4097d1, 0xe501ad00, 0x411e1127, 0x09ed067b, 0x12d00680, 0xe1027900, +0xd0068419, 0x6273401e, 0x06a4098d, 0x12409ad0, 0x00000208, 0x00000000, +0x02301012, 0x010c00ca, 0x32c188f0, 0x0c3c8f00, 0xc0cc3420, 0x00cf2233, +0x0df0030c, 0xc34231d1, 0xf0030d00, 0x02b3c48c, 0x222c088f, 0x4ac289ea, +0x00000040, 0x00000000, 0xa03db802, 0x43fd00b7, 0x35408ec0, 0x9e089f12, +0xc28db1a2, 0x20f30237, 0x0ff120f1, 0xff023ec0, 0xf103fc00, 0x023fc00f, +0xa2dc68f3, 0x09c08bf0, 0x00000660, 0x00000000, 0x0037a015, 0x024c009f, +0xb4c81bf0, 0x5c009301, 0xc00d3403, 0x20d36437, 0x0d30024c, 0xd30130d0, +0xf0034c03, 0x0037c00f, 0x044c0013, 0x54c04df2, 0x00000e00, 0x00000000, +0x00398812, 0x118488ed, 0x3ac40ed1, 0xb480e584, 0x486eb003, 0x00e1003b, +0x0e1083c4, 0xe1043840, 0xd002840c, 0x0033405e, 0x03840085, 0x48450ed0, +0x00000620, 0x00000000, 0x00790083, 0x05a421ad, 0xda4018d0, 0xb401a100, +0x405e1806, 0x11f1027b, 0x1e100684, 0xe1007e44, 0xd0078408, 0x007b429e, +0x02a403a1, 0x0c401cd1, 0x00000400, 0x00000000, 0x00730812, 0x074421cd, +0x32406cd2, 0x74028524, 0x480c900a, 0x00c90033, 0x1c144504, 0xc1027240, +0xd8030520, 0x0073400c, 0x472503c5, 0x48408cd0, 0x00000c20, 0x00000000, +0x015da817, 0x05cc015f, 0x96d007e2, 0xfc067300, 0xc0073411, 0x0172001f, +0x77340dcc, 0x534016c0, 0xf0014c00, 0x015fc005, 0x1de58373, 0x5cd015f0, +0x00000620, 0x00000000, 0x00870012, 0x287d081f, 0x07c001e0, 0x78041f00, +0xc021f810, 0x00170087, 0x31f0207d, 0x1f0005c0, 0xf0007c00, 0x5003c001, +0x005c040f, 0x4bc001f0, 0x00000c00, 0x00000000, 0x00a70810, 0x164c009f, +0x37c01930, 0x4c499b08, 0xc8493002, 0x009b0827, 0x3930064c, 0x8300b4c0, +0xf0024c01, 0x2024d009, 0x024c0193, 0x40c02930, 0x00000c20, 0x00000000, +0x01262001, 0x164c009d, 0x23403914, 0x6c839100, 0x40495002, 0x009108a7, +0x09100644, 0x914026c8, 0x70024083, 0x40244008, 0x024c0a91, 0x04502910, +0x00000800, 0x00000000, 0x0024a018, 0x02440085, 0x27498910, 0x44828100, +0x60091002, 0x00814023, 0x0c102305, 0x91002050, 0x50024414, 0x40244009, +0x03060091, 0x60410810, 0x00000200, 0x00000000, 0x00202010, 0xd204148d, +0x23414818, 0x24148140, 0x60085552, 0x08810223, 0x8c142204, 0x81022060, +0x500a0408, 0x02204028, 0x0a270181, 0x40408810, 0x00000080, 0x00000000, +0x2006b01d, 0x104c4407, 0x17c84130, 0x06041340, 0xc1413110, 0x02110093, +0x2032084c, 0x03108440, 0x71004c02, 0x1584c141, 0x004c9413, 0x74c16130, +0x00000ac0, 0x00000000, 0x082f9819, 0x52d914bf, 0x33c60bf0, 0xfc14bf20, +0xc009f452, 0x04a701a7, 0x4ff012fc, 0x9f0127c0, 0x7002f806, 0x012fc409, +0x03dc40bf, 0x67c04bf0, 0x00000e60, 0x00000000, 0x002f8019, 0x1acc0c93, +0x25c04b31, 0xcc069300, 0xd059300a, 0x029301ac, 0x29300a6c, 0x9300a4c0, +0xf0020c80, 0x0324c018, 0x0a4c00b3, 0x60c0cb30, 0x00000e00, 0x00000000, +0x0007089c, 0xa8400e11, 0x0452a110, 0x044a0120, 0x40011008, 0x04110384, +0x01100044, 0x31000440, 0xd1104416, 0x43844061, 0x106d0a15, 0x7040e110, +0x00000c20, 0x00000000, 0x0067a010, 0x12144085, 0x25401910, 0x24219580, +0x4029101a, 0x04a50127, 0x4a101285, 0xa1012840, 0xd0422422, 0x20204128, +0x12644089, 0x40400810, 0x00000e80, 0x00000000, 0x6065a818, 0x0a540481, +0x24400918, 0x44209551, 0x40090002, 0x00a10026, 0x0b100a84, 0xb1002c40, +0xd0066400, 0x04245409, 0x0264009d, 0x60404810, 0x00000620, 0x00000000, +0x00e7a805, 0x0a5d0197, 0xe5802934, 0x6d009720, 0xc0092402, 0x01970027, +0x2934026c, 0x93402490, 0xf0026c00, 0x4024c809, 0x0a2c069b, 0x14d01934, +0x00000e20, 0x00000000, 0x00258214, 0x666c209f, 0x37c009f1, 0x3c09db00, +0xc10df026, 0x429f4025, 0x59f0027c, 0x9f0e67c0, 0xf0825c00, 0x0227c008, +0x027c0997, 0x53c009f0, 0x00000600, 0x00000000, 0x24050814, 0x080c001f, +0xc4c00118, 0x4c881f00, 0xc0013404, 0x10130004, 0x0130004c, 0x0f0004d0, +0x30007c04, 0x4000c001, 0x007c0303, 0x50c101f0, 0x00000420, 0x00000000, +0x09d88214, 0x81c4005d, 0x14505630, 0xec415c00, 0x4004f015, 0x055b00d0, +0x15140545, 0x5d209440, 0x50017402, 0x401c4007, 0x01740671, 0x505077d0, +0x00000200, 0x00000000, 0x00b2a014, 0x0f0000cd, 0x37604c98, 0x0441dd00, +0x50205007, 0x03c106f0, 0x0d103704, 0xcd02f740, 0x10033400, 0x0030500c, +0x033401c1, 0x50400cd0, 0x00000a00, 0x00000000, 0x08388005, 0x080404ed, +0x3b400c10, 0xa400ed00, 0x4103d003, 0x00e9003c, 0x0e100384, 0xed001b41, +0x4007f401, 0x0038424e, 0x13b480e1, 0x14400cd0, 0x00000200, 0x00000000, +0x00781015, 0x048d03fd, 0x7bc01e94, 0x8c116f10, 0xc0127007, 0x01f3007c, +0x1f3106cc, 0x2f007bc0, 0x3047bc90, 0x007cc13e, 0x1fbc0123, 0x54c01ef0, +0x00000040, 0x00000000, 0x0015b010, 0x837c16df, 0xb4d80970, 0xfc205f01, +0xc068f403, 0x00df0037, 0x0df0027c, 0x1f0014c0, 0xb0137404, 0x0237c00d, +0x0b7c205f, 0x43c00df0, 0x00000660, 0x00000000, 0x1a7fa002, 0x04cc41ff, +0xfcc01730, 0xfc01f302, 0xc0333005, 0x01e7006f, 0x1b3007cc, 0xf3426fc0, +0x7243fc01, 0x207cc41f, 0x0fcc817b, 0x03c01f30, 0x00000e00, 0x00000000, +0x12398815, 0x1b8404ed, 0x3ac00e10, 0xf482a102, 0x48221003, 0x50e1003b, +0x0a104384, 0xe1000f48, 0xb023f400, 0x403ac00f, 0x03cc0061, 0x57400eb0, +0x00000620, 0x00000000, 0x2a190008, 0x038490cd, 0xba400e18, 0xb4002510, +0x40021001, 0x00f50223, 0x0a1402c5, 0x21142b40, 0x5023b400, 0x0038408e, +0x03a40001, 0x03400e10, 0x00000400, 0x00000000, 0x04032816, 0x0c0400cd, +0x32490c10, 0x74010500, 0x4000100b, 0x00d10033, 0x2c120204, 0x91200342, +0xc0033400, 0x0032400c, 0x0b042001, 0x13409d90, 0x00000c20, 0x00000000, +0x0075a805, 0x034d01ff, 0x32c00c34, 0x7c611740, 0xc601300a, 0x00170017, +0x1430014c, 0xd31037c9, 0x7003f800, 0x003cc20f, 0x1fad0513, 0x57c01d30, +0x00000620, 0x00000000, 0x02b70001, 0xb97c80df, 0x77c01df0, 0xfc081a00, +0xc001f022, 0x021f0017, 0x45f0017c, 0xdf0097c0, 0x22077c00, 0x0477c00d, +0x437c105f, 0x07c00df0, 0x00000c00, 0x00000000, 0x003f0801, 0x07cc00f3, +0x3cc80db0, 0xfc143300, 0xc0833042, 0x283b0018, 0x073040cc, 0x3f027cc0, +0xb003cc02, 0x2038c00f, 0x03cd0033, 0x00c09f30, 0x00000c22, 0x00000000, +0x00122081, 0x0d0400d1, 0x34500d10, 0x74031100, 0x4028f002, 0x02110094, +0x2d104844, 0x5d001440, 0x10034481, 0x4035400d, 0x03440641, 0x04402d10, +0x00000802, 0x00000000, 0x0c34a001, 0x224500c1, 0x34608593, 0x76005110, +0x40011005, 0x10190084, 0x21101104, 0xdd142540, 0x90030400, 0x0034400c, +0x03542119, 0x04610d10, 0x00000200, 0x00000000, 0x20102210, 0x004400c1, +0x54620d90, 0x34000100, 0x4800d203, 0x00c12010, 0x01120305, 0xcd002140, +0x91030440, 0x0031480c, 0x03440001, 0x40400c10, 0x00000080, 0x00000000, +0x0016b001, 0x020c80f1, 0x34c80db1, 0x7c001340, 0xd0013201, 0x001b0024, +0x0134014d, 0x1f0035c4, 0xb103cd00, 0x0034c00f, 0x03dc0013, 0x00d00132, +0x00000ac0, 0x00000000, 0x002fb805, 0x00fc00ff, 0x3bd00f70, 0xfc003f00, +0xc00ff483, 0x20ff103f, 0x0ff083bc, 0xff003ed4, 0x7183fc00, 0x403bc40f, +0x03fc803f, 0x17c003f0, 0x00000e60, 0x00000000, 0x8080a300, 0x0839020e, +0x83b820ec, 0x3b038ec0, 0xb020ec08, 0x820e80e3, 0x20ec083a, 0x0ec083a0, +0xec0a3b02, 0xc083a828, 0x083b020e, 0x03b020ec, 0x0000008c, 0x00000000, +0x8022a200, 0x023a808e, 0x23b808ea, 0x2a808ea0, 0xa808ea82, 0x808e8023, +0x08ea023a, 0x8ea023a8, 0xea023a80, 0xa0232808, 0x023a808e, 0x03a808ea, +0x00000a88, 0x00000000, 0x80402100, 0x04120104, 0x41201048, 0x12018480, +0x20104804, 0x01048061, 0x10480412, 0x04804120, 0x48041201, 0x80410018, +0x04120104, 0x01201048, 0x0000020c, 0x00000000, 0x80008100, 0x001a0006, +0x01a00068, 0x1a100680, 0xa0006800, 0x10068401, 0x0068001a, 0x060001a9, +0x68461a00, 0x80018900, 0x00180006, 0x01a00068, 0x00000004, 0x00000000, +0xa012a300, 0x013a804e, 0x13a004e2, 0x3a884e20, 0xa804ea01, 0x004ea013, +0x04e2013a, 0x4ea013a8, 0xea293a80, 0xa013a014, 0x013a804e, 0x03a804ea, +0x00000a88, 0x00000000, 0x0002a100, 0x00180006, 0x01800060, 0x18080600, +0x80006000, 0x00060201, 0x80602018, 0x06020180, 0x60a01800, 0x00018080, +0x00180006, 0x01800060, 0x0000008c, 0x00000000, 0x2012a300, 0x01108044, +0x11000442, 0x10804420, 0x08044201, 0x00042011, 0x00420110, 0x44201108, +0x42011080, 0x20110004, 0x01108044, 0x01080442, 0x0000008c, 0x00000000, +0xa052a300, 0x050a8142, 0x50a0142a, 0x0a8342a0, 0xa8142a05, 0x8102a050, +0x102a050a, 0x42a050a0, 0x2a070881, 0xa050a014, 0x05088142, 0x00a8142a, +0x00000a8c, 0x00000000, 0x80300100, 0x032a00ca, 0x32a00ca8, 0x2200ea80, +0xa00ca803, 0x00ca803a, 0x0ea80baa, 0xca802aa0, 0xa803aa00, 0x8032a00e, +0x032a00ca, 0x02a00ca8, 0x00000804, 0x00000000, 0x28000100, 0x00080002, +0x00820020, 0x20000200, 0x80002000, 0x00422000, 0x04200008, 0x02000082, +0x20020800, 0x00008800, 0x00080002, 0x80800020, 0x00000004, 0x00000000, +0x0006a200, 0x00410010, 0x04100104, 0x61101040, 0x10010400, 0x10100404, +0x01040841, 0x10401411, 0x04404100, 0x40041101, 0x00410010, 0x80100104, +0x00000a8c, 0x00000000, 0xa002a300, 0x001a8006, 0x01a0006a, 0x1a8026a0, +0xa8006a00, 0x4006a009, 0x026a009b, 0x06a8099a, 0x6a009a80, 0xa001a802, +0x001a8006, 0x01a8006a, 0x0000008c, 0x00000000, 0x8002a300, 0x001b0006, +0x01b0006c, 0x1b0006c0, 0xb0006c00, 0x0046c001, 0x046c001a, 0x06c001a0, +0x6c001b00, 0xc001b000, 0x001b0006, 0x01b0006e, 0x0000008c, 0x00000000, +0x0042a300, 0x0430810c, 0x430810c2, 0x30910c20, 0x0810c204, 0x810c2043, +0x10c20430, 0x0c204308, 0xc2443081, 0x20430810, 0x0430810c, 0x030810c2, +0x00000a8c, 0x00000000, 0x20000100, 0x0030000c, 0x030000c0, 0x30080c00, +0x0000c000, 0x000c0203, 0x80c02030, 0x0c020300, 0xc0203000, 0x00030080, +0x0030000c, 0x030000c0, 0x00000004, 0x00000000, 0x80400100, 0x0432010c, +0x432010c8, 0x32010c80, 0x2010c804, 0x830c8043, 0x30c80432, 0x0c804328, +0xc8043201, 0x80432010, 0x0432010c, 0x032010ca, 0x00000204, 0x00000000, +0xa042a200, 0x041a8106, 0x41a8106a, 0x1a8106a0, 0xa8106a04, 0x8306a041, +0x306a041a, 0x06a04188, 0x6a041a81, 0xa041a810, 0x041a8106, 0x01a81060, +0x00000a88, 0x00000000, 0x0042a300, 0x04100104, 0x41001040, 0x10210400, +0x00104004, 0x01040041, 0x10400430, 0x04004300, 0x40041001, 0x00410010, +0x04100104, 0x01001040, 0x00000084, 0x00000000, 0x2042a200, 0x04188106, +0x41881062, 0x18812620, 0x88106204, 0x81062041, 0x10620430, 0x06204308, +0x62049881, 0x20418810, 0x04188106, 0x01881060, 0x00000088, 0x00000000, +0x2006a300, 0x0068801a, 0x06a801aa, 0x6a801aa0, 0xa801a200, 0x001aa006, +0x01aa0062, 0x1aa00620, 0xaa006a80, 0xa006a801, 0x006a801a, 0x02a801a0, +0x00000a88, 0x00000000, 0x80600100, 0x060a0182, 0x60a01828, 0x08018280, +0xa0182806, 0x01828060, 0x1828060a, 0x82006080, 0x20060801, 0x80608018, +0x06080182, 0x00a01828, 0x00000004, 0x00000000, 0x80400000, 0x04020100, +0x40221008, 0x02012080, 0x20100804, 0x01008040, 0x10080402, 0x00804020, +0x08048201, 0x80402210, 0x04020100, 0x00201008, 0x00000004, 0x00000000, +0x8062a300, 0x062b018a, 0x62b018ac, 0x2b098ac0, 0xb018ac06, 0x098ac262, +0x98ac062b, 0x8ac062b0, 0xac262b01, 0xc062b098, 0x062b018a, 0x02b018ac, +0x00000a8c, 0x00000000, 0xa062a200, 0x063a818e, 0x63a818ea, 0x3a918ea0, +0xa818ea06, 0x818ea063, 0x18ea063b, 0x8e8063b8, 0xea463a81, 0xa0638818, +0x063a818e, 0x03a818ea, 0x00000088, 0x00000000, 0xc062a200, 0x063b018e, +0x63b018e4, 0x39018ec0, 0xb0186c06, 0x018ec063, 0x18ec063a, 0x8ec063a8, +0xec063b81, 0xc063b018, 0x063b018e, 0x03b018e4, 0x00000088, 0x00000000, +0xa062a200, 0x063a818e, 0x63a818ea, 0x3a898ea0, 0xa818ca06, 0x818ea263, +0x98eaa630, 0x8ea26308, 0xea263b81, 0xa063a898, 0x063a818e, 0x03a818ea, +0x00000a88, 0x00000000, 0x80400000, 0x04120104, 0x41201048, 0x12010480, +0x20104804, 0x01048041, 0x1048043a, 0x048043a0, 0x48041201, 0x80412010, +0x04120104, 0x01201048, 0x00000000, 0x00000000, 0x80600000, 0x061a0186, +0x61a01868, 0x1a018680, 0xa0186806, 0x81860061, 0x18680618, 0x860061a0, +0x68061a01, 0x8061a018, 0x061a0186, 0x01a01868, 0x00000000, 0x00000000, +0xa002a200, 0x003a800e, 0x038800ea, 0x3a800ea0, 0x8800e200, 0x800ca003, +0x00ca003a, 0x0ea003a8, 0xea003a00, 0xa0032800, 0x003a800e, 0x03a800e2, +0x00000a88, 0x00000000, 0x0042a200, 0x04180106, 0x41801060, 0x18010600, +0x80106004, 0x010400c1, 0x10400418, 0x06004180, 0x60041801, 0x00410010, +0x04180106, 0x01801060, 0x00000088, 0x00000000, 0x2042a200, 0x04108104, +0x41081042, 0x10810420, 0x08104204, 0x81062041, 0x10620410, 0x04204108, +0x42041001, 0x20418810, 0x04108104, 0x01081042, 0x00000088, 0x00000000, +0x2042a200, 0x040a8102, 0x4088102a, 0x088102a0, 0xa8102204, 0x010a2040, +0x10aa0c0a, 0x02a040a8, 0x2a040a01, 0xa042a830, 0x040a8102, 0x00a8102a, +0x00000a88, 0x00000000, 0x80c00000, 0x0c2a030a, 0xc2a030a8, 0x2a010a80, +0xa030a80c, 0x030a80c2, 0x30a80c2a, 0x0a80c2a0, 0xa80c2a03, 0x80c2a030, +0x0c2a030a, 0x02a030a8, 0x00000000, 0x00000000, 0x00120000, 0x01080042, +0x10800420, 0x08004200, 0x80042001, 0x00420000, 0x04208008, 0x42001080, +0x20010800, 0x00108000, 0x01080042, 0x00800420, 0x00000000, 0x00000000, +0x4042a200, 0x04010100, 0x40101004, 0x01010040, 0x10100404, 0x01004050, +0x10040501, 0x00404010, 0x04040101, 0x40401014, 0x04010100, 0x00101004, +0x00000a88, 0x00000000, 0xa000a200, 0x001a8006, 0x01a8006a, 0x1a0006a0, +0xa0006a00, 0x8006a001, 0x0062001b, 0x06a001b8, 0x6a001a80, 0xa001a800, +0x001a8006, 0x01a8006a, 0x00000088, 0x00000000, 0xc002a300, 0x001b0006, +0x01b0006c, 0x1b8006c0, 0xb0006c00, 0x8006c001, 0x006c003a, 0x06c003a0, +0x6c001b00, 0xc001b000, 0x00130006, 0x01b0006c, 0x00000000, 0x00000000, +0x20000000, 0x0030800c, 0x030800c2, 0x30800c20, 0x0800c200, 0x800c2003, +0x00c20018, 0x0c200188, 0xc2003080, 0x20030800, 0x0030800c, 0x030800c2, +0x00000000, 0x00000000, 0x00000000, 0x0030000c, 0x030000c0, 0x30002c00, +0x0000c000, 0x002c000b, 0x0ac00010, 0x0c002100, 0xc002b000, 0x00030002, +0x0010000c, 0x030000c0, 0x00000000, 0x00000000, 0x00a00000, 0x0a32028c, +0xa32028c8, 0x32808c80, 0x2028c80a, 0x028c80a3, 0x38c80a32, 0x8c80a328, +0xc80a3202, 0x80a32028, 0x0a32028c, 0x032028c8, 0x00000000, 0x00000000, +0xa0c40000, 0x0c588316, 0xc5a8316a, 0x5a8316a0, 0xa8316a0c, 0x8316a0c5, +0x316a0c5a, 0x16a0c5a8, 0x6a0c5a83, 0xa0c5a831, 0x0c5a8316, 0x01a8316a, +0x00000000, 0x00000000, 0x00000000, 0x00100004, 0x01000040, 0x10002400, +0x00004000, 0x00240019, 0x0a400110, 0x04002100, 0x40029000, 0x00010006, +0x00100004, 0x01000040, 0x00000000, 0x00000000, 0x22100000, 0x21188846, +0x11888462, 0x18804622, 0x88846221, 0x88462201, 0x04622018, 0x46201188, +0x62011888, 0x22118880, 0x21188846, 0x01888462, 0x00000000, 0x00000000, +0xa2000000, 0x2028880a, 0x028880a2, 0x28080aa2, 0xa880aa20, 0x880aa202, +0x80aa202a, 0x0aa222a0, 0xaa022888, 0x2202a880, 0x202a880a, 0x02a880aa, +0x00000000, 0x00000000, 0x84100000, 0x410a1042, 0x10a10428, 0x0a184284, +0xa1042041, 0x104a8010, 0x04a8410a, 0x42841081, 0x20010a10, 0x8412a004, +0x410a1042, 0x00a10428, 0x00000000, 0x00000000, 0x80500000, 0x05020140, +0x50201408, 0x02014080, 0x20140805, 0x01400050, 0x14000502, 0x40805020, +0x08050201, 0x80500014, 0x05020140, 0x00201408, 0x00000000, 0x00000000, +0xc0300000, 0x032b00ca, 0x32b00cac, 0x2b00cac0, 0xb00cac03, 0x00c84032, +0x0c84032b, 0xcac032b0, 0xac032b00, 0xc032100c, 0x032b00ca, 0x02b00cac, +0x00000000, 0x00000000, 0xa0100000, 0x013a804e, 0x13a004ea, 0x3a804ea0, +0xa804ea01, 0x804ea013, 0x04ea013b, 0x4ea013b8, 0xea013a80, 0xa013a804, +0x013a804e, 0x03a804ea, 0x00000000, 0x00000000, 0x18c40000, 0x8c486312, +0xc4863121, 0x48030218, 0x8631218c, 0x231208c0, 0x30218c08, 0x1218c082, +0x200c0863, 0x18c48230, 0x8c486312, 0x00863121, 0x00000000, 0x00000000, +0xfffc0000, 0xffcbfff2, 0xfcbfff2f, 0xcbfff2ff, 0xbfff2fff, 0xfff2fffc, +0xff2fffcb, 0xf2fffcbf, 0x2fffcbff, 0xfffcbfff, 0xffcbfff2, 0x00bfff2f, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xdb340000, 0xb3436cd0, +0x3436cd0d, 0x4bedf0db, 0x36cd0db3, 0x6cd2fb34, 0xdf0db343, 0xd0db7c36, +0x2fffc36c, 0xdb34becd, 0xb3436cd0, 0x0036cd0d, 0x00000000, 0x00000000, +0x3ccc0000, 0xccc8f332, 0xcc8f3323, 0xcbf3f23c, 0x8f3323cc, 0xf332fccc, +0x3f23ccc8, 0x323cfc8f, 0x2fffc8f3, 0x3cccbf33, 0xccc8f332, 0x008f3323, +0x00000000, 0x00000000, 0x7edc0000, 0xedc9fb72, 0xdc9fb727, 0xc9e3727e, +0x9fb727ed, 0x7b727ec4, 0xb727ec48, 0x727edc87, 0x27edc9fb, 0x7edc9fb1, +0xedc9fb72, 0x009fb727, 0x00000000, 0x00000000, 0x40800000, 0x0839020e, +0xa39020e4, 0x39020e60, 0x9028e408, 0x020e40a3, 0x20e60839, 0x0e408380, +0xe4083902, 0x60839028, 0x0833020e, 0x038020e4, 0x00000000, 0x00000000, +0xa0200000, 0x023aa08e, 0x63a808ea, 0x3a808ea0, 0xa808ea02, 0x808ea063, +0x08ea023a, 0x8ea023a0, 0xea823a80, 0xa023a808, 0x0232808e, 0x03a808ea, +0x00000000, 0x00000000, 0x80400000, 0x00120104, 0x61201048, 0x12010480, +0x20104804, 0x01048041, 0x10480412, 0x44804120, 0x48041201, 0x80412018, +0x04120104, 0x01201048, 0x00000000, 0x00000000, 0x00000000, 0x44180006, +0x01800060, 0x18100604, 0x81086000, 0x10060421, 0x00620018, 0x06000180, +0x60001800, 0x20018110, 0x80180006, 0x01890060, 0x00000000, 0x00000000, +0x22100000, 0x0138884e, 0x138804e2, 0x38804e00, 0x8814e201, 0x804e2013, +0x04e00138, 0x0e201388, 0xe2213880, 0x00138804, 0x2938804e, 0x038884e2, +0x00000000, 0x00000000, 0x00000000, 0x00180086, 0x01800060, 0x18080600, +0x80006080, 0x00060201, 0x80600018, 0x06020180, 0x60001800, 0x00018080, +0x00180806, 0x01800060, 0x00000000, 0x00000000, 0x20300000, 0x01908044, +0x19080442, 0x10806400, 0x08064201, 0x80642019, 0x04400110, 0x44201108, +0x42011080, 0x00110806, 0x03108044, 0x01080242, 0x00000000, 0x00000000, +0x20540000, 0x054881d2, 0x54881522, 0x48815220, 0x88152205, 0x81522054, +0x15200548, 0x52205488, 0x220d4881, 0x00548815, 0x05488152, 0x00801922, +0x00000000, 0x00000000, 0x00300310, 0x032800ca, 0x32800ca0, 0x2800ca00, +0x800ca003, 0x00ca00b2, 0x0ca00328, 0xca003280, 0x80032800, 0x0032800c, +0x032800ca, 0x02800ca0, 0x00000000, 0x00000000, 0x00600310, 0x00880082, +0x08800020, 0x08002200, 0x80022000, 0x00220008, 0x00200008, 0x02000080, +0x80000800, 0x00008002, 0x02080002, 0x01801620, 0x0000000c, 0x00000000, +0x40020310, 0x40010000, 0x00100004, 0x01100044, 0x11000400, 0x10004480, +0x00040001, 0x00400000, 0x84000100, 0x40001100, 0x00010000, 0x00110004, +0x0000000c, 0x00000000, 0x20020310, 0x00188006, 0x018a0062, 0x18800600, +0x88006200, 0x80062001, 0x00610018, 0x06200198, 0x628018a0, 0x20018800, +0x00188006, 0x01980060, 0x0000000c, 0x00000000, 0x40028008, 0x00190006, +0x01900064, 0x19800600, 0x98006400, 0x00064001, 0x00640019, 0x06400190, +0x60001900, 0x40019000, 0x00130006, 0x41800464, 0x00000000, 0x00000000, +0x2442a202, 0x0430910c, 0x430810c2, 0x30810c00, 0x0810c204, 0x810c2043, +0x10c20430, 0x0c204308, 0xc0443081, 0x20430810, 0x4430810c, 0x030910c2, +0x00000a88, 0x00000000, 0x00000002, 0x0030000c, 0x030000c0, 0x30080c00, +0x0000c000, 0x000c0203, 0x80c00030, 0x0c020300, 0xc0003000, 0x00030080, +0x0010080c, 0x030000c0, 0x00000000, 0x00000000, 0x00400002, 0x0430210c, +0x430010c0, 0x30810c00, 0x0010c004, 0x010c0043, 0x10c00430, 0x0c004300, +0xc0043001, 0x00430010, 0x0430010c, 0x030830c0, 0x00000000, 0x00000000, +0x2042a202, 0x04188106, 0x41881062, 0x18010620, 0x80106204, 0x81062041, +0x10620418, 0x06204188, 0x62041881, 0x20418810, 0x04188106, 0x01883062, +0x00000a88, 0x00000000, 0x00428002, 0x04100104, 0x41001040, 0x10010400, +0x00104004, 0x01040041, 0x10400410, 0x04004100, 0x40041001, 0x00410010, +0x04100104, 0x03001040, 0x00000000, 0x00000000, 0x2040800a, 0x04188106, +0x41881062, 0x18010620, 0x80106204, 0x81062041, 0x10620418, 0x06204188, +0x62041881, 0x20418810, 0x04188106, 0x03081062, 0x00000200, 0x00000000, +0x2002a202, 0x0028800a, 0x028800a2, 0x28000a20, 0x8000a200, 0x800a2002, +0x00a20028, 0x0a200288, 0xa2002880, 0x20028800, 0x0028800a, 0x420000a2, +0x00000a88, 0x00000000, 0x00602012, 0x06080182, 0x60801820, 0x08018200, +0x88182006, 0x01820060, 0x18200608, 0x82006080, 0x20060801, 0x00608018, +0x06080182, 0x00801820, 0x00000008, 0x00000000, 0x80400012, 0x84020100, +0x40201008, 0x02010080, 0x20100804, 0x01008040, 0x10080402, 0x00804020, +0x08040201, 0x80402010, 0x04022100, 0x00201008, 0x00000000, 0x00000000, +0xc062a202, 0x262b018a, 0x62b018ac, 0x2b098a82, 0xb098ac06, 0x098ac262, +0x188c062b, 0x8ac062b0, 0xa8062b01, 0xc062b098, 0x062b018a, 0x02b098ac, +0x00000a88, 0x00000000, 0x24628000, 0x0638118e, 0x638818e2, 0x38818e70, +0x8818e286, 0x818e2063, 0x18ca0638, 0x8e206388, 0xe6463881, 0x20638818, +0x4638818e, 0x039918e2, 0x00000000, 0x00000000, 0x40628002, 0x0639018e, +0x639018e4, 0x3b818e40, 0x9018e406, 0x018e6063, 0x18e40639, 0x8e406398, +0xe4063901, 0x40639018, 0x0639818e, 0x038018e4, 0x00000000, 0x00000000, +0xa062a20a, 0x063a818e, 0x63a818ea, 0x13898ea0, 0xa818ea06, 0x818ea263, +0x98ea063a, 0x8ea263a8, 0xea063a81, 0xa063a898, 0x063a898e, 0x430818ea, +0x00000a88, 0x00000000, 0x80480002, 0x04120124, 0x41201048, 0x12010480, +0x20104804, 0x01048041, 0x10480412, 0x04804120, 0x48049201, 0x80412010, +0x04920104, 0x03a01248, 0x00000200, 0x00000000, 0x00608002, 0x06180186, +0x61801860, 0x18018600, 0x80186006, 0x01862061, 0x18600618, 0x86006180, +0x60061801, 0x00618018, 0x06188186, 0x01881860, 0x00000080, 0x00000000, +0x20068200, 0x0078801e, 0x0788014a, 0x78001e20, 0x8801e200, 0x801c0007, +0x01e20078, 0x1e200700, 0xe2007880, 0x20078801, 0x0078001e, 0x038801e2, +0x00000a08, 0x00000000, 0x00488002, 0x04180126, 0x41801060, 0x18010600, +0x80106004, 0x01040041, 0x10600418, 0x06004100, 0x60049801, 0x00418010, +0x04980106, 0x01801260, 0x00000000, 0x00000000, 0x20428002, 0x04108104, +0x41081042, 0x10010420, 0x08104204, 0x81060041, 0x10420410, 0x04204180, +0x42041081, 0x20410810, 0x04100104, 0x01081042, 0x00000000, 0x00000000, +0x2042a202, 0x04088102, 0x40881022, 0x08010220, 0x88102204, 0x810a00c0, +0x30220408, 0x02204288, 0x22040881, 0x20408810, 0x04080102, 0x00801022, +0x00000a88, 0x00000000, 0x00c00002, 0x0c28030a, 0xc28030a0, 0x28030a00, +0x8030a00c, 0x030a00c2, 0x30a00c28, 0x0a00c280, 0xa0042803, 0x00c28030, +0x0c28030a, 0x028030a0, 0x00000000, 0x00000000, 0x00100202, 0x01080042, +0x10800420, 0x08204200, 0x80042001, 0x00420000, 0x00200108, 0x42001080, +0x60010800, 0x00108004, 0x01080042, 0x00800420, 0x00000000, 0x00000000, +0x4042a202, 0x04010100, 0x40101004, 0x01010040, 0x10100404, 0x01004050, +0x14040401, 0x00404010, 0x04040101, 0x40401010, 0x04010100, 0x00101004, +0x00000a88, 0x00000000, 0x20028202, 0x00188006, 0x01880060, 0x18000620, +0x88006200, 0x80062001, 0x00620018, 0x06200180, 0x62001880, 0x20018800, +0x00188006, 0x01980062, 0x00000000, 0x00000000, 0x48028002, 0x00190006, +0x01900066, 0x19002640, 0x90006400, 0x00060009, 0x02640019, 0x06400998, +0x64001900, 0x40019002, 0x00198026, 0x03800064, 0x00000000, 0x00000000, +0x2006a202, 0x0070801c, 0x070801c2, 0x70801c20, 0x0801c200, 0x801c0807, +0x01c20070, 0x1c200708, 0xc2007080, 0x20070801, 0x0070801c, 0x018801c2, +0x00000a88, 0x00000000, 0x0080000a, 0x0830020c, 0x830020c0, 0x30020c00, +0x0020c008, 0x020c0083, 0x20c00830, 0x0c00a300, 0xc0083002, 0x00830038, +0x0830028c, 0x010020c0, 0x00000000, 0x00000000, 0x00a0a002, 0x0a30028c, +0xa30028c2, 0x3002ac00, 0x0028c00a, 0x028c00ab, 0x2ac00a30, 0x8c00ab00, +0xc0023002, 0x00a3002a, 0x0a3082ac, 0x430828c0, 0x00000000, 0x00000000, +0x20c2a202, 0x0c188306, 0xc1883060, 0x18830620, 0x8830620c, 0x830620c1, +0x30620c18, 0x0620e180, 0x620c1883, 0x20c18830, 0x0c180386, 0x01883062, +0x00000a88, 0x00000000, 0x00028002, 0x00100004, 0x01000040, 0x10008400, +0x02004000, 0x00040011, 0x04400010, 0x04000100, 0x40001000, 0x00010008, +0x00100004, 0x01000040, 0x00000000, 0x00000000, 0x2210a002, 0x21188846, +0x11888460, 0x1880c622, 0x88846221, 0x88462201, 0x80622118, 0x46201180, +0x62211888, 0x22118804, 0x211800c6, 0x01888462, 0x00000000, 0x00000000, +0x2002a202, 0xa028800a, 0x028880a0, 0x28880a2a, 0x8880a220, 0x880a2202, +0x80a22028, 0x0a228288, 0xa2a02888, 0x22028880, 0x0028080a, 0x028000a2, +0x00000a88, 0x00000000, 0x04120002, 0x01081042, 0x10810420, 0x08004200, +0x80042040, 0x004a0010, 0x04204108, 0x42041281, 0x20410810, 0x04108004, +0x41081042, 0x00810420, 0x00000000, 0x00000000, 0x80508200, 0x05020140, +0x50201408, 0x02014080, 0x20140805, 0x01400050, 0x14080502, 0x40805020, +0x08050201, 0x80502014, 0x05020140, 0x00201408, 0x00000000, 0x00000000, +0xc0b2a202, 0x0b2b02ca, 0xb2b02cac, 0x2b02cac0, 0xb02cac0b, 0x02c800b2, +0x2cac0b2b, 0xcac0b290, 0xac0b2b02, 0xc0b2b02c, 0x0b2302ca, 0x02b02cac, +0x00000a88, 0x00000000, 0x20120002, 0x0138804e, 0x138800e2, 0x38404e20, +0x8804e200, 0x804e6013, 0x04e20138, 0x0e2013a0, 0xe2003880, 0x20138804, +0x0132804e, 0x039804e2, 0x00000000, 0x00000000, 0x10c40000, 0x8c484312, +0xc4863120, 0x48230208, 0x8231218c, 0x231208c0, 0x30218c48, 0x1218c086, +0x210c4863, 0x18c48230, 0x0c486302, 0x00803121, 0x00000000, 0x00000000, +0xfffc0000, 0xffcbfff2, 0xfcbfff2f, 0xcbfff2ff, 0xbfff2fff, 0xfff2fffc, +0xff2fffcb, 0xf2fffcbf, 0x2fffcbff, 0xfffcbfff, 0xffcbfff2, 0x00bfff2f, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xdfb40000, 0xb3437ed0, +0x3436cd2f, 0x4bedf2fb, 0xbecd0db3, 0xecd2fb34, 0xcd0db34b, 0xd0db7c36, +0x0db3436c, 0xdb34bedf, 0xfb436df0, 0x0037ed0d, 0x00000000, 0x00000000, +0x3fcc0000, 0xccc8ff32, 0xcc8f332f, 0xcbf3f2fc, 0xbf3323cc, 0xf332fccc, +0x3323cccb, 0x323cfc8f, 0x23ccc8f3, 0x3cccbf3f, 0xfcc8f3f2, 0x008ff323, +0x00000000, 0x00000000, 0x7edc0000, 0xedc9fb72, 0xdc9fb727, 0xc9fb727e, +0x9fb727ed, 0xfb727ec4, 0xb127edc9, 0x727edc9f, 0x278dc9fb, 0x7edc9fb7, +0xedc9fb72, 0x0087b727, 0x00000000, 0x00000000, 0x65000000, 0x10508c20, +0x422ac04a, 0x00843021, 0x08504a14, 0x84202140, 0x50821010, 0x00214018, +0x02100185, 0x61423850, 0x14508120, 0x00085002, 0x00000000, 0x00000000, +0xa0024000, 0x00618818, 0x0108208e, 0x00800820, 0x08008220, 0x80482011, +0x04420162, 0x58201228, 0x02012280, 0xa0100804, 0x05308848, 0x00080682, +0x00000920, 0x00000000, 0xe3000000, 0x20128c24, 0x032ac04a, 0x908d1423, +0x08c64a10, 0x8d342302, 0x42422011, 0x24230118, 0x4234518c, 0x21010842, +0x30708c34, 0x00084042, 0x00000000, 0x00000000, 0xe0100800, 0x21128060, +0x1f28040e, 0x90814420, 0x08040a01, 0x8140201c, 0x06c22150, 0x64201d18, +0x4205c180, 0x601e3806, 0x01b08004, 0x00080642, 0x00000000, 0x00000000, +0x01004000, 0x14202c40, 0x00200008, 0x40080401, 0x0040c400, 0x04080100, +0x40001100, 0x40010100, 0x40110004, 0x01113044, 0x15000444, 0x01004400, +0x00000900, 0x00000000, 0x40400010, 0x40032900, 0x423010c4, 0x02010000, +0x00104804, 0x01048040, 0x10800400, 0x00804000, 0x80040001, 0x00430010, +0x00000108, 0x00001000, 0x00000000, 0x00000000, 0x83100010, 0x19920d44, +0x112014c8, 0x010c6403, 0x0005c421, 0x0c784318, 0x44401580, 0x40031100, +0x4031800c, 0x01130046, 0x21d00c64, 0x41004640, 0x00000000, 0x00000000, +0x80100000, 0x41830044, 0x18300480, 0x41004400, 0x0005c021, 0x00480014, +0x06000140, 0x60401800, 0x40010000, 0x00183004, 0x21100060, 0x41000440, +0x00000000, 0x00000000, 0x21102010, 0x1501854c, 0x50085442, 0xa0854021, +0x08540615, 0x85402151, 0x54021511, 0x40615008, 0x4215d085, 0x61510854, +0x25238540, 0x40085482, 0x00000080, 0x00000000, 0xa0100010, 0x2183a05c, +0x1b2a048a, 0x92807420, 0x0806ca01, 0x80402015, 0x040201f2, 0x60a01008, +0x82010080, 0xa0130805, 0x0130804c, 0x020805c2, 0x00000000, 0x00000000, +0x62400010, 0x5043881c, 0x09184042, 0x118c2021, 0x2840c210, 0x84242103, +0x41421011, 0x20610908, 0x42101084, 0x61090842, 0x00708424, 0x410842c2, +0x00000000, 0x00000000, 0xe2040800, 0x00b28004, 0x0a180246, 0xb1800820, +0x28034e00, 0x80042001, 0x03420030, 0x2ca00908, 0x8e003388, 0x200b0802, +0x00938008, 0x433800ce, 0x00000020, 0x00000000, 0xa1000010, 0x5802a000, +0x0918408e, 0x63841821, 0x08400e10, 0x84206108, 0x40821010, 0x00210e08, +0x04108084, 0x210f0840, 0x10608418, 0x4008408e, 0x00000000, 0x00000000, +0x80440000, 0x24e02100, 0x4430108e, 0x90810c00, 0x00104204, 0x01188042, +0x10400490, 0x00004300, 0x0a042201, 0x00410010, 0x0410012c, 0x0000128a, +0x00000000, 0x00000000, 0xc1400000, 0x10120410, 0x0132834a, 0x30801401, +0x0040c210, 0x04044101, 0x40801010, 0x10010300, 0x46101008, 0x81030043, +0x30700404, 0x00004246, 0x00000000, 0x00000000, 0x80030800, 0x00122010, +0x0130018c, 0x10000800, 0x00024000, 0x00044009, 0x02000070, 0x20000300, +0x42009100, 0x80030002, 0x00300008, 0x000002c8, 0x00000420, 0x00000000, +0x0808000c, 0x04020002, 0x0b000800, 0x02080200, 0x00000104, 0x0801000b, +0x00010000, 0x00040b00, 0x01000008, 0x000b0000, 0x00000800, 0x33000001, +0x00000000, 0x00000000, 0x0308040c, 0x00000101, 0x09080808, 0x0001010b, +0x08000800, 0x05010b09, 0x000a0000, 0x01030908, 0x0a000005, 0x03090800, +0x00000503, 0x3108000a, 0x00000010, 0x00000000, 0xced7e000, 0xf8014f19, +0x9a481a7f, 0x02000201, 0x00080194, 0x00030000, 0x0c000c03, 0x03000000, +0x000c0300, 0x9800000c, 0x0c020000, 0x00000c00, 0x00000f80, 0x00000000, +0x0da00000, 0x280303b1, 0x01402208, 0x00000100, 0x02000000, 0x00000000, +0x08000800, 0x00000000, 0x00000100, 0x00000088, 0x00010000, 0x00000000, +0x00000000, 0x00000000, 0x82280000, 0x11118713, 0x01414081, 0x44000000, +0x00000100, 0x00000000, 0x00030044, 0x00000002, 0x01004400, 0x00000000, +0x00440000, 0x00020002, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0xf4000000, 0x03ffffff, 0x00000000, 0xfffffffc, 0x00000001, 0xfbfffc00, +0x000000ff, 0xfffc0000, 0x0000fffe, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0xf4000000, 0x03efffff, 0x00000000, 0xfef7fbfc, +0x00000003, 0xfffffc00, 0x000001fe, 0xfffc0000, 0x0003fdff, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xf8000000, 0x03fbfffb, +0x00000000, 0xfbfffffc, 0x00000003, 0xfffffc00, 0x000003f3, 0xf7fc0000, +0x0003f3ff, 0x00000000, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0xfc000000, 0x03bfdfef, 0x00000000, 0xefe7fffc, 0x00000003, 0xf7fffc00, +0x000003df, 0xfffc0000, 0x0003fcf7, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0xfc000000, 0x03ffffbf, 0x00000000, 0xfffdfffc, +0x00000003, 0xfffffc00, 0x000003ff, 0xfffc0000, 0x00037fff, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xfc000000, 0x03ffffff, +0x00000000, 0xfffffffc, 0x00000003, 0xfffffc00, 0x000003ff, 0xfffc0000, +0x00037fff, 0x00000000, 0x00000000, 0x00000000, 0x62000000, 0x14308420, +0x4138800e, 0x238c3021, 0x08104234, 0x8534e341, 0xd182b410, 0x10214408, +0x0614808d, 0x20440850, 0x14530518, 0x00089002, 0x00000000, 0x00000000, +0xe2000000, 0x00a28000, 0x0038000a, 0xb3805020, 0x18028200, 0x80542012, +0x04420020, 0x40200208, 0x0a01a080, 0x22000802, 0x01708004, 0x00080202, +0x00000000, 0x00000000, 0xa1100000, 0x11538464, 0x1928544e, 0x308d4421, +0x28c44211, 0x84402319, 0x44823010, 0x00210108, 0x4631508c, 0x230008c0, +0x10108400, 0x00080002, 0x00000000, 0x00000000, 0x60100000, 0x01a08164, +0x1928144a, 0x33884422, 0x08044201, 0x8844e019, 0x06820140, 0x40201908, +0x460110a0, 0x20180806, 0x01808040, 0x00080602, 0x00000000, 0x00000000, +0x02000000, 0x10100428, 0x01008040, 0x500c0401, 0x00c04030, 0x04000310, +0x80003041, 0x50010100, 0x0811010c, 0x41112044, 0x11100454, 0x00004400, +0x00000000, 0x00000000, 0x82400010, 0x04230104, 0x4210904c, 0x01010840, +0x00108004, 0x01048040, 0x90000402, 0x00004200, 0x00040201, 0x80421010, +0x04000108, 0x40001000, 0x00000000, 0x00000000, 0x41500010, 0x2112014c, +0x11209444, 0x920d7481, 0x20c74011, 0x06704318, 0x47003181, 0x70011900, +0x4821800c, 0x431122c7, 0x11900474, 0x4100c700, 0x00000000, 0x00000000, +0x00100010, 0x21830044, 0x1020844c, 0x12085002, 0x20040001, 0x08580010, +0x04000180, 0x50001000, 0x40210000, 0x00102004, 0x01000054, 0x41000400, +0x00000000, 0x00000000, 0x20100010, 0x11028448, 0x5608440a, 0x028c4c21, +0x08d44631, 0x8444e153, 0x55023520, 0x44235238, 0x0e55d084, 0x21580855, +0x15e38d48, 0x41085502, 0x00000000, 0x00000000, 0x62100010, 0x05018174, +0x1d28144e, 0x72816c00, 0x0804ca21, 0x80642017, 0x04420112, 0x6c201708, +0x4201a081, 0x20180884, 0x01a08064, 0x42080602, 0x00000000, 0x00000000, +0xa1000010, 0x24828510, 0x0118508a, 0x500d0c23, 0x0ac24230, 0x8d082307, +0xc0021071, 0x0c230b08, 0xc030108d, 0x21080a40, 0x10100000, 0x41084002, +0x00000000, 0x00000000, 0xe0480810, 0x20b3812c, 0x0b08128e, 0xa08808c0, +0x38008600, 0x8104e00a, 0x00820010, 0x10e00038, 0xc2003380, 0xe0033800, +0x0000a014, 0x423800ce, 0x00000020, 0x00000000, 0x22000010, 0x14028024, +0x04380006, 0x118d1c21, 0x08c00630, 0x84002102, 0x42023072, 0x00230508, +0x02108185, 0x61040842, 0x10008c10, 0x40084242, 0x00000000, 0x00000000, +0xc2400000, 0x00210000, 0x4a20008c, 0x73802c00, 0x12120820, 0x81300041, +0x10000402, 0x1000c900, 0x000c0200, 0x80481080, 0x04020104, 0x00001180, +0x00000000, 0x00000000, 0x81440000, 0x14520424, 0x0920434c, 0x508c0c03, +0x20c04430, 0x8c040305, 0xc24010c0, 0x00030300, 0x4030000c, 0x01012042, +0x10410c00, 0x00004240, 0x00000000, 0x00000000, 0x40050800, 0x04900114, +0x01200244, 0x13890800, 0x00014004, 0x00040007, 0x01400032, 0x00000300, +0x40008000, 0x40010011, 0x00810008, 0x00000000, 0x00000420, 0x00000000, +0x08080000, 0x00000802, 0x0b000401, 0x00000000, 0x00000000, 0x0800000b, +0x00010000, 0x00000b00, 0x01000008, 0x000b0000, 0x00000800, 0x03000001, +0x00000000, 0x00000000, 0x03083c3c, 0x00000501, 0x0908080a, 0x00050303, +0x08000a00, 0x05030309, 0x000a0000, 0x010f0908, 0x0a000005, 0x0b090800, +0x00000503, 0xf108000a, 0x000000f0, 0x00000000, 0x7b1c0000, 0xdc00fb8d, +0x01f35c9c, 0x03980300, 0x01900000, 0x00030000, 0x0c000403, 0x03000000, +0x00000300, 0x00000000, 0x0c030001, 0x00000c00, 0x00000000, 0x00000000, +0x82e80000, 0x5002a165, 0x0030cc35, 0x01000000, 0x00800008, 0x00010000, +0x08000001, 0x01000000, 0x02080100, 0x00000008, 0x08010001, 0x00008800, +0x00000000, 0x00000000, 0x24180000, 0x15101b22, 0x01142921, 0x44000000, +0x00000100, 0x00000000, 0x00010044, 0x00000001, 0x00004400, 0x00000200, +0x00440000, 0x00000001, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0x78000000, 0x03ef7be7, 0x00000000, 0x7ffffff8, 0x00000003, 0x677e7c00, +0x0000026f, 0x6bfc0000, 0x0002fff5, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0x78000000, 0x03eb7fef, 0x00000000, 0xe7e66e7c, +0x00000003, 0x777ffc00, 0x000003fd, 0x5cfc0000, 0x0003fefe, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xf8000000, 0x03fbfbf7, +0x00000000, 0xf3f1f9f4, 0x00000003, 0xfdf3f800, 0x000003f2, 0xfdf80000, +0x0003fefd, 0x00000000, 0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, +0xfc000000, 0x035fffff, 0x00000000, 0xfffffffc, 0x00000003, 0xfffffc00, +0x000003ff, 0xfffc0000, 0x0000fff7, 0x00000000, 0x00000000, 0x00000000, +0xfffc0000, 0x0003ffff, 0x9c000000, 0x03bfffdf, 0x00000000, 0xfffffffc, +0x00000001, 0xffbffc00, 0x000003ff, 0xfffc0000, 0x0003ffff, 0x00000000, +0x00000000, 0x00000000, 0xfffc0000, 0x0003ffff, 0xfc000000, 0x03bfffff, +0x00000000, 0xffffbffc, 0x00000003, 0xfffffc00, 0x000003ff, 0xfffc0000, +0x0003ffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x8004000c, 0x00000040, 0x71c2000c, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x8004000c, 0x00004040, 0x01c2000c, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x8000000c, 0x18c80000, 0x8001000c, 0xc0000000, 0x7002000c, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x8001000c, 0xa0000000, 0x8005000c, 0x00000000, 0x8000000c, +0xe9d60000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; diff -urN linux-2.4.21-rc1.orig/sound/pci/rme9652/rme9652.c linux/sound/pci/rme9652/rme9652.c --- linux-2.4.21-rc1.orig/sound/pci/rme9652/rme9652.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/rme9652/rme9652.c 2003-03-01 12:03:22.000000000 -0700 @@ -0,0 +1,2770 @@ +/* + * ALSA driver for RME Digi9652 audio interfaces + * + * Copyright (c) 1999 IEM - Winfried Ritsch + * Copyright (c) 1999-2001 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include +#include + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for RME Digi9652 (Hammerfall) soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable/disable specific RME96{52,36} soundcards."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(precise_ptr, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(precise_ptr, "Enable precise pointer (doesn't work reliably)."); +MODULE_PARM_SYNTAX(precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_AUTHOR("Paul Davis , Winfried Ritsch"); +MODULE_DESCRIPTION("RME Digi9652/Digi9636"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Hammerfall}," + "{RME,Hammerfall-Light}}"); + +/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for + capture, one for playback. Both the ADAT and S/PDIF channels appear + to the host CPU in the same block of memory. There is no functional + difference between them in terms of access. + + The Hammerfall Light is identical to the Hammerfall, except that it + has 2 sets 18 channels (16 ADAT + 2 S/PDIF) for capture and playback. +*/ + +#define RME9652_NCHANNELS 26 +#define RME9636_NCHANNELS 18 + +/* Preferred sync source choices - used by "sync_pref" control switch */ + +#define RME9652_SYNC_FROM_SPDIF 0 +#define RME9652_SYNC_FROM_ADAT1 1 +#define RME9652_SYNC_FROM_ADAT2 2 +#define RME9652_SYNC_FROM_ADAT3 3 + +/* Possible sources of S/PDIF input */ + +#define RME9652_SPDIFIN_OPTICAL 0 /* optical (ADAT1) */ +#define RME9652_SPDIFIN_COAXIAL 1 /* coaxial (RCA) */ +#define RME9652_SPDIFIN_INTERN 2 /* internal (CDROM) */ + +/* ------------- Status-Register bits --------------------- */ + +#define RME9652_IRQ (1<<0) /* IRQ is High if not reset by irq_clear */ +#define RME9652_lock_2 (1<<1) /* ADAT 3-PLL: 1=locked, 0=unlocked */ +#define RME9652_lock_1 (1<<2) /* ADAT 2-PLL: 1=locked, 0=unlocked */ +#define RME9652_lock_0 (1<<3) /* ADAT 1-PLL: 1=locked, 0=unlocked */ +#define RME9652_fs48 (1<<4) /* sample rate is 0=44.1/88.2,1=48/96 Khz */ +#define RME9652_wsel_rd (1<<5) /* if Word-Clock is used and valid then 1 */ + /* bits 6-15 encode h/w buffer pointer position */ +#define RME9652_sync_2 (1<<16) /* if ADAT-IN 3 in sync to system clock */ +#define RME9652_sync_1 (1<<17) /* if ADAT-IN 2 in sync to system clock */ +#define RME9652_sync_0 (1<<18) /* if ADAT-IN 1 in sync to system clock */ +#define RME9652_DS_rd (1<<19) /* 1=Double Speed Mode, 0=Normal Speed */ +#define RME9652_tc_busy (1<<20) /* 1=time-code copy in progress (960ms) */ +#define RME9652_tc_out (1<<21) /* time-code out bit */ +#define RME9652_F_0 (1<<22) /* 000=64kHz, 100=88.2kHz, 011=96kHz */ +#define RME9652_F_1 (1<<23) /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ +#define RME9652_F_2 (1<<24) /* external Crystal Chip if ERF=1 */ +#define RME9652_ERF (1<<25) /* Error-Flag of SDPIF Receiver (1=No Lock) */ +#define RME9652_buffer_id (1<<26) /* toggles by each interrupt on rec/play */ +#define RME9652_tc_valid (1<<27) /* 1 = a signal is detected on time-code input */ +#define RME9652_SPDIF_READ (1<<28) /* byte available from Rev 1.5+ S/PDIF interface */ + +#define RME9652_sync (RME9652_sync_0|RME9652_sync_1|RME9652_sync_2) +#define RME9652_lock (RME9652_lock_0|RME9652_lock_1|RME9652_lock_2) +#define RME9652_F (RME9652_F_0|RME9652_F_1|RME9652_F_2) +#define rme9652_decode_spdif_rate(x) ((x)>>22) + +/* Bit 6..15 : h/w buffer pointer */ + +#define RME9652_buf_pos 0x000FFC0 + +/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later + Rev G EEPROMS and Rev 1.5 cards or later. +*/ + +#define RME9652_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME9652_buf_pos)) + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL +#define PCI_DEVICE_ID_XILINX_HAMMERFALL 0x3fc4 +#endif + +/* amount of io space we remap for register access. i'm not sure we + even need this much, but 1K is nice round number :) +*/ + +#define RME9652_IO_EXTENT 1024 + +#define RME9652_init_buffer 0 +#define RME9652_play_buffer 32 /* holds ptr to 26x64kBit host RAM */ +#define RME9652_rec_buffer 36 /* holds ptr to 26x64kBit host RAM */ +#define RME9652_control_register 64 +#define RME9652_irq_clear 96 +#define RME9652_time_code 100 /* useful if used with alesis adat */ +#define RME9652_thru_base 128 /* 132...228 Thru for 26 channels */ + +/* Read-only registers */ + +/* Writing to any of the register locations writes to the status + register. We'll use the first location as our point of access. +*/ + +#define RME9652_status_register 0 + +/* --------- Control-Register Bits ---------------- */ + + +#define RME9652_start_bit (1<<0) /* start record/play */ + /* bits 1-3 encode buffersize/latency */ +#define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */ +#define RME9652_IE (1<<5) /* Interupt Enable */ +#define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */ +#define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */ +#define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ +#define RME9652_PRO (1<<9) /* S/PDIF out: 0=consumer, 1=professional */ +#define RME9652_EMP (1<<10) /* Emphasis 0=None, 1=ON */ +#define RME9652_Dolby (1<<11) /* Non-audio bit 1=set, 0=unset */ +#define RME9652_opt_out (1<<12) /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ +#define RME9652_wsel (1<<13) /* use Wordclock as sync (overwrites master) */ +#define RME9652_inp_0 (1<<14) /* SPDIF-IN: 00=optical (ADAT1), */ +#define RME9652_inp_1 (1<<15) /* 01=koaxial (Cinch), 10=Internal CDROM */ +#define RME9652_SyncPref_ADAT2 (1<<16) +#define RME9652_SyncPref_ADAT3 (1<<17) +#define RME9652_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w S/PDIF receiver */ +#define RME9652_SPDIF_SELECT (1<<19) +#define RME9652_SPDIF_CLOCK (1<<20) +#define RME9652_SPDIF_WRITE (1<<21) +#define RME9652_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */ + +/* buffersize = 512Bytes * 2^n, where n is made from Bit2 ... Bit0 */ + +#define RME9652_latency 0x0e +#define rme9652_encode_latency(x) (((x)&0x7)<<1) +#define rme9652_decode_latency(x) (((x)>>1)&0x7) +#define rme9652_running_double_speed(s) ((s)->control_register & RME9652_DS) +#define RME9652_inp (RME9652_inp_0|RME9652_inp_1) +#define rme9652_encode_spdif_in(x) (((x)&0x3)<<14) +#define rme9652_decode_spdif_in(x) (((x)>>14)&0x3) + +#define RME9652_SyncPref_Mask (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3) +#define RME9652_SyncPref_ADAT1 0 +#define RME9652_SyncPref_SPDIF (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3) + +/* the size of a substream (1 mono data stream) */ + +#define RME9652_CHANNEL_BUFFER_SAMPLES (16*1024) +#define RME9652_CHANNEL_BUFFER_BYTES (4*RME9652_CHANNEL_BUFFER_SAMPLES) + +/* the size of the area we need to allocate for DMA transfers. the + size is the same regardless of the number of channels - the + 9636 still uses the same memory area. + + Note that we allocate 1 more channel than is apparently needed + because the h/w seems to write 1 byte beyond the end of the last + page. Sigh. +*/ + +#define RME9652_DMA_AREA_BYTES ((RME9652_NCHANNELS+1) * RME9652_CHANNEL_BUFFER_BYTES) +#define RME9652_DMA_AREA_KILOBYTES (RME9652_DMA_AREA_BYTES/1024) + +typedef struct snd_rme9652 { + int dev; + + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + int precise_ptr; + + u32 control_register; /* cached value */ + u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ + + u32 creg_spdif; + u32 creg_spdif_stream; + + char *card_name; /* hammerfall or hammerfall light names */ + + size_t hw_offsetmask; /* &-with status register to get real hw_offset */ + size_t prev_hw_offset; /* previous hw offset */ + size_t max_jitter; /* maximum jitter in frames for + hw pointer */ + size_t period_bytes; /* guess what this is */ + + unsigned char ds_channels; + unsigned char ss_channels; /* different for hammerfall/hammerfall-light */ + + void *capture_buffer_unaligned; /* original buffer addresses */ + void *playback_buffer_unaligned; /* original buffer addresses */ + unsigned char *capture_buffer; /* suitably aligned address */ + unsigned char *playback_buffer; /* suitably aligned address */ + dma_addr_t capture_buffer_addr; + dma_addr_t playback_buffer_addr; + + pid_t capture_pid; + pid_t playback_pid; + + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback_substream; + int running; + + int passthru; /* non-zero if doing pass-thru */ + int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */ + + int last_spdif_sample_rate; /* so that we can catch externally ... */ + int last_adat_sample_rate; /* ... induced rate changes */ + + char *channel_map; + + snd_card_t *card; + snd_pcm_t *pcm; + struct pci_dev *pci; + snd_kcontrol_t *spdif_ctl; + +} rme9652_t; + +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refer to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_9652_ss[26] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25 +}; + +static char channel_map_9636_ss[26] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + /* channels 16 and 17 are S/PDIF */ + 24, 25, + /* channels 18-25 don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_9652_ds[26] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, + /* channels 12 and 13 are S/PDIF */ + 24, 25, + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_9636_ds[26] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, + /* channels 8 and 9 are S/PDIF */ + 24, 25 + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#define RME9652_PREALLOCATE_MEMORY /* via module snd-hammerfall-mem */ + +#ifdef RME9652_PREALLOCATE_MEMORY +extern void *snd_hammerfall_get_buffer(struct pci_dev *, dma_addr_t *dmaaddr); +extern void snd_hammerfall_free_buffer(struct pci_dev *, void *ptr); +#endif + +static struct pci_device_id snd_rme9652_ids[] __devinitdata = { + { + .vendor = 0x10ee, + .device = 0x3fc4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, /* RME Digi9652 */ + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, snd_rme9652_ids); + +static inline void rme9652_write(rme9652_t *rme9652, int reg, int val) +{ + writel(val, rme9652->iobase + reg); +} + +static inline unsigned int rme9652_read(rme9652_t *rme9652, int reg) +{ + return readl(rme9652->iobase + reg); +} + +static inline int snd_rme9652_use_is_exclusive(rme9652_t *rme9652) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&rme9652->lock, flags); + if ((rme9652->playback_pid != rme9652->capture_pid) && + (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) { + ret = 0; + } + spin_unlock_irqrestore(&rme9652->lock, flags); + return ret; +} + +static inline int rme9652_adat_sample_rate(rme9652_t *rme9652) +{ + if (rme9652_running_double_speed(rme9652)) { + return (rme9652_read(rme9652, RME9652_status_register) & + RME9652_fs48) ? 96000 : 88200; + } else { + return (rme9652_read(rme9652, RME9652_status_register) & + RME9652_fs48) ? 48000 : 44100; + } +} + +static inline void rme9652_compute_period_size(rme9652_t *rme9652) +{ + unsigned int i; + + i = rme9652->control_register & RME9652_latency; + rme9652->period_bytes = 1 << ((rme9652_decode_latency(i) + 8)); + rme9652->hw_offsetmask = + (rme9652->period_bytes * 2 - 1) & RME9652_buf_pos; + rme9652->max_jitter = 80; +} + +static snd_pcm_uframes_t rme9652_hw_pointer(rme9652_t *rme9652) +{ + int status; + unsigned int offset, frag; + snd_pcm_uframes_t period_size = rme9652->period_bytes / 4; + snd_pcm_sframes_t delta; + + status = rme9652_read(rme9652, RME9652_status_register); + if (!rme9652->precise_ptr) + return (status & RME9652_buffer_id) ? period_size : 0; + offset = status & RME9652_buf_pos; + + /* The hardware may give a backward movement for up to 80 frames + Martin Kirst knows the details. + */ + + delta = rme9652->prev_hw_offset - offset; + delta &= 0xffff; + if (delta <= (snd_pcm_sframes_t)rme9652->max_jitter * 4) + offset = rme9652->prev_hw_offset; + else + rme9652->prev_hw_offset = offset; + offset &= rme9652->hw_offsetmask; + offset /= 4; + frag = status & RME9652_buffer_id; + + if (offset < period_size) { + if (offset > rme9652->max_jitter) { + if (frag) + printk(KERN_ERR "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset); + } else if (!frag) + return 0; + offset -= rme9652->max_jitter; + if (offset < 0) + offset += period_size * 2; + } else { + if (offset > period_size + rme9652->max_jitter) { + if (!frag) + printk(KERN_ERR "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset); + } else if (frag) + return period_size; + offset -= rme9652->max_jitter; + } + + return offset; +} + +static inline void rme9652_reset_hw_pointer(rme9652_t *rme9652) +{ + int i; + + /* reset the FIFO pointer to zero. We do this by writing to 8 + registers, each of which is a 32bit wide register, and set + them all to zero. Note that s->iobase is a pointer to + int32, not pointer to char. + */ + + for (i = 0; i < 8; i++) { + rme9652_write(rme9652, i * 4, 0); + udelay(10); + } + rme9652->prev_hw_offset = 0; +} + +static inline void rme9652_start(rme9652_t *s) +{ + s->control_register |= (RME9652_IE | RME9652_start_bit); + rme9652_write(s, RME9652_control_register, s->control_register); +} + +static inline void rme9652_stop(rme9652_t *s) +{ + s->control_register &= ~(RME9652_start_bit | RME9652_IE); + rme9652_write(s, RME9652_control_register, s->control_register); +} + +static int rme9652_set_interrupt_interval(rme9652_t *s, + unsigned int frames) +{ + int restart = 0; + int n; + + spin_lock_irq(&s->lock); + + if ((restart = s->running)) { + rme9652_stop(s); + } + + frames >>= 7; + n = 0; + while (frames) { + n++; + frames >>= 1; + } + + s->control_register &= ~RME9652_latency; + s->control_register |= rme9652_encode_latency(n); + + rme9652_write(s, RME9652_control_register, s->control_register); + + rme9652_compute_period_size(s); + + if (restart) + rme9652_start(s); + + spin_unlock_irq(&s->lock); + + return 0; +} + +static int rme9652_set_rate(rme9652_t *rme9652, int rate) +{ + int restart; + int reject_if_open = 0; + int xrate; + + if (!snd_rme9652_use_is_exclusive (rme9652)) { + return -EBUSY; + } + + /* Changing from a "single speed" to a "double speed" rate is + not allowed if any substreams are open. This is because + such a change causes a shift in the location of + the DMA buffers and a reduction in the number of available + buffers. + + Note that a similar but essentially insoluble problem + exists for externally-driven rate changes. All we can do + is to flag rate changes in the read/write routines. + */ + + spin_lock_irq(&rme9652->lock); + xrate = rme9652_adat_sample_rate(rme9652); + + switch (rate) { + case 44100: + if (xrate > 48000) { + reject_if_open = 1; + } + rate = 0; + break; + case 48000: + if (xrate > 48000) { + reject_if_open = 1; + } + rate = RME9652_freq; + break; + case 88200: + if (xrate < 48000) { + reject_if_open = 1; + } + rate = RME9652_DS; + break; + case 96000: + if (xrate < 48000) { + reject_if_open = 1; + } + rate = RME9652_DS | RME9652_freq; + break; + default: + spin_unlock_irq(&rme9652->lock); + return -EINVAL; + } + + if (reject_if_open && (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) { + spin_unlock_irq(&rme9652->lock); + return -EBUSY; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + rme9652->control_register &= ~(RME9652_freq | RME9652_DS); + rme9652->control_register |= rate; + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + if (rate & RME9652_DS) { + if (rme9652->ss_channels == RME9652_NCHANNELS) { + rme9652->channel_map = channel_map_9652_ds; + } else { + rme9652->channel_map = channel_map_9636_ds; + } + } else { + if (rme9652->ss_channels == RME9652_NCHANNELS) { + rme9652->channel_map = channel_map_9652_ss; + } else { + rme9652->channel_map = channel_map_9636_ss; + } + } + + spin_unlock_irq(&rme9652->lock); + return 0; +} + +static void rme9652_set_thru(rme9652_t *rme9652, int channel, int enable) +{ + int i; + + rme9652->passthru = 0; + + if (channel < 0) { + + /* set thru for all channels */ + + if (enable) { + for (i = 0; i < RME9652_NCHANNELS; i++) { + rme9652->thru_bits |= (1 << i); + rme9652_write(rme9652, RME9652_thru_base + i * 4, 1); + } + } else { + for (i = 0; i < RME9652_NCHANNELS; i++) { + rme9652->thru_bits &= ~(1 << i); + rme9652_write(rme9652, RME9652_thru_base + i * 4, 0); + } + } + + } else { + int mapped_channel; + + snd_assert(channel == RME9652_NCHANNELS, return); + + mapped_channel = rme9652->channel_map[channel]; + + if (enable) { + rme9652->thru_bits |= (1 << mapped_channel); + } else { + rme9652->thru_bits &= ~(1 << mapped_channel); + } + + rme9652_write(rme9652, + RME9652_thru_base + mapped_channel * 4, + enable ? 1 : 0); + } +} + +static int rme9652_set_passthru(rme9652_t *rme9652, int onoff) +{ + if (onoff) { + rme9652_set_thru(rme9652, -1, 1); + + /* we don't want interrupts, so do a + custom version of rme9652_start(). + */ + + rme9652->control_register = + RME9652_inp_0 | + rme9652_encode_latency(7) | + RME9652_start_bit; + + rme9652_reset_hw_pointer(rme9652); + + rme9652_write(rme9652, RME9652_control_register, + rme9652->control_register); + rme9652->passthru = 1; + } else { + rme9652_set_thru(rme9652, -1, 0); + rme9652_stop(rme9652); + rme9652->passthru = 0; + } + + return 0; +} + +static void rme9652_spdif_set_bit (rme9652_t *rme9652, int mask, int onoff) +{ + if (onoff) + rme9652->control_register |= mask; + else + rme9652->control_register &= ~mask; + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); +} + +static void rme9652_spdif_write_byte (rme9652_t *rme9652, const int val) +{ + long mask; + long i; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + if (val & mask) + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 1); + else + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 0); + + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0); + } +} + +static int rme9652_spdif_read_byte (rme9652_t *rme9652) +{ + long mask; + long val; + long i; + + val = 0; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1); + if (rme9652_read (rme9652, RME9652_status_register) & RME9652_SPDIF_READ) + val |= mask; + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0); + } + + return val; +} + +static void rme9652_write_spdif_codec (rme9652_t *rme9652, const int address, const int data) +{ + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + rme9652_spdif_write_byte (rme9652, 0x20); + rme9652_spdif_write_byte (rme9652, address); + rme9652_spdif_write_byte (rme9652, data); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); +} + + +static int rme9652_spdif_read_codec (rme9652_t *rme9652, const int address) +{ + int ret; + + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + rme9652_spdif_write_byte (rme9652, 0x20); + rme9652_spdif_write_byte (rme9652, address); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + + rme9652_spdif_write_byte (rme9652, 0x21); + ret = rme9652_spdif_read_byte (rme9652); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); + + return ret; +} + +static void rme9652_initialize_spdif_receiver (rme9652_t *rme9652) +{ + /* XXX what unsets this ? */ + + rme9652->control_register |= RME9652_SPDIF_RESET; + + rme9652_write_spdif_codec (rme9652, 4, 0x40); + rme9652_write_spdif_codec (rme9652, 17, 0x13); + rme9652_write_spdif_codec (rme9652, 6, 0x02); +} + +static inline int rme9652_spdif_sample_rate(rme9652_t *s) +{ + unsigned int rate_bits; + + if (rme9652_read(s, RME9652_status_register) & RME9652_ERF) { + return -1; /* error condition */ + } + + if (s->hw_rev == 15) { + + int x, y, ret; + + x = rme9652_spdif_read_codec (s, 30); + + if (x != 0) + y = 48000 * 64 / x; + else + y = 0; + + if (y > 30400 && y < 33600) ret = 32000; + else if (y > 41900 && y < 46000) ret = 44100; + else if (y > 46000 && y < 50400) ret = 48000; + else if (y > 60800 && y < 67200) ret = 64000; + else if (y > 83700 && y < 92000) ret = 88200; + else if (y > 92000 && y < 100000) ret = 96000; + else ret = 0; + return ret; + } + + rate_bits = rme9652_read(s, RME9652_status_register) & RME9652_F; + + switch (rme9652_decode_spdif_rate(rate_bits)) { + case 0x7: + return 32000; + break; + + case 0x6: + return 44100; + break; + + case 0x5: + return 48000; + break; + + case 0x4: + return 88200; + break; + + case 0x3: + return 96000; + break; + + case 0x0: + return 64000; + break; + + default: + snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n", + s->card_name, rate_bits); + return 0; + break; + } +} + +/*----------------------------------------------------------------------------- + Control Interface + ----------------------------------------------------------------------------*/ + +static u32 snd_rme9652_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME9652_PRO : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME9652_Dolby : 0; + if (val & RME9652_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME9652_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME9652_EMP : 0; + return val; +} + +static void snd_rme9652_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & RME9652_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & RME9652_Dolby) ? IEC958_AES0_NONAUDIO : 0); + if (val & RME9652_PRO) + aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme9652_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif); + return 0; +} + +static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652->creg_spdif; + rme9652->creg_spdif = val; + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream); + return 0; +} + +static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652->creg_spdif_stream; + rme9652->creg_spdif_stream = val; + rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +#define RME9652_ADAT1_IN(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_rme9652_info_adat1_in, \ + .get = snd_rme9652_get_adat1_in, \ + .put = snd_rme9652_put_adat1_in } + +static unsigned int rme9652_adat1_in(rme9652_t *rme9652) +{ + if (rme9652->control_register & RME9652_ADAT1_INTERNAL) + return 1; + return 0; +} + +static int rme9652_set_adat1_input(rme9652_t *rme9652, int internal) +{ + int restart = 0; + + if (internal) { + rme9652->control_register |= RME9652_ADAT1_INTERNAL; + } else { + rme9652->control_register &= ~RME9652_ADAT1_INTERNAL; + } + + /* XXX do we actually need to stop the card when we do this ? */ + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_adat1_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[2] = {"ADAT1", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 2; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_adat1_in(rme9652); + if (change) + rme9652_set_adat1_input(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SPDIF_IN(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_rme9652_info_spdif_in, \ + .get = snd_rme9652_get_spdif_in, .put = snd_rme9652_put_spdif_in } + +static unsigned int rme9652_spdif_in(rme9652_t *rme9652) +{ + return rme9652_decode_spdif_in(rme9652->control_register & + RME9652_inp); +} + +static int rme9652_set_spdif_input(rme9652_t *rme9652, int in) +{ + int restart = 0; + + rme9652->control_register &= ~RME9652_inp; + rme9652->control_register |= rme9652_encode_spdif_in(in); + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"ADAT1", "Coaxial", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_spdif_in(rme9652); + if (change) + rme9652_set_spdif_input(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SPDIF_OUT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_rme9652_info_spdif_out, \ + .get = snd_rme9652_get_spdif_out, .put = snd_rme9652_put_spdif_out } + +static int rme9652_spdif_out(rme9652_t *rme9652) +{ + return (rme9652->control_register & RME9652_opt_out) ? 1 : 0; +} + +static int rme9652_set_spdif_output(rme9652_t *rme9652, int out) +{ + int restart = 0; + + if (out) { + rme9652->control_register |= RME9652_opt_out; + } else { + rme9652->control_register &= ~RME9652_opt_out; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_spdif_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&rme9652->lock, flags); + change = (int)val != rme9652_spdif_out(rme9652); + rme9652_set_spdif_output(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SYNC_MODE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_rme9652_info_sync_mode, \ + .get = snd_rme9652_get_sync_mode, .put = snd_rme9652_put_sync_mode } + +static int rme9652_sync_mode(rme9652_t *rme9652) +{ + if (rme9652->control_register & RME9652_wsel) { + return 2; + } else if (rme9652->control_register & RME9652_Master) { + return 1; + } else { + return 0; + } +} + +static int rme9652_set_sync_mode(rme9652_t *rme9652, int mode) +{ + int restart = 0; + + switch (mode) { + case 0: + rme9652->control_register &= + ~(RME9652_Master | RME9652_wsel); + break; + case 1: + rme9652->control_register = + (rme9652->control_register & ~RME9652_wsel) | RME9652_Master; + break; + case 2: + rme9652->control_register |= + (RME9652_Master | RME9652_wsel); + break; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_sync_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"AutoSync", "Master", "Word Clock"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme9652->lock, flags); + change = (int)val != rme9652_sync_mode(rme9652); + rme9652_set_sync_mode(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SYNC_PREF(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_rme9652_info_sync_pref, \ + .get = snd_rme9652_get_sync_pref, .put = snd_rme9652_put_sync_pref } + +static int rme9652_sync_pref(rme9652_t *rme9652) +{ + switch (rme9652->control_register & RME9652_SyncPref_Mask) { + case RME9652_SyncPref_ADAT1: + return RME9652_SYNC_FROM_ADAT1; + case RME9652_SyncPref_ADAT2: + return RME9652_SYNC_FROM_ADAT2; + case RME9652_SyncPref_ADAT3: + return RME9652_SYNC_FROM_ADAT3; + case RME9652_SyncPref_SPDIF: + return RME9652_SYNC_FROM_SPDIF; + } + /* Not reachable */ + return 0; +} + +static int rme9652_set_sync_pref(rme9652_t *rme9652, int pref) +{ + int restart; + + rme9652->control_register &= ~RME9652_SyncPref_Mask; + switch (pref) { + case RME9652_SYNC_FROM_ADAT1: + rme9652->control_register |= RME9652_SyncPref_ADAT1; + break; + case RME9652_SYNC_FROM_ADAT2: + rme9652->control_register |= RME9652_SyncPref_ADAT2; + break; + case RME9652_SYNC_FROM_ADAT3: + rme9652->control_register |= RME9652_SyncPref_ADAT3; + break; + case RME9652_SYNC_FROM_SPDIF: + rme9652->control_register |= RME9652_SyncPref_SPDIF; + break; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"}; + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, max; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; + val = ucontrol->value.enumerated.item[0] % max; + spin_lock_irqsave(&rme9652->lock, flags); + change = (int)val != rme9652_sync_pref(rme9652); + rme9652_set_sync_pref(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = rme9652->ss_channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned int k; + u32 thru_bits = rme9652->thru_bits; + + for (k = 0; k < rme9652->ss_channels; ++k) { + ucontrol->value.integer.value[k] = !!(thru_bits & (1 << k)); + } + return 0; +} + +static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int chn; + u32 thru_bits = 0; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + + for (chn = 0; chn < rme9652->ss_channels; ++chn) { + if (ucontrol->value.integer.value[chn]) + thru_bits |= 1 << chn; + } + + spin_lock_irqsave(&rme9652->lock, flags); + change = thru_bits ^ rme9652->thru_bits; + if (change) { + for (chn = 0; chn < rme9652->ss_channels; ++chn) { + if (!(change & (1 << chn))) + continue; + rme9652_set_thru(rme9652,chn,thru_bits&(1<lock, flags); + return !!change; +} + +#define RME9652_PASSTHRU(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .info = snd_rme9652_info_passthru, \ + .put = snd_rme9652_put_passthru, \ + .get = snd_rme9652_get_passthru } + +static int snd_rme9652_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652->passthru; + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + int err = 0; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&rme9652->lock, flags); + change = (ucontrol->value.integer.value[0] != rme9652->passthru); + if (change) + err = rme9652_set_passthru(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return err ? err : change; +} + +/* Read-only switches */ + +#define RME9652_SPDIF_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_rme9652_info_spdif_rate, \ + .get = snd_rme9652_get_spdif_rate } + +static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 96000; + return 0; +} + +static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +#define RME9652_ADAT_SYNC(xname, xindex, xidx) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_rme9652_info_adat_sync, \ + .get = snd_rme9652_get_adat_sync, .private_value = xidx } + +static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned int mask1, mask2, val; + + switch (kcontrol->private_value) { + case 0: mask1 = RME9652_lock_0; mask2 = RME9652_sync_0; break; + case 1: mask1 = RME9652_lock_1; mask2 = RME9652_sync_1; break; + case 2: mask1 = RME9652_lock_2; mask2 = RME9652_sync_2; break; + default: return -EINVAL; + } + val = rme9652_read(rme9652, RME9652_status_register); + ucontrol->value.enumerated.item[0] = (val & mask1) ? 1 : 0; + ucontrol->value.enumerated.item[0] |= (val & mask2) ? 2 : 0; + return 0; +} + +#define RME9652_TC_VALID(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_rme9652_info_tc_valid, \ + .get = snd_rme9652_get_tc_valid } + +static int snd_rme9652_info_tc_valid(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + (rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0; + return 0; +} + +#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE + +/* FIXME: this routine needs a port to the new control API --jk */ + +static int snd_rme9652_get_tc_value(void *private_data, + snd_kswitch_t *kswitch, + snd_switch_t *uswitch) +{ + rme9652_t *s = (rme9652_t *) private_data; + u32 value; + int i; + + uswitch->type = SNDRV_SW_TYPE_DWORD; + + if ((rme9652_read(s, RME9652_status_register) & + RME9652_tc_valid) == 0) { + uswitch->value.data32[0] = 0; + return 0; + } + + /* timecode request */ + + rme9652_write(s, RME9652_time_code, 0); + + /* XXX bug alert: loop-based timing !!!! */ + + for (i = 0; i < 50; i++) { + if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) + break; + } + + if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) { + return -EIO; + } + + value = 0; + + for (i = 0; i < 32; i++) { + value >>= 1; + + if (rme9652_read(s, i * 4) & RME9652_tc_out) + value |= 0x80000000; + } + + if (value > 2 * 60 * 48000) { + value -= 2 * 60 * 48000; + } else { + value = 0; + } + + uswitch->value.data32[0] = value; + + return 0; +} + +#endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */ + +#define RME9652_CONTROLS (sizeof(snd_rme9652_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_rme9652_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_rme9652_control_spdif_info, + .get = snd_rme9652_control_spdif_get, + .put = snd_rme9652_control_spdif_put, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_rme9652_control_spdif_stream_info, + .get = snd_rme9652_control_spdif_stream_get, + .put = snd_rme9652_control_spdif_stream_put, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_rme9652_control_spdif_mask_info, + .get = snd_rme9652_control_spdif_mask_get, + .private_value = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS, +}, +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_rme9652_control_spdif_mask_info, + .get = snd_rme9652_control_spdif_mask_get, + .private_value = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS, +}, +RME9652_SPDIF_IN("IEC958 Input Connector", 0), +RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0), +RME9652_SYNC_MODE("Sync Mode", 0), +RME9652_SYNC_PREF("Preferred Sync Source", 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Channels Thru", + .index = 0, + .info = snd_rme9652_info_thru, + .get = snd_rme9652_get_thru, + .put = snd_rme9652_put_thru, +}, +RME9652_SPDIF_RATE("IEC958 Sample Rate", 0), +RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0), +RME9652_ADAT_SYNC("ADAT2 Sync Check", 0, 1), +RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2), +RME9652_TC_VALID("Timecode Valid", 0), +RME9652_PASSTHRU("Passthru", 0) +}; + +static snd_kcontrol_new_t snd_rme9652_adat3_check = +RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2); + +static snd_kcontrol_new_t snd_rme9652_adat1_input = +RME9652_ADAT1_IN("ADAT1 Input Source", 0); + +int snd_rme9652_create_controls(snd_card_t *card, rme9652_t *rme9652) +{ + unsigned int idx; + int err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < RME9652_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme9652->spdif_ctl = kctl; + } + + if (rme9652->ss_channels == RME9652_NCHANNELS) + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0) + return err; + + if (rme9652->hw_rev >= 15) + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0) + return err; + + return 0; +} + +/*------------------------------------------------------------ + /proc interface + ------------------------------------------------------------*/ + +static void +snd_rme9652_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + rme9652_t *rme9652 = (rme9652_t *) entry->private_data; + u32 thru_bits = rme9652->thru_bits; + int show_auto_sync_source = 0; + int i; + unsigned int status; + int x; + + status = rme9652_read(rme9652, RME9652_status_register); + + snd_iprintf(buffer, "%s (Card #%d)\n", rme9652->card_name, rme9652->card->number + 1); + snd_iprintf(buffer, "Buffers: capture %p playback %p\n", + rme9652->capture_buffer, rme9652->playback_buffer); + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + rme9652->irq, rme9652->port, rme9652->iobase); + snd_iprintf(buffer, "Control register: %x\n", rme9652->control_register); + + snd_iprintf(buffer, "\n"); + + x = 1 << (6 + rme9652_decode_latency(rme9652->control_register & + RME9652_latency)); + + snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) rme9652->period_bytes); + snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", + rme9652_hw_pointer(rme9652)); + snd_iprintf(buffer, "Passthru: %s\n", + rme9652->passthru ? "yes" : "no"); + + if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) { + snd_iprintf(buffer, "Clock mode: autosync\n"); + show_auto_sync_source = 1; + } else if (rme9652->control_register & RME9652_wsel) { + if (status & RME9652_wsel_rd) { + snd_iprintf(buffer, "Clock mode: word clock\n"); + } else { + snd_iprintf(buffer, "Clock mode: word clock (no signal)\n"); + } + } else { + snd_iprintf(buffer, "Clock mode: master\n"); + } + + if (show_auto_sync_source) { + switch (rme9652->control_register & RME9652_SyncPref_Mask) { + case RME9652_SyncPref_ADAT1: + snd_iprintf(buffer, "Pref. sync source: ADAT1\n"); + break; + case RME9652_SyncPref_ADAT2: + snd_iprintf(buffer, "Pref. sync source: ADAT2\n"); + break; + case RME9652_SyncPref_ADAT3: + snd_iprintf(buffer, "Pref. sync source: ADAT3\n"); + break; + case RME9652_SyncPref_SPDIF: + snd_iprintf(buffer, "Pref. sync source: IEC958\n"); + break; + default: + snd_iprintf(buffer, "Pref. sync source: ???\n"); + } + } + + if (rme9652->hw_rev >= 15) + snd_iprintf(buffer, "\nADAT1 Input source: %s\n", + (rme9652->control_register & RME9652_ADAT1_INTERNAL) ? + "Internal" : "ADAT1 optical"); + + snd_iprintf(buffer, "\n"); + + switch (rme9652_decode_spdif_in(rme9652->control_register & + RME9652_inp)) { + case RME9652_SPDIFIN_OPTICAL: + snd_iprintf(buffer, "IEC958 input: ADAT1\n"); + break; + case RME9652_SPDIFIN_COAXIAL: + snd_iprintf(buffer, "IEC958 input: Coaxial\n"); + break; + case RME9652_SPDIFIN_INTERN: + snd_iprintf(buffer, "IEC958 input: Internal\n"); + break; + default: + snd_iprintf(buffer, "IEC958 input: ???\n"); + break; + } + + if (rme9652->control_register & RME9652_opt_out) { + snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n"); + } else { + snd_iprintf(buffer, "IEC958 output: Coaxial only\n"); + } + + if (rme9652->control_register & RME9652_PRO) { + snd_iprintf(buffer, "IEC958 quality: Professional\n"); + } else { + snd_iprintf(buffer, "IEC958 quality: Consumer\n"); + } + + if (rme9652->control_register & RME9652_EMP) { + snd_iprintf(buffer, "IEC958 emphasis: on\n"); + } else { + snd_iprintf(buffer, "IEC958 emphasis: off\n"); + } + + if (rme9652->control_register & RME9652_Dolby) { + snd_iprintf(buffer, "IEC958 Dolby: on\n"); + } else { + snd_iprintf(buffer, "IEC958 Dolby: off\n"); + } + + i = rme9652_spdif_sample_rate(rme9652); + + if (i < 0) { + snd_iprintf(buffer, + "IEC958 sample rate: error flag set\n"); + } else if (i == 0) { + snd_iprintf(buffer, "IEC958 sample rate: undetermined\n"); + } else { + snd_iprintf(buffer, "IEC958 sample rate: %d\n", i); + } + + snd_iprintf(buffer, "\n"); + + snd_iprintf(buffer, "ADAT Sample rate: %dHz\n", + rme9652_adat_sample_rate(rme9652)); + + /* Sync Check */ + + x = status & RME9652_sync_0; + if (status & RME9652_lock_0) { + snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT1: No Lock\n"); + } + + x = status & RME9652_sync_1; + if (status & RME9652_lock_1) { + snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT2: No Lock\n"); + } + + x = status & RME9652_sync_2; + if (status & RME9652_lock_2) { + snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT3: No Lock\n"); + } + + snd_iprintf(buffer, "\n"); + + snd_iprintf(buffer, "Timecode signal: %s\n", + (status & RME9652_tc_valid) ? "yes" : "no"); + + /* thru modes */ + + snd_iprintf(buffer, "Punch Status:\n\n"); + + for (i = 0; i < rme9652->ss_channels; i++) { + if (thru_bits & (1 << i)) { + snd_iprintf(buffer, "%2d: on ", i + 1); + } else { + snd_iprintf(buffer, "%2d: off ", i + 1); + } + + if (((i + 1) % 8) == 0) { + snd_iprintf(buffer, "\n"); + } + } + + snd_iprintf(buffer, "\n"); +} + +static void __devinit snd_rme9652_proc_init(rme9652_t *rme9652) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(rme9652->card, "rme9652", &entry)) + snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read); +} + +static void snd_rme9652_free_buffers(rme9652_t *rme9652) +{ + if (rme9652->capture_buffer_unaligned) { +#ifndef RME9652_PREALLOCATE_MEMORY + snd_free_pci_pages(rme9652->pci, + RME9652_DMA_AREA_BYTES, + rme9652->capture_buffer_unaligned, + rme9652->capture_buffer_addr); +#else + snd_hammerfall_free_buffer(rme9652->pci, rme9652->capture_buffer_unaligned); +#endif + } + + if (rme9652->playback_buffer_unaligned) { +#ifndef RME9652_PREALLOCATE_MEMORY + snd_free_pci_pages(rme9652->pci, + RME9652_DMA_AREA_BYTES, + rme9652->playback_buffer_unaligned, + rme9652->playback_buffer_addr); +#else + snd_hammerfall_free_buffer(rme9652->pci, rme9652->playback_buffer_unaligned); +#endif + } +} + +static int snd_rme9652_free(rme9652_t *rme9652) +{ + if (rme9652->irq >= 0) + rme9652_stop(rme9652); + snd_rme9652_free_buffers(rme9652); + + if (rme9652->iobase) + iounmap((void *) rme9652->iobase); + if (rme9652->res_port) { + release_resource(rme9652->res_port); + kfree_nocheck(rme9652->res_port); + } + if (rme9652->irq >= 0) + free_irq(rme9652->irq, (void *)rme9652); + return 0; +} + +static int __devinit snd_rme9652_initialize_memory(rme9652_t *rme9652) +{ + void *pb, *cb; + dma_addr_t pb_addr, cb_addr; + unsigned long pb_bus, cb_bus; + +#ifndef RME9652_PREALLOCATE_MEMORY + cb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &cb_addr); + pb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &pb_addr); +#else + cb = snd_hammerfall_get_buffer(rme9652->pci, &cb_addr); + pb = snd_hammerfall_get_buffer(rme9652->pci, &pb_addr); +#endif + + if (cb == 0 || pb == 0) { + if (cb) { +#ifdef RME9652_PREALLOCATE_MEMORY + snd_hammerfall_free_buffer(rme9652->pci, cb); +#else + snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, cb, cb_addr); +#endif + } + if (pb) { +#ifdef RME9652_PREALLOCATE_MEMORY + snd_hammerfall_free_buffer(rme9652->pci, pb); +#else + snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, pb, pb_addr); +#endif + } + + printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name); + return -ENOMEM; + } + + /* save raw addresses for use when freeing memory later */ + + rme9652->capture_buffer_unaligned = cb; + rme9652->playback_buffer_unaligned = pb; + rme9652->capture_buffer_addr = cb_addr; + rme9652->playback_buffer_addr = pb_addr; + + /* Align to bus-space 64K boundary */ + + cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl; + pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl; + + /* Tell the card where it is */ + + rme9652_write(rme9652, RME9652_rec_buffer, cb_bus); + rme9652_write(rme9652, RME9652_play_buffer, pb_bus); + + rme9652->capture_buffer = cb + (cb_bus - cb_addr); + rme9652->playback_buffer = pb + (pb_bus - pb_addr); + + return 0; +} + +static void snd_rme9652_set_defaults(rme9652_t *rme9652) +{ + unsigned int k; + + /* ASSUMPTION: rme9652->lock is either held, or + there is no need to hold it (e.g. during module + initalization). + */ + + /* set defaults: + + SPDIF Input via Coax + autosync clock mode + maximum latency (7 = 8192 samples, 64Kbyte buffer, + which implies 2 4096 sample, 32Kbyte periods). + + if rev 1.5, initialize the S/PDIF receiver. + + */ + + rme9652->control_register = + RME9652_inp_0 | rme9652_encode_latency(7); + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + rme9652_reset_hw_pointer(rme9652); + rme9652_compute_period_size(rme9652); + + /* default: thru off for all channels */ + + for (k = 0; k < RME9652_NCHANNELS; ++k) + rme9652_write(rme9652, RME9652_thru_base + k * 4, 0); + + rme9652->thru_bits = 0; + rme9652->passthru = 0; + + /* set a default rate so that the channel map is set up */ + + rme9652_set_rate(rme9652, 48000); +} + +void snd_rme9652_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + rme9652_t *rme9652 = (rme9652_t *) dev_id; + + if (!(rme9652_read(rme9652, RME9652_status_register) & RME9652_IRQ)) { + return; + } + + rme9652_write(rme9652, RME9652_irq_clear, 0); + + if (rme9652->capture_substream) { + snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + } + + if (rme9652->playback_substream) { + snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); + } +} + +static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + return rme9652_hw_pointer(rme9652); +} + +static char *rme9652_channel_buffer_location(rme9652_t *rme9652, + int stream, + int channel) + +{ + int mapped_channel; + + snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL); + + if ((mapped_channel = rme9652->channel_map[channel]) < 0) { + return NULL; + } + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return rme9652->capture_buffer + + (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES); + } else { + return rme9652->playback_buffer + + (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES); + } +} + +static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + return -EFAULT; + return count; +} + +static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + return -EFAULT; + return count; +} + +static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + memset(channel_buf + pos * 4, 0, count * 4); + return count; +} + +static int snd_rme9652_reset(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = rme9652->capture_substream; + else + other = rme9652->playback_substream; + if (rme9652->running) + runtime->status->hw_ptr = rme9652_hw_pointer(rme9652); + else + runtime->status->hw_ptr = 0; + if (other) { + snd_pcm_substream_t *s = substream; + snd_pcm_runtime_t *oruntime = other->runtime; + do { + s = s->link_next; + if (s == other) { + oruntime->status->hw_ptr = runtime->status->hw_ptr; + break; + } + } while (s != substream); + } + return 0; +} + +static int snd_rme9652_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int err; + pid_t this_pid; + pid_t other_pid; + + spin_lock_irq(&rme9652->lock); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream); + this_pid = rme9652->playback_pid; + other_pid = rme9652->capture_pid; + } else { + this_pid = rme9652->capture_pid; + other_pid = rme9652->playback_pid; + } + + if ((other_pid > 0) && (this_pid != other_pid)) { + + /* The other stream is open, and not by the same + task as this one. Make sure that the parameters + that matter are the same. + */ + + if ((int)params_rate(params) != + rme9652_adat_sample_rate(rme9652)) { + spin_unlock_irq(&rme9652->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return -EBUSY; + } + + if (params_period_size(params) != rme9652->period_bytes / 4) { + spin_unlock_irq(&rme9652->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return -EBUSY; + } + + /* We're fine. */ + + spin_unlock_irq(&rme9652->lock); + return 0; + + } else { + spin_unlock_irq(&rme9652->lock); + } + + /* how to make sure that the rate matches an externally-set one ? + */ + + if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return err; + } + + if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return err; + } + + return 0; +} + +static int snd_rme9652_channel_info(snd_pcm_substream_t *substream, + snd_pcm_channel_info_t *info) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int chn; + + snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL); + + if ((chn = rme9652->channel_map[info->channel]) < 0) { + return -EINVAL; + } + + info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES; + info->first = 0; + info->step = 32; + return 0; +} + +static int snd_rme9652_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + { + return snd_rme9652_reset(substream); + } + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + { + snd_pcm_channel_info_t *info = arg; + return snd_rme9652_channel_info(substream, info); + } + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static void rme9652_silence_playback(rme9652_t *rme9652) +{ + memset(rme9652->playback_buffer, 0, RME9652_DMA_AREA_BYTES); +} + +static int snd_rme9652_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + int running; + spin_lock(&rme9652->lock); + running = rme9652->running; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + running |= 1 << substream->stream; + break; + case SNDRV_PCM_TRIGGER_STOP: + running &= ~(1 << substream->stream); + break; + default: + snd_BUG(); + spin_unlock(&rme9652->lock); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = rme9652->capture_substream; + else + other = rme9652->playback_substream; + + if (other) { + snd_pcm_substream_t *s = substream; + do { + s = s->link_next; + if (s == other) { + snd_pcm_trigger_done(s, substream); + if (cmd == SNDRV_PCM_TRIGGER_START) + running |= 1 << s->stream; + else + running &= ~(1 << s->stream); + goto _ok; + } + } while (s != substream); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rme9652_silence_playback(rme9652); + } else { + if (running && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rme9652_silence_playback(rme9652); + } + } else { + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rme9652_silence_playback(rme9652); + } + _ok: + snd_pcm_trigger_done(substream, substream); + if (!rme9652->running && running) + rme9652_start(rme9652); + else if (rme9652->running && !running) + rme9652_stop(rme9652); + rme9652->running = running; + spin_unlock(&rme9652->lock); + + return 0; +} + +static int snd_rme9652_prepare(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irq(&rme9652->lock); + if (!rme9652->running) + rme9652_reset_hw_pointer(rme9652); + spin_unlock_irq(&rme9652->lock); + return result; +} + +static snd_pcm_hardware_t snd_rme9652_playback_subinfo = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_DOUBLE), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 44100, + .rate_max = 96000, + .channels_min = 10, + .channels_max = 26, + .buffer_bytes_max = RME9652_CHANNEL_BUFFER_BYTES * 26, + .period_bytes_min = (64 * 4) * 10, + .period_bytes_max = (8192 * 4) * 26, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_rme9652_capture_subinfo = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 44100, + .rate_max = 96000, + .channels_min = 10, + .channels_max = 26, + .buffer_bytes_max = RME9652_CHANNEL_BUFFER_BYTES *26, + .period_bytes_min = (64 * 4) * 10, + .period_bytes_max = (8192 * 4) * 26, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; + +#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { + .count = PERIOD_SIZES, + .list = period_sizes, + .mask = 0 +}; + +static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + unsigned int list[2] = { rme9652->ds_channels, rme9652->ss_channels }; + return snd_interval_list(c, 2, list, 0); +} + +static int snd_rme9652_hw_rule_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > 48000) { + snd_interval_t t = { + .min = rme9652->ds_channels, + .max = rme9652->ds_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 88200) { + snd_interval_t t = { + .min = rme9652->ss_channels, + .max = rme9652->ss_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } + return 0; +} + +static int snd_rme9652_hw_rule_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (c->min >= rme9652->ss_channels) { + snd_interval_t t = { + .min = 44100, + .max = 48000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= rme9652->ds_channels) { + snd_interval_t t = { + .min = 88200, + .max = 96000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } + return 0; +} + +static int snd_rme9652_playback_open(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&rme9652->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme9652_playback_subinfo; + runtime->dma_area = rme9652->playback_buffer; + runtime->dma_bytes = RME9652_DMA_AREA_BYTES; + + if (rme9652->capture_substream == NULL) { + rme9652_stop(rme9652); + rme9652_set_thru(rme9652, -1, 0); + } + + rme9652->playback_pid = current->pid; + rme9652->playback_substream = substream; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels_rate, rme9652, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_rme9652_hw_rule_rate_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + rme9652->creg_spdif_stream = rme9652->creg_spdif; + rme9652->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id); + return 0; +} + +static int snd_rme9652_playback_release(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + + rme9652->playback_pid = -1; + rme9652->playback_substream = NULL; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + rme9652->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id); + return 0; +} + + +static int snd_rme9652_capture_open(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&rme9652->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme9652_capture_subinfo; + runtime->dma_area = rme9652->capture_buffer; + runtime->dma_bytes = RME9652_DMA_AREA_BYTES; + + if (rme9652->playback_substream == NULL) { + rme9652_stop(rme9652); + rme9652_set_thru(rme9652, -1, 0); + } + + rme9652->capture_pid = current->pid; + rme9652->capture_substream = substream; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels_rate, rme9652, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_rme9652_hw_rule_rate_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + return 0; +} + +static int snd_rme9652_capture_release(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + + rme9652->capture_pid = -1; + rme9652->capture_substream = NULL; + + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_rme9652_playback_ops = { + .open = snd_rme9652_playback_open, + .close = snd_rme9652_playback_release, + .ioctl = snd_rme9652_ioctl, + .hw_params = snd_rme9652_hw_params, + .prepare = snd_rme9652_prepare, + .trigger = snd_rme9652_trigger, + .pointer = snd_rme9652_hw_pointer, + .copy = snd_rme9652_playback_copy, + .silence = snd_rme9652_hw_silence, +}; + +static snd_pcm_ops_t snd_rme9652_capture_ops = { + .open = snd_rme9652_capture_open, + .close = snd_rme9652_capture_release, + .ioctl = snd_rme9652_ioctl, + .hw_params = snd_rme9652_hw_params, + .prepare = snd_rme9652_prepare, + .trigger = snd_rme9652_trigger, + .pointer = snd_rme9652_hw_pointer, + .copy = snd_rme9652_capture_copy, +}; + +static int __devinit snd_rme9652_create_pcm(snd_card_t *card, + rme9652_t *rme9652) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(card, + rme9652->card_name, + 0, 1, 1, &pcm)) < 0) { + return err; + } + + rme9652->pcm = pcm; + pcm->private_data = rme9652; + strcpy(pcm->name, rme9652->card_name); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + return 0; +} + +static int __devinit snd_rme9652_create(snd_card_t *card, + rme9652_t *rme9652, + int precise_ptr) +{ + struct pci_dev *pci = rme9652->pci; + int err; + int status; + unsigned short rev; + + rme9652->irq = -1; + rme9652->card = card; + + pci_read_config_word(rme9652->pci, PCI_CLASS_REVISION, &rev); + + switch (rev & 0xff) { + case 3: + case 4: + case 8: + case 9: + break; + + default: + /* who knows? */ + return -ENODEV; + } + + if ((err = pci_enable_device(pci)) < 0) + return err; + + spin_lock_init(&rme9652->lock); + + rme9652->port = pci_resource_start(pci, 0); + if ((rme9652->res_port = request_mem_region(rme9652->port, RME9652_IO_EXTENT, "rme9652")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); + return -EBUSY; + } + + rme9652->iobase = (unsigned long) ioremap_nocache(rme9652->port, RME9652_IO_EXTENT); + if (rme9652->iobase == 0) { + snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) { + snd_printk("unable to request IRQ %d\n", pci->irq); + return -EBUSY; + } + rme9652->irq = pci->irq; + rme9652->precise_ptr = precise_ptr; + + /* Determine the h/w rev level of the card. This seems like + a particularly kludgy way to encode it, but its what RME + chose to do, so we follow them ... + */ + + status = rme9652_read(rme9652, RME9652_status_register); + if (rme9652_decode_spdif_rate(status&RME9652_F) == 1) { + rme9652->hw_rev = 15; + } else { + rme9652->hw_rev = 11; + } + + /* Differentiate between the standard Hammerfall, and the + "Light", which does not have the expansion board. This + method comes from information received from Mathhias + Clausen at RME. Display the EEPROM and h/w revID where + relevant. + */ + + switch (rev) { + case 8: /* original eprom */ + strcpy(card->driver, "RME9636"); + if (rme9652->hw_rev == 15) { + rme9652->card_name = "RME Digi9636 (Rev 1.5)"; + } else { + rme9652->card_name = "RME Digi9636"; + } + rme9652->ss_channels = RME9636_NCHANNELS; + break; + case 9: /* W36_G EPROM */ + strcpy(card->driver, "RME9636"); + rme9652->card_name = "RME Digi9636 (Rev G)"; + rme9652->ss_channels = RME9636_NCHANNELS; + break; + case 4: /* W52_G EPROM */ + strcpy(card->driver, "RME9652"); + rme9652->card_name = "RME Digi9652 (Rev G)"; + rme9652->ss_channels = RME9652_NCHANNELS; + break; + case 3: /* original eprom */ + strcpy(card->driver, "RME9652"); + if (rme9652->hw_rev == 15) { + rme9652->card_name = "RME Digi9652 (Rev 1.5)"; + } else { + rme9652->card_name = "RME Digi9652"; + } + rme9652->ss_channels = RME9652_NCHANNELS; + break; + } + + rme9652->ds_channels = (rme9652->ss_channels - 2) / 2 + 2; + + pci_set_master(rme9652->pci); + + if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) { + return err; + } + + if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) { + return err; + } + + if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) { + return err; + } + + snd_rme9652_proc_init(rme9652); + + rme9652->last_spdif_sample_rate = -1; + rme9652->last_adat_sample_rate = -1; + rme9652->playback_pid = -1; + rme9652->capture_pid = -1; + rme9652->capture_substream = NULL; + rme9652->playback_substream = NULL; + + snd_rme9652_set_defaults(rme9652); + + if (rme9652->hw_rev == 15) { + rme9652_initialize_spdif_receiver (rme9652); + } + + return 0; +} + +static void snd_rme9652_card_free(snd_card_t *card) +{ + rme9652_t *rme9652 = (rme9652_t *) card->private_data; + + if (rme9652) + snd_rme9652_free(rme9652); +} + +static int __devinit snd_rme9652_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + rme9652_t *rme9652; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(rme9652_t)); + + if (!card) + return -ENOMEM; + + rme9652 = (rme9652_t *) card->private_data; + card->private_free = snd_rme9652_card_free; + rme9652->dev = dev; + rme9652->pci = pci; + + if ((err = snd_rme9652_create(card, rme9652, precise_ptr[dev])) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->shortname, rme9652->card_name); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, rme9652->port, rme9652->irq); + + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme9652_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "RME Digi9652 (Hammerfall)", + .id_table = snd_rme9652_ids, + .probe = snd_rme9652_probe, + .remove = __devexit_p(snd_rme9652_remove), +}; + +static int __init alsa_card_hammerfall_init(void) +{ + if (pci_module_init(&driver) < 0) { +#ifdef MODULE + printk(KERN_ERR "RME Digi9652/Digi9636: no cards found\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_hammerfall_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_hammerfall_init) +module_exit(alsa_card_hammerfall_exit) + +#ifndef MODULE + +/* format is: snd-rme9652=enable,index,id */ + +static int __init alsa_card_rme9652_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme9652=", alsa_card_rme9652_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/sonicvibes.c linux/sound/pci/sonicvibes.c --- linux-2.4.21-rc1.orig/sound/pci/sonicvibes.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/sonicvibes.c 2003-03-01 12:03:21.000000000 -0700 @@ -0,0 +1,1574 @@ +/* + * Driver for S3 SonicVibes soundcard + * Copyright (c) by Jaroslav Kysela + * + * BUGS: + * It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB? + * Driver sometimes hangs... Nobody knows why at this moment... + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("S3 SonicVibes PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{S3,SonicVibes PCI}}"); + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static unsigned int dmaio = 0x7a00; /* DDMA i/o address */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(reverb, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(reverb, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(mge, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(mge, "MIC Gain Enable for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(mge, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(dmaio, "i"); +MODULE_PARM_DESC(dmaio, "DDMA i/o base address for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(dmaio, "global," SNDRV_PORT_DESC); + +/* + * Enhanced port direct registers + */ + +#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x) + +#define SV_REG_CONTROL 0x00 /* R/W: CODEC/Mixer control register */ +#define SV_ENHANCED 0x01 /* audio mode select - enhanced mode */ +#define SV_TEST 0x02 /* test bit */ +#define SV_REVERB 0x04 /* reverb enable */ +#define SV_WAVETABLE 0x08 /* wavetable active / FM active if not set */ +#define SV_INTA 0x20 /* INTA driving - should be always 1 */ +#define SV_RESET 0x80 /* reset chip */ +#define SV_REG_IRQMASK 0x01 /* R/W: CODEC/Mixer interrupt mask register */ +#define SV_DMAA_MASK 0x01 /* mask DMA-A interrupt */ +#define SV_DMAC_MASK 0x04 /* mask DMA-C interrupt */ +#define SV_SPEC_MASK 0x08 /* special interrupt mask - should be always masked */ +#define SV_UD_MASK 0x40 /* Up/Down button interrupt mask */ +#define SV_MIDI_MASK 0x80 /* mask MIDI interrupt */ +#define SV_REG_STATUS 0x02 /* R/O: CODEC/Mixer status register */ +#define SV_DMAA_IRQ 0x01 /* DMA-A interrupt */ +#define SV_DMAC_IRQ 0x04 /* DMA-C interrupt */ +#define SV_SPEC_IRQ 0x08 /* special interrupt */ +#define SV_UD_IRQ 0x40 /* Up/Down interrupt */ +#define SV_MIDI_IRQ 0x80 /* MIDI interrupt */ +#define SV_REG_INDEX 0x04 /* R/W: CODEC/Mixer index address register */ +#define SV_MCE 0x40 /* mode change enable */ +#define SV_TRD 0x80 /* DMA transfer request disabled */ +#define SV_REG_DATA 0x05 /* R/W: CODEC/Mixer index data register */ + +/* + * Enhanced port indirect registers + */ + +#define SV_IREG_LEFT_ADC 0x00 /* Left ADC Input Control */ +#define SV_IREG_RIGHT_ADC 0x01 /* Right ADC Input Control */ +#define SV_IREG_LEFT_AUX1 0x02 /* Left AUX1 Input Control */ +#define SV_IREG_RIGHT_AUX1 0x03 /* Right AUX1 Input Control */ +#define SV_IREG_LEFT_CD 0x04 /* Left CD Input Control */ +#define SV_IREG_RIGHT_CD 0x05 /* Right CD Input Control */ +#define SV_IREG_LEFT_LINE 0x06 /* Left Line Input Control */ +#define SV_IREG_RIGHT_LINE 0x07 /* Right Line Input Control */ +#define SV_IREG_MIC 0x08 /* MIC Input Control */ +#define SV_IREG_GAME_PORT 0x09 /* Game Port Control */ +#define SV_IREG_LEFT_SYNTH 0x0a /* Left Synth Input Control */ +#define SV_IREG_RIGHT_SYNTH 0x0b /* Right Synth Input Control */ +#define SV_IREG_LEFT_AUX2 0x0c /* Left AUX2 Input Control */ +#define SV_IREG_RIGHT_AUX2 0x0d /* Right AUX2 Input Control */ +#define SV_IREG_LEFT_ANALOG 0x0e /* Left Analog Mixer Output Control */ +#define SV_IREG_RIGHT_ANALOG 0x0f /* Right Analog Mixer Output Control */ +#define SV_IREG_LEFT_PCM 0x10 /* Left PCM Input Control */ +#define SV_IREG_RIGHT_PCM 0x11 /* Right PCM Input Control */ +#define SV_IREG_DMA_DATA_FMT 0x12 /* DMA Data Format */ +#define SV_IREG_PC_ENABLE 0x13 /* Playback/Capture Enable Register */ +#define SV_IREG_UD_BUTTON 0x14 /* Up/Down Button Register */ +#define SV_IREG_REVISION 0x15 /* Revision */ +#define SV_IREG_ADC_OUTPUT_CTRL 0x16 /* ADC Output Control */ +#define SV_IREG_DMA_A_UPPER 0x18 /* DMA A Upper Base Count */ +#define SV_IREG_DMA_A_LOWER 0x19 /* DMA A Lower Base Count */ +#define SV_IREG_DMA_C_UPPER 0x1c /* DMA C Upper Base Count */ +#define SV_IREG_DMA_C_LOWER 0x1d /* DMA C Lower Base Count */ +#define SV_IREG_PCM_RATE_LOW 0x1e /* PCM Sampling Rate Low Byte */ +#define SV_IREG_PCM_RATE_HIGH 0x1f /* PCM Sampling Rate High Byte */ +#define SV_IREG_SYNTH_RATE_LOW 0x20 /* Synthesizer Sampling Rate Low Byte */ +#define SV_IREG_SYNTH_RATE_HIGH 0x21 /* Synthesizer Sampling Rate High Byte */ +#define SV_IREG_ADC_CLOCK 0x22 /* ADC Clock Source Selection */ +#define SV_IREG_ADC_ALT_RATE 0x23 /* ADC Alternative Sampling Rate Selection */ +#define SV_IREG_ADC_PLL_M 0x24 /* ADC PLL M Register */ +#define SV_IREG_ADC_PLL_N 0x25 /* ADC PLL N Register */ +#define SV_IREG_SYNTH_PLL_M 0x26 /* Synthesizer PLL M Register */ +#define SV_IREG_SYNTH_PLL_N 0x27 /* Synthesizer PLL N Register */ +#define SV_IREG_MPU401 0x2a /* MPU-401 UART Operation */ +#define SV_IREG_DRIVE_CTRL 0x2b /* Drive Control */ +#define SV_IREG_SRS_SPACE 0x2c /* SRS Space Control */ +#define SV_IREG_SRS_CENTER 0x2d /* SRS Center Control */ +#define SV_IREG_WAVE_SOURCE 0x2e /* Wavetable Sample Source Select */ +#define SV_IREG_ANALOG_POWER 0x30 /* Analog Power Down Control */ +#define SV_IREG_DIGITAL_POWER 0x31 /* Digital Power Down Control */ + +#define SV_IREG_ADC_PLL SV_IREG_ADC_PLL_M +#define SV_IREG_SYNTH_PLL SV_IREG_SYNTH_PLL_M + +/* + * DMA registers + */ + +#define SV_DMA_ADDR0 0x00 +#define SV_DMA_ADDR1 0x01 +#define SV_DMA_ADDR2 0x02 +#define SV_DMA_ADDR3 0x03 +#define SV_DMA_COUNT0 0x04 +#define SV_DMA_COUNT1 0x05 +#define SV_DMA_COUNT2 0x06 +#define SV_DMA_MODE 0x0b +#define SV_DMA_RESET 0x0d +#define SV_DMA_MASK 0x0f + +/* + * Record sources + */ + +#define SV_RECSRC_RESERVED (0x00<<5) +#define SV_RECSRC_CD (0x01<<5) +#define SV_RECSRC_DAC (0x02<<5) +#define SV_RECSRC_AUX2 (0x03<<5) +#define SV_RECSRC_LINE (0x04<<5) +#define SV_RECSRC_AUX1 (0x05<<5) +#define SV_RECSRC_MIC (0x06<<5) +#define SV_RECSRC_OUT (0x07<<5) + +/* + * constants + */ + +#define SV_FULLRATE 48000 +#define SV_REFFREQUENCY 24576000 +#define SV_ADCMULT 512 + +#define SV_MODE_PLAY 1 +#define SV_MODE_CAPTURE 2 + +/* + + */ + +typedef struct _snd_sonicvibes sonicvibes_t; +#define chip_t sonicvibes_t + +struct _snd_sonicvibes { + unsigned long dma1size; + unsigned long dma2size; + int irq; + + unsigned long sb_port; + struct resource *res_sb_port; + unsigned long enh_port; + struct resource *res_enh_port; + unsigned long synth_port; + struct resource *res_synth_port; + unsigned long midi_port; + struct resource *res_midi_port; + unsigned long game_port; + unsigned int dmaa_port; + struct resource *res_dmaa; + unsigned int dmac_port; + struct resource *res_dmac; + + unsigned char enable; + unsigned char irqmask; + unsigned char revision; + unsigned char format; + unsigned char srs_space; + unsigned char srs_center; + unsigned char mpu_switch; + unsigned char wave_source; + + unsigned int mode; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_rawmidi_t *rmidi; + snd_hwdep_t *fmsynth; /* S3FM */ + + spinlock_t reg_lock; + + unsigned int p_dma_size; + unsigned int c_dma_size; + + snd_kcontrol_t *master_mute; + snd_kcontrol_t *master_volume; + +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + struct gameport gameport; +#endif +}; + +static struct pci_device_id snd_sonic_ids[] __devinitdata = { + { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_sonic_ids); + +static ratden_t sonicvibes_adc_clock = { + .num_min = 4000 * 65536, + .num_max = 48000UL * 65536, + .num_step = 1, + .den = 65536, +}; +static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = { + .nrats = 1, + .rats = &sonicvibes_adc_clock, +}; + +/* + * common I/O routines + */ + +static inline void snd_sonicvibes_setdmaa(sonicvibes_t * sonic, + unsigned int addr, + unsigned int count) +{ + count--; + outl(addr, sonic->dmaa_port + SV_DMA_ADDR0); + outl(count, sonic->dmaa_port + SV_DMA_COUNT0); + outb(0x18, sonic->dmaa_port + SV_DMA_MODE); +#if 0 + printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0)); +#endif +} + +static inline void snd_sonicvibes_setdmac(sonicvibes_t * sonic, + unsigned int addr, + unsigned int count) +{ + /* note: dmac is working in word mode!!! */ + count >>= 1; + count--; + outl(addr, sonic->dmac_port + SV_DMA_ADDR0); + outl(count, sonic->dmac_port + SV_DMA_COUNT0); + outb(0x14, sonic->dmac_port + SV_DMA_MODE); +#if 0 + printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0)); +#endif +} + +static inline unsigned int snd_sonicvibes_getdmaa(sonicvibes_t * sonic) +{ + return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1; +} + +static inline unsigned int snd_sonicvibes_getdmac(sonicvibes_t * sonic) +{ + /* note: dmac is working in word mode!!! */ + return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +} + +static void snd_sonicvibes_out1(sonicvibes_t * sonic, + unsigned char reg, + unsigned char value) +{ + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + outb(value, SV_REG(sonic, DATA)); + udelay(10); +} + +static void snd_sonicvibes_out(sonicvibes_t * sonic, + unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + outb(value, SV_REG(sonic, DATA)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static unsigned char snd_sonicvibes_in1(sonicvibes_t * sonic, unsigned char reg) +{ + unsigned char value; + + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + value = inb(SV_REG(sonic, DATA)); + udelay(10); + return value; +} + +static unsigned char snd_sonicvibes_in(sonicvibes_t * sonic, unsigned char reg) +{ + unsigned long flags; + unsigned char value; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + value = inb(SV_REG(sonic, DATA)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return value; +} + +#ifdef CONFIG_SND_DEBUG +void snd_sonicvibes_debug(sonicvibes_t * sonic) +{ + printk("SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX))); + printk(" STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00)); + printk(" 0x20: synth rate low = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20)); + printk(" 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01)); + printk(" 0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21)); + printk(" 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02)); + printk(" 0x22: ADC clock = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22)); + printk(" 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03)); + printk(" 0x23: ADC alt rate = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23)); + printk(" 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04)); + printk(" 0x24: ADC pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24)); + printk(" 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05)); + printk(" 0x25: ADC pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25)); + printk(" 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06)); + printk(" 0x26: Synth pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26)); + printk(" 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07)); + printk(" 0x27: Synth pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27)); + printk(" 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08)); + printk(" 0x28: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28)); + printk(" 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09)); + printk(" 0x29: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29)); + printk(" 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a)); + printk(" 0x2a: MPU401 = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a)); + printk(" 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b)); + printk(" 0x2b: drive ctrl = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b)); + printk(" 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c)); + printk(" 0x2c: SRS space = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c)); + printk(" 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d)); + printk(" 0x2d: SRS center = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d)); + printk(" 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e)); + printk(" 0x2e: wave source = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e)); + printk(" 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f)); + printk(" 0x2f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f)); + printk(" 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10)); + printk(" 0x30: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30)); + printk(" 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11)); + printk(" 0x31: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31)); + printk(" 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12)); + printk(" 0x32: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32)); + printk(" 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13)); + printk(" 0x33: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33)); + printk(" 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14)); + printk(" 0x34: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34)); + printk(" 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15)); + printk(" 0x35: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35)); + printk(" 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16)); + printk(" 0x36: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36)); + printk(" 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17)); + printk(" 0x37: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37)); + printk(" 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18)); + printk(" 0x38: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38)); + printk(" 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19)); + printk(" 0x39: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39)); + printk(" 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a)); + printk(" 0x3a: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a)); + printk(" 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b)); + printk(" 0x3b: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b)); + printk(" 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c)); + printk(" 0x3c: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c)); + printk(" 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d)); + printk(" 0x3d: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d)); + printk(" 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e)); + printk(" 0x3e: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e)); + printk(" 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f)); + printk(" 0x3f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f)); +} + +#endif + +static void snd_sonicvibes_setfmt(sonicvibes_t * sonic, + unsigned char mask, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX)); + if (mask) { + sonic->format = inb(SV_REG(sonic, DATA)); + udelay(10); + } + sonic->format = (sonic->format & mask) | value; + outb(sonic->format, SV_REG(sonic, DATA)); + udelay(10); + outb(0, SV_REG(sonic, INDEX)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static void snd_sonicvibes_pll(unsigned int rate, + unsigned int *res_r, + unsigned int *res_m, + unsigned int *res_n) +{ + unsigned int r, m = 0, n = 0; + unsigned int xm, xn, xr, xd, metric = ~0U; + + if (rate < 625000 / SV_ADCMULT) + rate = 625000 / SV_ADCMULT; + if (rate > 150000000 / SV_ADCMULT) + rate = 150000000 / SV_ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 33; xn++) /* 35 */ + for (xm = 3; xm < 257; xm++) { + xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn; + if (xr >= rate) + xd = xr - rate; + else + xd = rate - xr; + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + *res_r = r; + *res_m = m; + *res_n = n; +#if 0 + printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn); + printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n); +#endif +} + +static void snd_sonicvibes_setpll(sonicvibes_t * sonic, + unsigned char reg, + unsigned int rate) +{ + unsigned long flags; + unsigned int r, m, n; + + snd_sonicvibes_pll(rate, &r, &m, &n); + if (sonic != NULL) { + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, reg, m); + snd_sonicvibes_out1(sonic, reg + 1, r | n); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + } +} + +static void snd_sonicvibes_set_adc_rate(sonicvibes_t * sonic, unsigned int rate) +{ + unsigned long flags; + unsigned int div; + unsigned char clock; + + div = 48000 / rate; + if (div > 8) + div = 8; + if ((48000 / div) == rate) { /* use the alternate clock */ + clock = 0x10; + } else { /* use the PLL source */ + clock = 0x00; + snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate); + } + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4); + snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static int snd_sonicvibes_hw_constraint_dac_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int rate, div, r, m, n; + + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min == + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) { + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min; + div = 48000 / rate; + if (div > 8) + div = 8; + if ((48000 / div) == rate) { + params->rate_num = rate; + params->rate_den = 1; + } else { + snd_sonicvibes_pll(rate, &r, &m, &n); + snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL); + snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL); + params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r; + params->rate_den = (SV_ADCMULT/512) * (m+2); + } + } + return 0; +} + +static void snd_sonicvibes_set_dac_rate(sonicvibes_t * sonic, unsigned int rate) +{ + unsigned int div; + unsigned long flags; + + div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static int snd_sonicvibes_trigger(sonicvibes_t * sonic, int what, int cmd) +{ + int result = 0; + + spin_lock(&sonic->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(sonic->enable & what)) { + sonic->enable |= what; + snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); + } + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (sonic->enable & what) { + sonic->enable &= ~what; + snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); + } + } else { + result = -EINVAL; + } + spin_unlock(&sonic->reg_lock); + return result; +} + +static void snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return); + unsigned char status; + + status = inb(SV_REG(sonic, STATUS)); + if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ))) + return; + if (status == 0xff) { /* failure */ + outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK)); + snd_printk("IRQ failure - interrupts disabled!!\n"); + return; + } + if (sonic->pcm) { + if (status & SV_DMAA_IRQ) + snd_pcm_period_elapsed(sonic->playback_substream); + if (status & SV_DMAC_IRQ) + snd_pcm_period_elapsed(sonic->capture_substream); + } + if (sonic->rmidi) { + if (status & SV_MIDI_IRQ) + snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data, regs); + } + if (status & SV_UD_IRQ) { + unsigned char udreg; + int vol, oleft, oright, mleft, mright; + + spin_lock(&sonic->reg_lock); + udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON); + vol = udreg & 0x3f; + if (!(udreg & 0x40)) + vol = -vol; + oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG); + oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG); + oleft &= 0x1f; + oright &= 0x1f; + oleft += vol; + if (oleft < 0) + oleft = 0; + if (oleft > 0x1f) + oleft = 0x1f; + oright += vol; + if (oright < 0) + oright = 0; + if (oright > 0x1f) + oright = 0x1f; + if (udreg & 0x80) { + mleft ^= 0x80; + mright ^= 0x80; + } + oleft |= mleft & 0x80; + oright |= mright & 0x80; + snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft); + snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright); + spin_unlock(&sonic->reg_lock); + snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id); + snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id); + } +} + +/* + * PCM part + */ + +static int snd_sonicvibes_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + return snd_sonicvibes_trigger(sonic, 1, cmd); +} + +static int snd_sonicvibes_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + return snd_sonicvibes_trigger(sonic, 2, cmd); +} + +static int snd_sonicvibes_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sonicvibes_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char fmt = 0; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + sonic->p_dma_size = size; + count--; + if (runtime->channels > 1) + fmt |= 1; + if (snd_pcm_format_width(runtime->format) == 16) + fmt |= 2; + snd_sonicvibes_setfmt(sonic, ~3, fmt); + snd_sonicvibes_set_dac_rate(sonic, runtime->rate); + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char fmt = 0; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + sonic->c_dma_size = size; + count >>= 1; + count--; + if (runtime->channels > 1) + fmt |= 0x10; + if (snd_pcm_format_width(runtime->format) == 16) + fmt |= 0x20; + snd_sonicvibes_setfmt(sonic, ~0x30, fmt); + snd_sonicvibes_set_adc_rate(sonic, runtime->rate); + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_sonicvibes_playback_pointer(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(sonic->enable & 1)) + return 0; + ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(sonic->enable & 2)) + return 0; + ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_sonicvibes_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 32, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_sonicvibes_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 32, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + sonic->mode |= SV_MODE_PLAY; + sonic->playback_substream = substream; + runtime->hw = snd_sonicvibes_playback; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, 0, SNDRV_PCM_HW_PARAM_RATE, -1); + return 0; +} + +static int snd_sonicvibes_capture_open(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + sonic->mode |= SV_MODE_CAPTURE; + sonic->capture_substream = substream; + runtime->hw = snd_sonicvibes_capture; + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_sonicvibes_hw_constraints_adc_clock); + return 0; +} + +static int snd_sonicvibes_playback_close(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + + sonic->playback_substream = NULL; + sonic->mode &= ~SV_MODE_PLAY; + return 0; +} + +static int snd_sonicvibes_capture_close(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + + sonic->capture_substream = NULL; + sonic->mode &= ~SV_MODE_CAPTURE; + return 0; +} + +static snd_pcm_ops_t snd_sonicvibes_playback_ops = { + .open = snd_sonicvibes_playback_open, + .close = snd_sonicvibes_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sonicvibes_hw_params, + .hw_free = snd_sonicvibes_hw_free, + .prepare = snd_sonicvibes_playback_prepare, + .trigger = snd_sonicvibes_playback_trigger, + .pointer = snd_sonicvibes_playback_pointer, +}; + +static snd_pcm_ops_t snd_sonicvibes_capture_ops = { + .open = snd_sonicvibes_capture_open, + .close = snd_sonicvibes_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sonicvibes_hw_params, + .hw_free = snd_sonicvibes_hw_free, + .prepare = snd_sonicvibes_capture_prepare, + .trigger = snd_sonicvibes_capture_trigger, + .pointer = snd_sonicvibes_capture_pointer, +}; + +static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, pcm->private_data, return); + sonic->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0) + return err; + snd_assert(pcm != NULL, return -EINVAL); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops); + + pcm->private_data = sonic; + pcm->private_free = snd_sonicvibes_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "S3 SonicVibes"); + sonic->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer part + */ + +#define SONICVIBES_MUX(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_sonicvibes_info_mux, \ + .get = snd_sonicvibes_get_mux, .put = snd_sonicvibes_put_mux } + +static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[7] = { + "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 7; + if (uinfo->value.enumerated.item >= 7) + uinfo->value.enumerated.item = 6; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1; + ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right, oval1, oval2; + int change; + + if (ucontrol->value.enumerated.item[0] >= 7 || + ucontrol->value.enumerated.item[1] >= 7) + return -EINVAL; + left = (ucontrol->value.enumerated.item[0] + 1) << 5; + right = (ucontrol->value.enumerated.item[1] + 1) << 5; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC); + oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC); + left = (oval1 & ~SV_RECSRC_OUT) | left; + right = (oval2 & ~SV_RECSRC_OUT) | right; + change = left != oval1 || right != oval2; + snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left); + snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_sonicvibes_info_single, \ + .get = snd_sonicvibes_get_single, .put = snd_sonicvibes_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val, oval; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval = snd_sonicvibes_in1(sonic, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_sonicvibes_out1(sonic, reg, val); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_sonicvibes_info_double, \ + .get = snd_sonicvibes_get_double, .put = snd_sonicvibes_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval1 = snd_sonicvibes_in1(sonic, left_reg); + oval2 = snd_sonicvibes_in1(sonic, right_reg); + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + snd_sonicvibes_out1(sonic, left_reg, val1); + snd_sonicvibes_out1(sonic, right_reg, val2); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_CONTROLS (sizeof(snd_sonicvibes_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sonicvibes_controls[] __devinitdata = { +SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0), +SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1), +SONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1), +SONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1), +SONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1), +SONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1), +SONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0), +SONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1), +SONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1), +SONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1), +SONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0), +SONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1), +SONICVIBES_MUX("Capture Source", 0) +}; + +static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, _snd_kcontrol_chip(kcontrol), return); + sonic->master_mute = NULL; + sonic->master_volume = NULL; +} + +static int __devinit snd_sonicvibes_mixer(sonicvibes_t * sonic) +{ + snd_card_t *card; + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL); + card = sonic->card; + strcpy(card->mixername, "S3 SonicVibes"); + + for (idx = 0; idx < SONICVIBES_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0) + return err; + switch (idx) { + case 0: + case 1: kctl->private_free = snd_sonicvibes_master_free; break; + } + } + return 0; +} + +/* + + */ + +static void snd_sonicvibes_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, entry->private_data, return); + unsigned char tmp; + + tmp = sonic->srs_space & 0x0f; + snd_iprintf(buffer, "SRS 3D : %s\n", + sonic->srs_space & 0x80 ? "off" : "on"); + snd_iprintf(buffer, "SRS Space : %s\n", + tmp == 0x00 ? "100%" : + tmp == 0x01 ? "75%" : + tmp == 0x02 ? "50%" : + tmp == 0x03 ? "25%" : "0%"); + tmp = sonic->srs_center & 0x0f; + snd_iprintf(buffer, "SRS Center : %s\n", + tmp == 0x00 ? "100%" : + tmp == 0x01 ? "75%" : + tmp == 0x02 ? "50%" : + tmp == 0x03 ? "25%" : "0%"); + tmp = sonic->wave_source & 0x03; + snd_iprintf(buffer, "WaveTable Source : %s\n", + tmp == 0x00 ? "on-board ROM" : + tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus"); + tmp = sonic->mpu_switch; + snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off"); + snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off"); + snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off"); +} + +static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry)) + snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read); +} + +/* + + */ + +static snd_kcontrol_new_t snd_sonicvibes_game_control __devinitdata = +SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0); + +static int snd_sonicvibes_free(sonicvibes_t *sonic) +{ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + if (sonic->gameport.io) + gameport_unregister_port(&sonic->gameport); +#endif + pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); + pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); + if (sonic->res_sb_port) { + release_resource(sonic->res_sb_port); + kfree_nocheck(sonic->res_sb_port); + } + if (sonic->res_enh_port) { + release_resource(sonic->res_enh_port); + kfree_nocheck(sonic->res_enh_port); + } + if (sonic->res_synth_port) { + release_resource(sonic->res_synth_port); + kfree_nocheck(sonic->res_synth_port); + } + if (sonic->res_midi_port) { + release_resource(sonic->res_midi_port); + kfree_nocheck(sonic->res_midi_port); + } + if (sonic->res_dmaa) { + release_resource(sonic->res_dmaa); + kfree_nocheck(sonic->res_dmaa); + } + if (sonic->res_dmac) { + release_resource(sonic->res_dmac); + kfree_nocheck(sonic->res_dmac); + } + if (sonic->irq >= 0) + free_irq(sonic->irq, (void *)sonic); + snd_magic_kfree(sonic); + return 0; +} + +static int snd_sonicvibes_dev_free(snd_device_t *device) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, device->device_data, return -ENXIO); + return snd_sonicvibes_free(sonic); +} + +static int __devinit snd_sonicvibes_create(snd_card_t * card, + struct pci_dev *pci, + int reverb, + int mge, + sonicvibes_t ** rsonic) +{ + sonicvibes_t *sonic; + unsigned int dmaa, dmac; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_sonicvibes_dev_free, + }; + + *rsonic = NULL; + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + sonic = snd_magic_kcalloc(sonicvibes_t, 0, GFP_KERNEL); + if (sonic == NULL) + return -ENOMEM; + spin_lock_init(&sonic->reg_lock); + sonic->card = card; + sonic->pci = pci; + sonic->irq = -1; + sonic->sb_port = pci_resource_start(pci, 0); + if ((sonic->res_sb_port = request_region(sonic->sb_port, 0x10, "S3 SonicVibes SB")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab SB port at 0x%lx-0x%lx\n", sonic->sb_port, sonic->sb_port + 0x10 - 1); + return -EBUSY; + } + sonic->enh_port = pci_resource_start(pci, 1); + if ((sonic->res_enh_port = request_region(sonic->enh_port, 0x10, "S3 SonicVibes Enhanced")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab PCM port at 0x%lx-0x%lx\n", sonic->enh_port, sonic->enh_port + 0x10 - 1); + return -EBUSY; + } + sonic->synth_port = pci_resource_start(pci, 2); + if ((sonic->res_synth_port = request_region(sonic->synth_port, 4, "S3 SonicVibes Synth")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab synth port at 0x%lx-0x%lx\n", sonic->synth_port, sonic->synth_port + 4 - 1); + return -EBUSY; + } + sonic->midi_port = pci_resource_start(pci, 3); + if ((sonic->res_midi_port = request_region(sonic->midi_port, 4, "S3 SonicVibes Midi")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab MIDI port at 0x%lx-0x%lx\n", sonic->midi_port, sonic->midi_port + 4 - 1); + return -EBUSY; + } + sonic->game_port = pci_resource_start(pci, 4); + if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) { + snd_magic_kfree(sonic); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + sonic->irq = pci->irq; + + pci_read_config_dword(pci, 0x40, &dmaa); + pci_read_config_dword(pci, 0x48, &dmac); + dmaio &= ~0x0f; + dmaa &= ~0x0f; + dmac &= ~0x0f; + if (!dmaa) { + dmaa = dmaio; + dmaio += 0x10; + snd_printk("BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa); + } + if (!dmac) { + dmac = dmaio; + dmaio += 0x10; + snd_printk("BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac); + } + pci_write_config_dword(pci, 0x40, dmaa); + pci_write_config_dword(pci, 0x48, dmac); + + if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1); + return -EBUSY; + } + if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1); + return -EBUSY; + } + + pci_read_config_dword(pci, 0x40, &sonic->dmaa_port); + pci_read_config_dword(pci, 0x48, &sonic->dmac_port); + sonic->dmaa_port &= ~0x0f; + sonic->dmac_port &= ~0x0f; + pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9); /* enable + enhanced */ + pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9); /* enable */ + /* ok.. initialize S3 SonicVibes chip */ + outb(SV_RESET, SV_REG(sonic, CONTROL)); /* reset chip */ + udelay(100); + outb(0, SV_REG(sonic, CONTROL)); /* release reset */ + udelay(100); + outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL)); + inb(SV_REG(sonic, STATUS)); /* clear IRQs */ +#if 1 + snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0); /* drive current 16mA */ +#else + snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40); /* drive current 8mA */ +#endif + snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0); /* disable playback & capture */ + outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK)); + inb(SV_REG(sonic, STATUS)); /* clear IRQs */ + snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0); /* use PLL as clock source */ + snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0); /* power up analog parts */ + snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0); /* power up digital parts */ + snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000); + snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80); /* SRS space off */ + snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */ + snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05); /* MPU-401 switch */ + snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00); /* onboard ROM */ + snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff); + snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf); + snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc); +#if 0 + snd_sonicvibes_debug(sonic); +#endif + sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION); + snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic)); + snd_sonicvibes_proc_init(sonic); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) { + snd_sonicvibes_free(sonic); + return err; + } + + *rsonic = sonic; + return 0; +} + +/* + * MIDI section + */ + +#define SONICVIBES_MIDI_CONTROLS (sizeof(snd_sonicvibes_midi_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] __devinitdata = { +SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0), +SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0), +SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0), +SONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0), +SONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0) +}; + +static int snd_sonicvibes_midi_input_open(mpu401_t * mpu) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return -EIO); + outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); + return 0; +} + +static void snd_sonicvibes_midi_input_close(mpu401_t * mpu) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return); + outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); +} + +static int __devinit snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi) +{ + mpu401_t * mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENXIO); + snd_card_t *card = sonic->card; + snd_rawmidi_str_t *dir; + unsigned int idx; + int err; + + mpu->private_data = sonic; + mpu->open_input = snd_sonicvibes_midi_input_open; + mpu->close_input = snd_sonicvibes_midi_input_close; + dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + for (idx = 0; idx < SONICVIBES_MIDI_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0) + return err; + return 0; +} + +static int __devinit snd_sonic_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + sonicvibes_t *sonic; + snd_rawmidi_t *midi_uart; + opl3_t *opl3; + int idx, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + for (idx = 0; idx < 5; idx++) { + if (pci_resource_start(pci, idx) == 0 || + !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { + snd_card_free(card); + return -ENODEV; + } + } + if ((err = snd_sonicvibes_create(card, pci, + reverb[dev] ? 1 : 0, + mge[dev] ? 1 : 0, + &sonic)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sonicvibes_mixer(sonic)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES, + sonic->midi_port, 1, + sonic->irq, 0, + &midi_uart)) < 0) { + snd_card_free(card); + return err; + } + snd_sonicvibes_midi(sonic, midi_uart); + if ((err = snd_opl3_create(card, sonic->synth_port, + sonic->synth_port + 2, + OPL3_HW_OPL3_SV, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + sonic->gameport.io = sonic->game_port; + gameport_register_port(&sonic->gameport); +#endif + strcpy(card->driver, "SonicVibes"); + strcpy(card->shortname, "S3 SonicVibes"); + sprintf(card->longname, "%s rev %i at 0x%lx, irq %i", + card->shortname, + sonic->revision, + pci_resource_start(pci, 1), + sonic->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_sonic_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "S3 SonicVibes", + .id_table = snd_sonic_ids, + .probe = snd_sonic_probe, + .remove = __devexit_p(snd_sonic_remove), +}; + +static int __init alsa_card_sonicvibes_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "S3 SonicVibes soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_sonicvibes_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_sonicvibes_init) +module_exit(alsa_card_sonicvibes_exit) + +#ifndef MODULE + +/* format is: snd-sonicvibes=enable,index,id, + reverb,mge,dmaio */ + +static int __init alsa_card_sonicvibes_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&reverb[nr_dev]) == 2 && + get_option(&str,&mge[nr_dev]) == 2 && + get_option(&str,(int *)&dmaio) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sonicvibes=", alsa_card_sonicvibes_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/trident/Makefile linux/sound/pci/trident/Makefile --- linux-2.4.21-rc1.orig/sound/pci/trident/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/trident/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _trident.o + +list-multi := snd-trident.o snd-trident-synth.o + +export-objs := trident_main.o + +snd-trident-objs := trident.o trident_main.o trident_memory.o +snd-trident-synth-objs := trident_synth.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_TRIDENT) += snd-trident.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_TRIDENT) += snd-trident-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-trident.o: $(snd-trident-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-objs) + +snd-trident-synth.o: $(snd-trident-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-synth-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/trident/trident.c linux/sound/pci/trident/trident.c --- linux-2.4.21-rc1.orig/sound/pci/trident/trident.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/trident/trident.c 2003-03-10 06:35:36.000000000 -0700 @@ -0,0 +1,266 @@ +/* + * Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard + * + * Driver was originated by Trident + * Fri Feb 19 15:55:28 MST 1999 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela , "); +MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Trident,4DWave DX}," + "{Trident,4DWave NX}," + "{SiS,SI7018 PCI Audio}," + "{Best Union,Miss Melody 4DWave PCI}," + "{HIS,4DWave PCI}," + "{Warpspeed,ONSpeed 4DWave PCI}," + "{Aztech Systems,PCI 64-Q3D}," + "{Addonics,SV 750}," + "{CHIC,True Sound 4Dwave}," + "{Shark,Predator4D-PCI}," + "{Jaton,SonicWave 4D}," + "{Hoontech,SoundTrack Digital 4DWave NX}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; +static int wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM."); +MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); +MODULE_PARM(wavetable_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth."); +MODULE_PARM_SYNTAX(wavetable_size, SNDRV_ENABLED ",default:8192,skill:advanced"); + +static struct pci_device_id snd_trident_ids[] __devinitdata = { + { 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave DX PCI Audio */ + { 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave NX PCI Audio */ + { 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* SiS SI7018 PCI Audio */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_trident_ids); + +static int __devinit snd_trident_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + trident_t *trident; + const char *str; + int err, pcm_dev = 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_trident_create(card, pci, + pcm_channels[dev], + ((pci->vendor << 16) | pci->device) == TRIDENT_DEVICE_ID_SI7018 ? 1 : 2, + wavetable_size[dev], + &trident)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_trident_pcm(trident, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + if ((err = snd_trident_foldback_pcm(trident, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + break; + } + if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { + if ((err = snd_trident_spdif_pcm(trident, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, + trident->midi_port, 1, + trident->irq, 0, &trident->rmidi)) < 0) { + snd_card_free(card); + return err; + } + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if ((err = snd_trident_attach_synthesizer(trident)) < 0) { + snd_card_free(card); + return err; + } +#endif + + snd_trident_gameport(trident); + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + str = "TRID4DWAVEDX"; + break; + case TRIDENT_DEVICE_ID_NX: + str = "TRID4DWAVENX"; + break; + case TRIDENT_DEVICE_ID_SI7018: + str = "SI7018"; + break; + default: + str = "Unknown"; + } + strcpy(card->driver, str); + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + strcpy(card->shortname, "SiS "); + } else { + strcpy(card->shortname, "Trident "); + } + strcat(card->shortname, card->driver); + sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", + card->shortname, trident->port, trident->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, trident); + dev++; + return 0; +} + +static void __devexit snd_trident_remove(struct pci_dev *pci) +{ + trident_t *trident = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + if (trident) + snd_card_free(trident->card); + pci_set_drvdata(pci, NULL); +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_trident_suspend(struct pci_dev *pci, u32 state) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO); + snd_trident_suspend(chip); + return 0; +} +static int snd_card_trident_resume(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO); + snd_trident_resume(chip); + return 0; +} +#else +static void snd_card_trident_suspend(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + snd_trident_suspend(chip); +} +static void snd_card_trident_resume(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + snd_trident_resume(chip); +} +#endif +#endif + +static struct pci_driver driver = { + .name = "Trident4DWaveAudio", + .id_table = snd_trident_ids, + .probe = snd_trident_probe, + .remove = __devexit_p(snd_trident_remove), +#ifdef CONFIG_PM + .suspend = snd_card_trident_suspend, + .resume = snd_card_trident_resume, +#endif +}; + +static int __init alsa_card_trident_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Trident 4DWave PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_trident_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_trident_init) +module_exit(alsa_card_trident_exit) + +#ifndef MODULE + +/* format is: snd-trident=enable,index,id, + pcm_channels,wavetable_size */ + +static int __init alsa_card_trident_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,&pcm_channels[nr_dev]) == 2 && + get_option(&str,&wavetable_size[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-trident=", alsa_card_trident_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/trident/trident_main.c linux/sound/pci/trident/trident_main.c --- linux-2.4.21-rc1.orig/sound/pci/trident/trident_main.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/trident/trident_main.c 2003-03-12 04:27:55.000000000 -0700 @@ -0,0 +1,3999 @@ +/* + * Maintained by Jaroslav Kysela + * Originated by audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Routines for control of Trident 4DWave (DX and NX) chip + * + * BUGS: + * + * TODO: + * --- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * SiS7018 S/PDIF support by Thomas Winischhofer + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define chip_t trident_t + +static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); +static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); +static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_PM +static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state); +#endif +static int snd_trident_sis_reset(trident_t *trident); + +/* + * common I/O routines + */ + + +#if 0 +static void snd_trident_print_voice_regs(trident_t *trident, int voice) +{ + unsigned int val, tmp; + + printk("Trident voice %i:\n", voice); + outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR)); + val = inl(TRID_REG(trident, CH_LBA)); + printk("LBA: 0x%x\n", val); + val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + printk("GVSel: %i\n", val >> 31); + printk("Pan: 0x%x\n", (val >> 24) & 0x7f); + printk("Vol: 0x%x\n", (val >> 16) & 0xff); + printk("CTRL: 0x%x\n", (val >> 12) & 0x0f); + printk("EC: 0x%x\n", val & 0x0fff); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS)); + printk("CSO: 0x%x\n", val >> 16); + printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff); + printk("FMS: 0x%x\n", val & 0x0f); + val = inl(TRID_REG(trident, CH_DX_ESO_DELTA)); + printk("ESO: 0x%x\n", val >> 16); + printk("Delta: 0x%x\n", val & 0xffff); + val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); + } else { // TRIDENT_DEVICE_ID_NX + val = inl(TRID_REG(trident, CH_NX_DELTA_CSO)); + tmp = (val >> 24) & 0xff; + printk("CSO: 0x%x\n", val & 0x00ffffff); + val = inl(TRID_REG(trident, CH_NX_DELTA_ESO)); + tmp |= (val >> 16) & 0xff00; + printk("Delta: 0x%x\n", tmp); + printk("ESO: 0x%x\n", val & 0x00ffffff); + val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL)); + printk("Alpha: 0x%x\n", val >> 20); + printk("FMS: 0x%x\n", (val >> 16) & 0x0f); + } + printk("FMC: 0x%x\n", (val >> 14) & 3); + printk("RVol: 0x%x\n", (val >> 7) & 0x7f); + printk("CVol: 0x%x\n", val & 0x7f); +} +#endif + +/*--------------------------------------------------------------------------- + unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) + + Description: This routine will do all of the reading from the external + CODEC (AC97). + + Parameters: ac97 - ac97 codec structure + reg - CODEC register index, from AC97 Hal. + + returns: 16 bit value read from the AC97. + + ---------------------------------------------------------------------------*/ +static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) +{ + unsigned int data = 0, treg; + unsigned short count = 0xffff; + unsigned long flags; + trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return -ENXIO); + + spin_lock_irqsave(&trident->reg_lock, flags); + if (trident->device == TRIDENT_DEVICE_ID_DX) { + data = (DX_AC97_BUSY_READ | (reg & 0x000000ff)); + outl(data, TRID_REG(trident, DX_ACR1_AC97_R)); + do { + data = inl(TRID_REG(trident, DX_ACR1_AC97_R)); + if ((data & DX_AC97_BUSY_READ) == 0) + break; + } while (--count); + } else if (trident->device == TRIDENT_DEVICE_ID_NX) { + data = (NX_AC97_BUSY_READ | (reg & 0x000000ff)); + treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY; + outl(data, TRID_REG(trident, treg)); + do { + data = inl(TRID_REG(trident, treg)); + if ((data & 0x00000C00) == 0) + break; + } while (--count); + } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); + if (ac97->num == 1) + data |= SI_AC97_SECONDARY; + outl(data, TRID_REG(trident, SI_AC97_READ)); + do { + data = inl(TRID_REG(trident, SI_AC97_READ)); + if ((data & (SI_AC97_BUSY_READ)) == 0) + break; + } while (--count); + } + + if (count == 0 && !trident->ac97_detect) { + snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data); + data = 0; + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return ((unsigned short) (data >> 16)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) + + Description: This routine will do all of the writing to the external + CODEC (AC97). + + Parameters: ac97 - ac97 codec structure + reg - CODEC register index, from AC97 Hal. + data - Lower 16 bits are the data to write to CODEC. + + returns: TRUE if everything went ok, else FALSE. + + ---------------------------------------------------------------------------*/ +static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) +{ + unsigned int address, data; + unsigned short count = 0xffff; + unsigned long flags; + trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return); + + data = ((unsigned long) wdata) << 16; + + spin_lock_irqsave(&trident->reg_lock, flags); + if (trident->device == TRIDENT_DEVICE_ID_DX) { + address = DX_ACR0_AC97_W; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0) + break; + } while (--count); + + data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff)); + } else if (trident->device == TRIDENT_DEVICE_ID_NX) { + address = NX_ACR1_AC97_W; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0) + break; + } while (--count); + + data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff)); + } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + address = SI_AC97_WRITE; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0) + break; + } while (--count); + + data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); + if (ac97->num == 1) + data |= SI_AC97_SECONDARY; + } else { + address = 0; /* keep GCC happy */ + count = 0; /* return */ + } + + if (count == 0) { + spin_unlock_irqrestore(&trident->reg_lock, flags); + return; + } + outl(data, TRID_REG(trident, address)); + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +/*--------------------------------------------------------------------------- + void snd_trident_enable_eso(trident_t *trident) + + Description: This routine will enable end of loop interrupts. + End of loop interrupts will occur when a running + channel reaches ESO. + Also enables middle of loop interrupts. + + Parameters: trident - pointer to target device class for 4DWave. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_enable_eso(trident_t * trident) +{ + unsigned int val; + + val = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + val |= ENDLP_IE; + val |= MIDLP_IE; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + val |= BANK_B_EN; + outl(val, TRID_REG(trident, T4D_LFO_GC_CIR)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_disable_eso(trident_t *trident) + + Description: This routine will disable end of loop interrupts. + End of loop interrupts will occur when a running + channel reaches ESO. + Also disables middle of loop interrupts. + + Parameters: + trident - pointer to target device class for 4DWave. + + returns: TRUE if everything went ok, else FALSE. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_disable_eso(trident_t * trident) +{ + unsigned int tmp; + + tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + tmp &= ~ENDLP_IE; + tmp &= ~MIDLP_IE; + outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_start_voice(trident_t * trident, unsigned int voice) + + Description: Start a voice, any channel 0 thru 63. + This routine automatically handles the fact that there are + more than 32 channels available. + + Parameters : voice - Voice number 0 thru n. + trident - pointer to target device class for 4DWave. + + Return Value: None. + + ---------------------------------------------------------------------------*/ + +void snd_trident_start_voice(trident_t * trident, unsigned int voice) +{ + unsigned int mask = 1 << (voice & 0x1f); + unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A; + + outl(mask, TRID_REG(trident, reg)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_stop_voice(trident_t * trident, unsigned int voice) + + Description: Stop a voice, any channel 0 thru 63. + This routine automatically handles the fact that there are + more than 32 channels available. + + Parameters : voice - Voice number 0 thru n. + trident - pointer to target device class for 4DWave. + + Return Value: None. + + ---------------------------------------------------------------------------*/ + +void snd_trident_stop_voice(trident_t * trident, unsigned int voice) +{ + unsigned int mask = 1 << (voice & 0x1f); + unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A; + + outl(mask, TRID_REG(trident, reg)); +} + +/*--------------------------------------------------------------------------- + int snd_trident_allocate_pcm_channel(trident_t *trident) + + Description: Allocate hardware channel in Bank B (32-63). + + Parameters : trident - pointer to target device class for 4DWave. + + Return Value: hardware channel - 32-63 or -1 when no channel is available + + ---------------------------------------------------------------------------*/ + +static int snd_trident_allocate_pcm_channel(trident_t * trident) +{ + int idx; + + if (trident->ChanPCMcnt >= trident->ChanPCM) + return -1; + for (idx = 31; idx >= 0; idx--) { + if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) { + trident->ChanMap[T4D_BANK_B] |= 1 << idx; + trident->ChanPCMcnt++; + return idx + 32; + } + } + return -1; +} + +/*--------------------------------------------------------------------------- + void snd_trident_free_pcm_channel(int channel) + + Description: Free hardware channel in Bank B (32-63) + + Parameters : trident - pointer to target device class for 4DWave. + channel - hardware channel number 0-63 + + Return Value: none + + ---------------------------------------------------------------------------*/ + +static void snd_trident_free_pcm_channel(trident_t *trident, int channel) +{ + if (channel < 32 || channel > 63) + return; + channel &= 0x1f; + if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) { + trident->ChanMap[T4D_BANK_B] &= ~(1 << channel); + trident->ChanPCMcnt--; + } +} + +/*--------------------------------------------------------------------------- + unsigned int snd_trident_allocate_synth_channel(void) + + Description: Allocate hardware channel in Bank A (0-31). + + Parameters : trident - pointer to target device class for 4DWave. + + Return Value: hardware channel - 0-31 or -1 when no channel is available + + ---------------------------------------------------------------------------*/ + +static int snd_trident_allocate_synth_channel(trident_t * trident) +{ + int idx; + + for (idx = 31; idx >= 0; idx--) { + if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) { + trident->ChanMap[T4D_BANK_A] |= 1 << idx; + trident->synth.ChanSynthCount++; + return idx; + } + } + return -1; +} + +/*--------------------------------------------------------------------------- + void snd_trident_free_synth_channel( int channel ) + + Description: Free hardware channel in Bank B (0-31). + + Parameters : trident - pointer to target device class for 4DWave. + channel - hardware channel number 0-63 + + Return Value: none + + ---------------------------------------------------------------------------*/ + +static void snd_trident_free_synth_channel(trident_t *trident, int channel) +{ + if (channel < 0 || channel > 31) + return; + channel &= 0x1f; + if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) { + trident->ChanMap[T4D_BANK_A] &= ~(1 << channel); + trident->synth.ChanSynthCount--; + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_voice_regs + + Description: This routine will complete and write the 5 hardware channel + registers to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Each register field. + + ---------------------------------------------------------------------------*/ + +void snd_trident_write_voice_regs(trident_t * trident, + snd_trident_voice_t * voice) +{ + unsigned int FmcRvolCvol; + unsigned int regs[5]; + + regs[1] = voice->LBA; + regs[4] = (voice->GVSel << 31) | + ((voice->Pan & 0x0000007f) << 24) | + ((voice->CTRL & 0x0000000f) << 12); + FmcRvolCvol = ((voice->FMC & 3) << 14) | + ((voice->RVol & 0x7f) << 7) | + (voice->CVol & 0x7f); + + switch (trident->device) { + case TRIDENT_DEVICE_ID_SI7018: + regs[4] |= voice->number > 31 ? + (voice->Vol & 0x000003ff) : + ((voice->Vol & 0x00003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f); + regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); + regs[3] = (voice->Attribute << 16) | FmcRvolCvol; + break; + case TRIDENT_DEVICE_ID_DX: + regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f); + regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); + regs[3] = FmcRvolCvol; + break; + case TRIDENT_DEVICE_ID_NX: + regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff); + regs[2] = ((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff); + regs[3] = (voice->Alpha << 20) | ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol; + break; + default: + snd_BUG(); + } + + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl(regs[0], TRID_REG(trident, CH_START + 0)); + outl(regs[1], TRID_REG(trident, CH_START + 4)); + outl(regs[2], TRID_REG(trident, CH_START + 8)); + outl(regs[3], TRID_REG(trident, CH_START + 12)); + outl(regs[4], TRID_REG(trident, CH_START + 16)); + +#if 0 + printk("written %i channel:\n", voice->number); + printk(" regs[0] = 0x%x/0x%x\n", regs[0], inl(TRID_REG(trident, CH_START + 0))); + printk(" regs[1] = 0x%x/0x%x\n", regs[1], inl(TRID_REG(trident, CH_START + 4))); + printk(" regs[2] = 0x%x/0x%x\n", regs[2], inl(TRID_REG(trident, CH_START + 8))); + printk(" regs[3] = 0x%x/0x%x\n", regs[3], inl(TRID_REG(trident, CH_START + 12))); + printk(" regs[4] = 0x%x/0x%x\n", regs[4], inl(TRID_REG(trident, CH_START + 16))); +#endif +} + +/*--------------------------------------------------------------------------- + snd_trident_write_cso_reg + + Description: This routine will write the new CSO offset + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + CSO - new CSO value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_cso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CSO) +{ + voice->CSO = CSO; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); + } else { + outl((voice->Delta << 24) | (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_eso_reg + + Description: This routine will write the new ESO offset + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + ESO - new ESO value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_eso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int ESO) +{ + voice->ESO = ESO; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2); + } else { + outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_ESO)); + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_vol_reg + + Description: This routine will write the new voice volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Vol - new voice volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_vol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Vol) +{ + voice->Vol = Vol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2)); + break; + case TRIDENT_DEVICE_ID_SI7018: + // printk("voice->Vol = 0x%x\n", voice->Vol); + outw((voice->CTRL << 12) | voice->Vol, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + break; + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_pan_reg + + Description: This routine will write the new voice pan + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Pan - new pan value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_pan_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Pan) +{ + voice->Pan = Pan; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_rvol_reg + + Description: This routine will write the new reverb volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + RVol - new reverb volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_rvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int RVol) +{ + voice->RVol = RVol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f), + TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_cvol_reg + + Description: This routine will write the new chorus volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + CVol - new chorus volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_cvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CVol) +{ + voice->CVol = CVol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f), + TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); +} + +/*--------------------------------------------------------------------------- + snd_trident_convert_rate + + Description: This routine converts rate in HZ to hardware delta value. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_convert_rate(unsigned int rate) +{ + unsigned int delta; + + // We special case 44100 and 8000 since rounding with the equation + // does not give us an accurate enough value. For 11025 and 22050 + // the equation gives us the best answer. All other frequencies will + // also use the equation. JDW + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff; + return delta; +} + +/*--------------------------------------------------------------------------- + snd_trident_convert_adc_rate + + Description: This routine converts rate in HZ to hardware delta value. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +static unsigned int snd_trident_convert_adc_rate(unsigned int rate) +{ + unsigned int delta; + + // We special case 44100 and 8000 since rounding with the equation + // does not give us an accurate enough value. For 11025 and 22050 + // the equation gives us the best answer. All other frequencies will + // also use the equation. JDW + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + return delta; +} + +/*--------------------------------------------------------------------------- + snd_trident_spurious_threshold + + Description: This routine converts rate in HZ to spurious threshold. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_spurious_threshold(unsigned int rate, unsigned int period_size) +{ + unsigned int res = (rate * period_size) / 48000; + if (res < 64) + res = res / 2; + else + res -= 32; + return res; +} + +/*--------------------------------------------------------------------------- + snd_trident_control_mode + + Description: This routine returns a control mode for a PCM channel. + + Paramters: trident - pointer to target device class for 4DWave. + substream - PCM substream + + Returns: Control value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream) +{ + unsigned int CTRL; + snd_pcm_runtime_t *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; // 16-bit data + if (snd_pcm_format_signed(runtime->format)) + CTRL |= 0x00000002; // signed data + if (runtime->channels > 1) + CTRL |= 0x00000004; // stereo data + return CTRL; +} + +/* + * PCM part + */ + +/*--------------------------------------------------------------------------- + snd_trident_ioctl + + Description: Device I/O control handler for playback/capture parameters. + + Paramters: substream - PCM substream class + cmd - what ioctl message to process + arg - additional message infoarg + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + /* FIXME: it seems that with small periods the behaviour of + trident hardware is unpredictable and interrupt generator + is broken */ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +/*--------------------------------------------------------------------------- + snd_trident_allocate_pcm_mem + + Description: Allocate PCM ring buffer for given substream + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (trident->tlb.entries) { + if (err > 0) { /* change */ + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = snd_trident_alloc_pages(trident, substream); + if (voice->memblk == NULL) + return -ENOMEM; + } + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_allocate_evoice + + Description: Allocate extra voice as interrupt generator + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +int snd_trident_allocate_evoice(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_hw_params + + Description: Set the hardware parameters for the playback device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + int err; + + err = snd_trident_allocate_pcm_mem(substream, hw_params); + if (err >= 0) + err = snd_trident_allocate_evoice(substream, hw_params); + return err; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_hw_free + + Description: Release the hardware resources for the playback device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + if (trident->tlb.entries) { + if (voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + } + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_prepare + + Description: Prepare playback device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number]; + + spin_lock(&trident->reg_lock); + + /* set delta (rate) value */ + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + /* set Loop Begin Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->GVSel = 1; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Vol = mix->vol; + voice->RVol = mix->rvol; + voice->CVol = mix->cvol; + voice->Pan = mix->pan; + voice->Attribute = 0; +#if 0 + voice->Attribute = (1<<(30-16))|(2<<(26-16))| + (0<<(24-16))|(0x1f<<(19-16)); +#else + voice->Attribute = 0; +#endif + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ +#if 0 + evoice->Attribute = (1<<(30-16))|(2<<(26-16))| + (0<<(24-16))|(0x1f<<(19-16)); +#else + evoice->Attribute = 0; +#endif + snd_trident_write_voice_regs(trident, evoice); + evoice->isync2 = 1; + evoice->isync_mark = runtime->period_size; + evoice->ESO = (runtime->period_size * 2) - 1; + } + + spin_unlock(&trident->reg_lock); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_hw_params + + Description: Set the hardware parameters for the capture device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_trident_allocate_pcm_mem(substream, hw_params); +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_prepare + + Description: Prepare capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int val, ESO_bytes; + + snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI, return -EIO); + + spin_lock(&trident->reg_lock); + + // Initilize the channel and set channel Mode + outb(0, TRID_REG(trident, LEGACY_DMAR15)); + + // Set DMA channel operation mode register + outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); + + // Set channel buffer Address, DMAR0 expects contiguous PCI memory area + voice->LBA = runtime->dma_addr; + outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); + if (voice->memblk) + voice->LBA = voice->memblk->offset; + + // set ESO + ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; + outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6)); + outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4)); + ESO_bytes++; + + // Set channel sample rate, 4.12 format + val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate; + outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R)); + + // Set channel interrupt blk length + if (snd_pcm_format_width(runtime->format) == 16) { + val = (unsigned short) ((ESO_bytes >> 1) - 1); + } else { + val = (unsigned short) (ESO_bytes - 1); + } + + outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL)); + + // Right now, set format and start to run captureing, + // continuous run loop enable. + trident->bDMAStart = 0x19; // 0001 1001b + + if (snd_pcm_format_width(runtime->format) == 16) + trident->bDMAStart |= 0x80; + if (snd_pcm_format_signed(runtime->format)) + trident->bDMAStart |= 0x20; + if (runtime->channels > 1) + trident->bDMAStart |= 0x40; + + // Prepare capture intr channel + + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + voice->isync = 1; + voice->isync_mark = runtime->period_size; + voice->isync_max = runtime->buffer_size; + + // Set voice parameters + voice->CSO = 0; + voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1; + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; /* mute */ + voice->Vol = 0x3ff; /* mute */ + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + snd_trident_write_voice_regs(trident, voice); + + spin_unlock(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_hw_params + + Description: Set the hardware parameters for the capture device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + + return snd_trident_allocate_evoice(substream, hw_params); +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_hw_free + + Description: Release the hardware resources for the capture device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_prepare + + Description: Prepare capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + + spin_lock(&trident->reg_lock); + + voice->LBA = runtime->dma_addr; + voice->Delta = snd_trident_convert_adc_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + // Set voice parameters + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 0; + voice->RVol = 0; + voice->CVol = 0; + voice->GVSel = 1; + voice->Pan = T4D_DEFAULT_PCM_PAN; + voice->Vol = 0; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + + voice->Attribute = (2 << (30-16)) | + (2 << (26-16)) | + (2 << (24-16)) | + (1 << (23-16)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = snd_trident_convert_rate(runtime->rate); + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = 0; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + evoice->isync2 = 1; + evoice->isync_mark = runtime->period_size; + evoice->ESO = (runtime->period_size * 2) - 1; + } + + spin_unlock(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_prepare + + Description: Prepare foldback capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + + spin_lock(&trident->reg_lock); + + /* Set channel buffer Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + /* set target ESO for channel */ + voice->ESO = runtime->buffer_size - 1; /* in samples */ + + /* set sample rate */ + voice->Delta = 0x1000; + voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); + + voice->CSO = 0; + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; /* mute */ + voice->Vol = 0x3ff; /* mute */ + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + /* set up capture channel */ + outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + evoice->isync2 = 1; + evoice->isync_mark = runtime->period_size; + evoice->ESO = (runtime->period_size * 2) - 1; + } + + spin_unlock(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_hw_params + + Description: Set the hardware parameters for the spdif device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + unsigned int old_bits = 0, change = 0; + int err; + + err = snd_trident_allocate_pcm_mem(substream, hw_params); + if (err < 0) + return err; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + err = snd_trident_allocate_evoice(substream, hw_params); + if (err < 0) + return err; + } + + /* prepare SPDIF channel */ + spin_lock_irq(&trident->reg_lock); + old_bits = trident->spdif_pcm_bits; + if (old_bits & IEC958_AES0_PROFESSIONAL) + trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS; + else + trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24); + if (params_rate(hw_params) >= 48000) { + trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_48000 : + (IEC958_AES3_CON_FS_48000 << 24); + } + else if (params_rate(hw_params) >= 44100) { + trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_44100 : + (IEC958_AES3_CON_FS_44100 << 24); + } + else { + trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_32000 : + (IEC958_AES3_CON_FS_32000 << 24); + } + change = old_bits != trident->spdif_pcm_bits; + spin_unlock_irq(&trident->reg_lock); + + if (change) + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_prepare + + Description: Prepare SPDIF device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number]; + unsigned int RESO, LBAO; + unsigned int temp; + + spin_lock(&trident->reg_lock); + + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + + /* set delta (rate) value */ + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + /* set Loop Back Address */ + LBAO = runtime->dma_addr; + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = LBAO; + + voice->isync = 1; + voice->isync3 = 1; + voice->isync_mark = runtime->period_size; + voice->isync_max = runtime->buffer_size; + + /* set target ESO for channel */ + RESO = runtime->buffer_size - 1; + voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1; + + /* set ctrl mode */ + voice->CTRL = snd_trident_control_mode(substream); + + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; + voice->Vol = 0x3ff; + voice->EC = 0; + voice->CSO = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + /* prepare surrogate IRQ channel */ + snd_trident_write_voice_regs(trident, voice); + + outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO)); + outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2)); + outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA)); + outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO)); + outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2)); + + /* set SPDIF setting */ + outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + + } else { /* SiS */ + + /* set delta (rate) value */ + voice->Delta = 0x800; + voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); + + /* set Loop Begin Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->GVSel = 1; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Vol = mix->vol; + voice->RVol = mix->rvol; + voice->CVol = mix->cvol; + voice->Pan = mix->pan; + voice->Attribute = (1<<(30-16))|(7<<(26-16))| + (0<<(24-16))|(0<<(19-16)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + evoice->isync2 = 1; + evoice->isync_mark = runtime->period_size; + evoice->ESO = (runtime->period_size * 2) - 1; + } + + outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS)); + temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + temp &= ~(1<<19); + outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR)); + temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + temp |= SPDIF_EN; + outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + } + + spin_unlock(&trident->reg_lock); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_trigger + + Description: Start/stop devices + + Parameters: substream - PCM substream class + cmd - trigger command (STOP, GO) + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_trigger(snd_pcm_substream_t *substream, + int cmd) + +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *s; + unsigned int what, whati, capture_flag, spdif_flag; + snd_trident_voice_t *voice, *evoice; + unsigned int val, go; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + go = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + go = 0; + break; + default: + return -EINVAL; + } + what = whati = capture_flag = spdif_flag = 0; + s = substream; + spin_lock(&trident->reg_lock); + val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; + do { + if ((trident_t *) _snd_pcm_chip(s->pcm) == trident) { + voice = (snd_trident_voice_t *) s->runtime->private_data; + evoice = voice->extra; + what |= 1 << (voice->number & 0x1f); + if (evoice == NULL) { + whati |= 1 << (voice->number & 0x1f); + } else { + what |= 1 << (evoice->number & 0x1f); + whati |= 1 << (evoice->number & 0x1f); + if (go) + evoice->stimer = val; + } + if (go) { + voice->running = 1; + voice->stimer = val; + } else { + voice->running = 0; + } + snd_pcm_trigger_done(s, substream); + if (voice->capture) + capture_flag = 1; + if (voice->spdif) + spdif_flag = 1; + } + s = s->link_next; + } while (s != substream); + if (spdif_flag) { + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } else { + outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS)); + val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN; + outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + } + } + if (!go) + outl(what, TRID_REG(trident, T4D_STOP_B)); + val = inl(TRID_REG(trident, T4D_AINTEN_B)); + if (go) { + val |= whati; + } else { + val &= ~whati; + } + outl(val, TRID_REG(trident, T4D_AINTEN_B)); + if (go) { + outl(what, TRID_REG(trident, T4D_START_B)); + + if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) + outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); + } else { + if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) + outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); + } + spin_unlock(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_pointer + + Description: This routine return the playback position + + Parameters: substream - PCM substream class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_playback_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int cso; + + if (!voice->running) + return 0; + + spin_lock(&trident->reg_lock); + + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + + if (trident->device != TRIDENT_DEVICE_ID_NX) { + cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); + } else { // ID_4DWAVE_NX + cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff; + } + + spin_unlock(&trident->reg_lock); + + if (cso >= runtime->buffer_size) + cso = 0; + + return cso; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_pointer + + Description: This routine return the capture position + + Paramters: pcm1 - PCM device class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_capture_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int result; + + if (!voice->running) + return 0; + + result = inw(TRID_REG(trident, T4D_SBBL_SBCL)); + if (runtime->channels > 1) + result >>= 1; + if (result > 0) + result = runtime->buffer_size - result; + + return result; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_pointer + + Description: This routine return the SPDIF playback position + + Parameters: substream - PCM substream class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_spdif_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int result; + + if (!voice->running) + return 0; + + result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; + + return result; +} + +/* + * Playback support device description + */ + +static snd_pcm_hardware_t snd_trident_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 64, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_trident_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * Foldback capture support device description + */ + +static snd_pcm_hardware_t snd_trident_foldback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * SPDIF playback support device description + */ + +static snd_pcm_hardware_t snd_trident_spdif = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_trident_spdif_7018 = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + trident_t *trident; + + if (voice) { + trident = voice->trident; + snd_trident_free_voice(trident, voice); + } +} + +static int snd_trident_playback_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice; + + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) + return -EAGAIN; + snd_trident_pcm_mixer_build(trident, voice, substream); + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_playback; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_close + + Description: This routine will close the 4DWave playback device. For now + we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_playback_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + snd_trident_pcm_mixer_free(trident, voice, substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_open + + Description: This routine will open the 4DWave SPDIF device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) + return -EAGAIN; + voice->spdif = 1; + voice->substream = substream; + spin_lock_irq(&trident->reg_lock); + trident->spdif_pcm_bits = trident->spdif_bits; + spin_unlock_irq(&trident->reg_lock); + + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + runtime->hw = snd_trident_spdif; + } else { + runtime->hw = snd_trident_spdif_7018; + } + + trident->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +/*--------------------------------------------------------------------------- + snd_trident_spdif_close + + Description: This routine will close the 4DWave SPDIF device. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + unsigned int temp; + + spin_lock_irq(&trident->reg_lock); + // restore default SPDIF setting + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + } else { + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + if (trident->spdif_ctrl) { + temp |= SPDIF_EN; + } else { + temp &= ~SPDIF_EN; + } + outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + } + spin_unlock_irq(&trident->reg_lock); + trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_open + + Description: This routine will open the 4DWave capture device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) + return -EAGAIN; + voice->capture = 1; + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_capture; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_close + + Description: This routine will close the 4DWave capture device. For now + we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_capture_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_open + + Description: This routine will open the 4DWave foldback capture device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) + return -EAGAIN; + voice->foldback_chan = substream->number; + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_foldback; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_close + + Description: This routine will close the 4DWave foldback capture device. + For now we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_foldback_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + voice = (snd_trident_voice_t *) runtime->private_data; + + /* stop capture channel */ + spin_lock_irq(&trident->reg_lock); + outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); + spin_unlock_irq(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + PCM operations + ---------------------------------------------------------------------------*/ + +static snd_pcm_ops_t snd_trident_playback_ops = { + .open = snd_trident_playback_open, + .close = snd_trident_playback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_playback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_nx_playback_ops = { + .open = snd_trident_playback_open, + .close = snd_trident_playback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_playback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static snd_pcm_ops_t snd_trident_capture_ops = { + .open = snd_trident_capture_open, + .close = snd_trident_capture_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_capture_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_capture_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_capture_pointer, +}; + +static snd_pcm_ops_t snd_trident_si7018_capture_ops = { + .open = snd_trident_capture_open, + .close = snd_trident_capture_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_si7018_capture_hw_params, + .hw_free = snd_trident_si7018_capture_hw_free, + .prepare = snd_trident_si7018_capture_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_foldback_ops = { + .open = snd_trident_foldback_open, + .close = snd_trident_foldback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_foldback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_nx_foldback_ops = { + .open = snd_trident_foldback_open, + .close = snd_trident_foldback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_foldback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static snd_pcm_ops_t snd_trident_spdif_ops = { + .open = snd_trident_spdif_open, + .close = snd_trident_spdif_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_spdif_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_spdif_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_spdif_pointer, +}; + +static snd_pcm_ops_t snd_trident_spdif_7018_ops = { + .open = snd_trident_spdif_open, + .close = snd_trident_spdif_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_spdif_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_spdif_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_free + + Description: This routine release the 4DWave private data. + + Paramters: private_data - pointer to 4DWave device info. + + Returns: None + + ---------------------------------------------------------------------------*/ +static void snd_trident_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->foldback = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->spdif = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +/*--------------------------------------------------------------------------- + snd_trident_pcm + + Description: This routine registers the 4DWave device for PCM support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0) + return err; + + pcm->private_data = trident; + pcm->private_free = snd_trident_pcm_free; + + if (trident->tlb.entries) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops); + } else { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); + } + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + trident->device != TRIDENT_DEVICE_ID_SI7018 ? + &snd_trident_capture_ops : + &snd_trident_si7018_capture_ops); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "Trident 4DWave"); + trident->pcm = pcm; + + if (trident->tlb.entries) { + snd_pcm_substream_t *substream; + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_sg_pages(trident->pci, substream, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pci_pages(trident->pci, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, 128*1024); + } else { + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 128*1024); + } + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_pcm + + Description: This routine registers the 4DWave device for foldback PCM support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *foldback; + int err; + int num_chan = 3; + snd_pcm_substream_t *substream; + + if (rpcm) + *rpcm = NULL; + if (trident->device == TRIDENT_DEVICE_ID_NX) + num_chan = 4; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0) + return err; + + foldback->private_data = trident; + foldback->private_free = snd_trident_foldback_pcm_free; + if (trident->tlb.entries) + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops); + else + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); + foldback->info_flags = 0; + strcpy(foldback->name, "Trident 4DWave"); + substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + strcpy(substream->name, "Front Mixer"); + substream = substream->next; + strcpy(substream->name, "Reverb Mixer"); + substream = substream->next; + strcpy(substream->name, "Chorus Mixer"); + if (num_chan == 4) { + substream = substream->next; + strcpy(substream->name, "Second AC'97 ADC"); + } + trident->foldback = foldback; + + if (trident->tlb.entries) + snd_pcm_lib_preallocate_sg_pages_for_all(trident->pci, foldback, 0, 128*1024); + else + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + + if (rpcm) + *rpcm = foldback; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif + + Description: This routine registers the 4DWave-NX device for SPDIF support. + + Paramters: trident - pointer to target device class for 4DWave-NX. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *spdif; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0) + return err; + + spdif->private_data = trident; + spdif->private_free = snd_trident_spdif_pcm_free; + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); + } else { + snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); + } + spdif->info_flags = 0; + strcpy(spdif->name, "Trident 4DWave IEC958"); + trident->spdif = spdif; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + + if (rpcm) + *rpcm = spdif; + return 0; +} + +/* + * Mixer part + */ + + +/*--------------------------------------------------------------------------- + snd_trident_spdif_control + + Description: enable/disable S/PDIF out from ac97 mixer + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_trident_spdif_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->spdif_ctrl; + ucontrol->value.integer.value[0] = val == kcontrol->private_value; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + int change; + + val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00; + spin_lock_irqsave(&trident->reg_lock, flags); + /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */ + change = trident->spdif_ctrl != val; + trident->spdif_ctrl = val; + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) { + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + } else { + if (trident->spdif == NULL) { + unsigned int temp; + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN; + if (val) + temp |= SPDIF_EN; + outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + } + } + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), + .info = snd_trident_spdif_control_info, + .get = snd_trident_spdif_control_get, + .put = snd_trident_spdif_control_put, + .private_value = 0x28, +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_default + + Description: put/get the S/PDIF default settings + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&trident->reg_lock, flags); + change = trident->spdif_bits != val; + trident->spdif_bits = val; + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + } else { + if (trident->spdif == NULL) + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + } + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_trident_spdif_default_info, + .get = snd_trident_spdif_default_get, + .put = snd_trident_spdif_default_put +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_mask + + Description: put/get the S/PDIF mask + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + .info = snd_trident_spdif_mask_info, + .get = snd_trident_spdif_mask_get, +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_stream + + Description: put/get the S/PDIF stream settings + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&trident->reg_lock, flags); + change = trident->spdif_pcm_bits != val; + trident->spdif_pcm_bits = val; + if (trident->spdif != NULL) { + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + } else { + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + } + } + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_trident_spdif_stream_info, + .get = snd_trident_spdif_stream_get, + .put = snd_trident_spdif_stream_put +}; + +/*--------------------------------------------------------------------------- + snd_trident_ac97_control + + Description: enable/disable rear path for ac97 + ---------------------------------------------------------------------------*/ + +static int snd_trident_ac97_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_trident_ac97_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_ac97_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + int change = 0; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + val &= ~(1 << kcontrol->private_value); + if (ucontrol->value.integer.value[0]) + val |= 1 << kcontrol->private_value; + change = val != trident->ac97_ctrl; + trident->ac97_ctrl = val; + outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Rear Path", + .info = snd_trident_ac97_control_info, + .get = snd_trident_ac97_control_get, + .put = snd_trident_ac97_control_put, + .private_value = 4, +}; + +/*--------------------------------------------------------------------------- + snd_trident_vol_control + + Description: wave & music volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_trident_vol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned int val; + + val = trident->musicvol_wavevol; + ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff); + ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff); + return 0; +} + +static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->musicvol_wavevol; + val &= ~(0xffff << kcontrol->private_value); + val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) | + ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value; + change = val != trident->musicvol_wavevol; + outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Music Playback Volume", + .info = snd_trident_vol_control_info, + .get = snd_trident_vol_control_get, + .put = snd_trident_vol_control_put, + .private_value = 16, +}; + +static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Wave Playback Volume", + .info = snd_trident_vol_control_info, + .get = snd_trident_vol_control_get, + .put = snd_trident_vol_control_put, + .private_value = 0, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_vol_control + + Description: PCM front volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + uinfo->value.integer.max = 1023; + return 0; +} + +static int snd_trident_pcm_vol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + ucontrol->value.integer.value[0] = 1023 - mix->vol; + } else { + ucontrol->value.integer.value[0] = 255 - (mix->vol>>2); + } + return 0; +} + +static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned int val; + int change = 0; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + val = 1023 - (ucontrol->value.integer.value[0] & 1023); + } else { + val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2; + } + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->vol; + mix->vol = val; + if (mix->voice != NULL) + snd_trident_write_vol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Front Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_trident_pcm_vol_control_info, + .get = snd_trident_pcm_vol_control_get, + .put = snd_trident_pcm_vol_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_pan_control + + Description: PCM front pan control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_pan_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_pan_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = mix->pan; + if (ucontrol->value.integer.value[0] & 0x40) { + ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)); + } else { + ucontrol->value.integer.value[0] |= 0x40; + } + return 0; +} + +static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned char val; + int change = 0; + + if (ucontrol->value.integer.value[0] & 0x40) + val = ucontrol->value.integer.value[0] & 0x3f; + else + val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40; + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->pan; + mix->pan = val; + if (mix->voice != NULL) + snd_trident_write_pan_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Pan Playback Control", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_trident_pcm_pan_control_info, + .get = snd_trident_pcm_pan_control_get, + .put = snd_trident_pcm_pan_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_rvol_control + + Description: PCM reverb volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_rvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_rvol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = 127 - mix->rvol; + return 0; +} + +static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned short val; + int change = 0; + + val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->rvol; + mix->rvol = val; + if (mix->voice != NULL) + snd_trident_write_rvol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Reverb Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_trident_pcm_rvol_control_info, + .get = snd_trident_pcm_rvol_control_get, + .put = snd_trident_pcm_rvol_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_cvol_control + + Description: PCM chorus volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_cvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_cvol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = 127 - mix->cvol; + return 0; +} + +static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned short val; + int change = 0; + + val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->cvol; + mix->cvol = val; + if (mix->voice != NULL) + snd_trident_write_cvol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Chorus Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_trident_pcm_cvol_control_info, + .get = snd_trident_pcm_cvol_control_get, + .put = snd_trident_pcm_cvol_control_put, +}; + +static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int activate) +{ + snd_runtime_check(kctl != NULL, return); + if (activate) + kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); +} + +static void snd_trident_notify_pcm_change(snd_card_t * card, snd_trident_pcm_mixer_t * tmix, int activate) +{ + snd_trident_notify_pcm_change1(card, tmix->ctl_vol, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_pan, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_rvol, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_cvol, activate); +} + +static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream) +{ + snd_trident_pcm_mixer_t *tmix; + + snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL); + tmix = &trident->pcm_mixer[substream->number]; + tmix->voice = voice; + tmix->vol = T4D_DEFAULT_PCM_VOL; + tmix->pan = T4D_DEFAULT_PCM_PAN; + tmix->rvol = T4D_DEFAULT_PCM_RVOL; + tmix->cvol = T4D_DEFAULT_PCM_CVOL; + snd_trident_notify_pcm_change(trident->card, tmix, 1); + return 0; +} + +static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream) +{ + snd_trident_pcm_mixer_t *tmix; + + snd_assert(trident != NULL && substream != NULL, return -EINVAL); + tmix = &trident->pcm_mixer[substream->number]; + tmix->voice = NULL; + snd_trident_notify_pcm_change(trident->card, tmix, 0); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_mixer + + Description: This routine registers the 4DWave device for mixer support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device) +{ + ac97_t _ac97; + snd_card_t * card = trident->card; + snd_kcontrol_t *kctl; + snd_ctl_elem_value_t uctl; + int idx, err, retries = 2; + + memset(&uctl, 0, sizeof(uctl)); + + memset(&_ac97, 0, sizeof(_ac97)); + _ac97.write = snd_trident_codec_write; + _ac97.read = snd_trident_codec_read; + _ac97.private_data = trident; + trident->ac97_detect = 1; + + __again: + if ((err = snd_ac97_mixer(trident->card, &_ac97, &trident->ac97)) < 0) { + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + if ((err = snd_trident_sis_reset(trident)) < 0) + return err; + if (retries-- > 0) + goto __again; + return -EIO; + } + return err; + } + + /* secondary codec? */ + if (trident->device == TRIDENT_DEVICE_ID_SI7018 && + (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) { + _ac97.num = 1; + err = snd_ac97_mixer(trident->card, &_ac97, &trident->ac97_sec); + if (err < 0) + snd_printk("SI7018: the secondary codec - invalid access\n"); +#if 0 // only for my testing purpose --jk + { + ac97_t *mc97; + err = snd_ac97_modem(trident->card, &_ac97, &mc97); + if (err < 0) + snd_printk("snd_ac97_modem returned error %i\n", err); + } +#endif + } + + trident->ac97_detect = 0; + + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + } else { + outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + } + + for (idx = 0; idx < 32; idx++) { + snd_trident_pcm_mixer_t *tmix; + + tmix = &trident->pcm_mixer[idx]; + tmix->voice = NULL; + if ((kctl = tmix->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + } + if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0) + return err; + if (trident->ac97->ext_id & AC97_EI_SPDIF) + kctl->id.index++; + if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF)) + kctl->id.index++; + idx = kctl->id.index; + kctl->put(kctl, &uctl); + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0) + return err; + kctl->id.index = idx; + kctl->id.device = pcm_spdif_device; + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0) + return err; + kctl->id.index = idx; + kctl->id.device = pcm_spdif_device; + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0) + return err; + kctl->id.index = idx; + kctl->id.device = pcm_spdif_device; + trident->spdif_pcm_ctl = kctl; + } + + return 0; +} + +/* + * gameport interface + */ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + +typedef struct snd_trident_gameport { + struct gameport info; + trident_t *chip; +} trident_gameport_t; + +static unsigned char snd_trident_gameport_read(struct gameport *gameport) +{ + trident_gameport_t *gp = (trident_gameport_t *)gameport; + trident_t *chip; + snd_assert(gp, return 0); + chip = snd_magic_cast(trident_t, gp->chip, return 0); + return inb(TRID_REG(chip, GAMEPORT_LEGACY)); +} + +static void snd_trident_gameport_trigger(struct gameport *gameport) +{ + trident_gameport_t *gp = (trident_gameport_t *)gameport; + trident_t *chip; + snd_assert(gp, return); + chip = snd_magic_cast(trident_t, gp->chip, return); + outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY)); +} + +static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + trident_gameport_t *gp = (trident_gameport_t *)gameport; + trident_t *chip; + int i; + + snd_assert(gp, return 0); + chip = snd_magic_cast(trident_t, gp->chip, return 0); + + *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf; + + for (i = 0; i < 4; i++) { + axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2)); + if (axes[i] == 0xffff) axes[i] = -1; + } + + return 0; +} + +static int snd_trident_gameport_open(struct gameport *gameport, int mode) +{ + trident_gameport_t *gp = (trident_gameport_t *)gameport; + trident_t *chip; + snd_assert(gp, return -1); + chip = snd_magic_cast(trident_t, gp->chip, return -1); + + switch (mode) { + case GAMEPORT_MODE_COOKED: + outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR)); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1 + 20 * HZ / 1000); /* 20msec */ + return 0; + case GAMEPORT_MODE_RAW: + outb(0, TRID_REG(chip, GAMEPORT_GCR)); + return 0; + default: + return -1; + } +} + +void __devinit snd_trident_gameport(trident_t *chip) +{ + trident_gameport_t *gp; + gp = kmalloc(sizeof(*gp), GFP_KERNEL); + if (! gp) { + snd_printk("cannot allocate gameport area\n"); + return; + } + memset(gp, 0, sizeof(*gp)); + gp->chip = chip; + gp->info.fuzz = 64; + gp->info.read = snd_trident_gameport_read; + gp->info.trigger = snd_trident_gameport_trigger; + gp->info.cooked_read = snd_trident_gameport_cooked_read; + gp->info.open = snd_trident_gameport_open; + chip->gameport = gp; + + gameport_register_port(&gp->info); +} + +#else +void __devinit snd_trident_gameport(trident_t *chip) +{ +} +#endif /* CONFIG_GAMEPORT */ + +/* + * delay for 1 tick + */ +inline static void do_delay(trident_t *chip) +{ +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay((1000 + HZ - 1) / HZ); + return; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +} + +/* + * SiS reset routine + */ + +static int snd_trident_sis_reset(trident_t *trident) +{ + signed long end_time; + unsigned int i; + int r; + + r = trident->in_suspend ? 0 : 2; /* count of retries */ + __si7018_retry: + pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */ + udelay(100); + pci_write_config_byte(trident->pci, 0x46, 0x00); + udelay(100); + /* disable AC97 GPIO interrupt */ + outb(0x00, TRID_REG(trident, SI_AC97_GPIO)); + /* initialize serial interface, force cold reset */ + i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET; + outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + udelay(1000); + /* remove cold reset */ + i &= ~COLD_RESET; + outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + udelay(2000); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) + goto __si7018_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); + if (r-- > 0) { + end_time = jiffies + HZ; + do { + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + goto __si7018_retry; + } + __si7018_ok: + /* wait for the second codec */ + do { + if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0) + break; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + /* enable 64 channel mode */ + outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR)); + return 0; +} + +/* + * /proc interface + */ + +static void snd_trident_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + trident_t *trident = snd_magic_cast(trident_t, entry->private_data, return); + char *s; + + switch (trident->device) { + case TRIDENT_DEVICE_ID_SI7018: + s = "SiS 7018 Audio"; + break; + case TRIDENT_DEVICE_ID_DX: + s = "Trident 4DWave PCI DX"; + break; + case TRIDENT_DEVICE_ID_NX: + s = "Trident 4DWave PCI NX"; + break; + default: + s = "???"; + } + snd_iprintf(buffer, "%s\n\n", s); + snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); + snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); + if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) + snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); + if (trident->tlb.entries) { + snd_iprintf(buffer,"\nVirtual Memory\n"); + snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); + snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used); + snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); + } + } +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + snd_iprintf(buffer,"\nWavetable Synth\n"); + snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size); + snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size); + snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size)); +#endif +} + +static void __devinit snd_trident_proc_init(trident_t * trident) +{ + snd_info_entry_t *entry; + const char *s = "trident"; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + s = "sis7018"; + if (! snd_card_proc_new(trident->card, s, &entry)) + snd_info_set_text_ops(entry, trident, snd_trident_proc_read); +} + +static int snd_trident_dev_free(snd_device_t *device) +{ + trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); + return snd_trident_free(trident); +} + +/*--------------------------------------------------------------------------- + snd_trident_tlb_alloc + + Description: Allocate and set up the TLB page table on 4D NX. + Each entry has 4 bytes (physical PCI address). + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: 0 or negative error code + + ---------------------------------------------------------------------------*/ + +static int __devinit snd_trident_tlb_alloc(trident_t *trident) +{ + int i; + + /* TLB array must be aligned to 16kB !!! so we allocate + 32kB region and correct offset when necessary */ + + trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); + if (trident->tlb.buffer == NULL) { + snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); + return -ENOMEM; + } + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + /* allocate shadow TLB page table (virtual addresses) */ + trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); + if (trident->tlb.shadow_entries == NULL) { + snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n"); + return -ENOMEM; + } + /* allocate and setup silent page and initialise TLB entries */ + trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page == 0UL) { + snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); + return -ENOMEM; + } + memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { + trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + } + + /* use emu memory block manager code to manage tlb page allocation */ + trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); + if (trident->tlb.memhdr == NULL) + return -ENOMEM; + + trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); + return 0; +} + +/* + * initialize 4D DX chip + */ + +static void snd_trident_stop_all_voices(trident_t *trident) +{ + outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); + outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); + outl(0, TRID_REG(trident, T4D_AINTEN_A)); + outl(0, TRID_REG(trident, T4D_AINTEN_B)); +} + +static int snd_trident_4d_dx_init(trident_t *trident) +{ + struct pci_dev *pci = trident->pci; + signed long end_time; + + /* reset the legacy configuration and whole audio/wavetable block */ + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + pci_write_config_byte(pci, 0x46, 4); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* DAC on, disable SB IRQ and try to force ADC valid signal */ + trident->ac97_ctrl = 0x0000004a; + outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) + goto __dx_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "AC'97 codec ready error\n"); + return -EIO; + + __dx_ok: + snd_trident_stop_all_voices(trident); + + return 0; +} + +/* + * initialize 4D NX chip + */ +static int snd_trident_4d_nx_init(trident_t *trident) +{ + struct pci_dev *pci = trident->pci; + signed long end_time; + + /* reset the legacy configuration and whole audio/wavetable block */ + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + + pci_write_config_byte(pci, 0x46, 1); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) + goto __nx_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); + return -EIO; + + __nx_ok: + /* DAC on */ + trident->ac97_ctrl = 0x00000002; + outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* disable SB IRQ */ + outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + + snd_trident_stop_all_voices(trident); + + if (trident->tlb.entries != NULL) { + unsigned int i; + /* enable virtual addressing via TLB */ + i = trident->tlb.entries_dmaaddr; + i |= 0x00000001; + outl(i, TRID_REG(trident, NX_TLBC)); + } else { + outl(0, TRID_REG(trident, NX_TLBC)); + } + /* initialize S/PDIF */ + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + + return 0; +} + +/* + * initialize sis7018 chip + */ +static int snd_trident_sis_init(trident_t *trident) +{ + int err; + + if ((err = snd_trident_sis_reset(trident)) < 0) + return err; + + snd_trident_stop_all_voices(trident); + + /* initialize S/PDIF */ + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_create + + Description: This routine will create the device specific class for + the 4DWave card. It will also perform basic initialization. + + Paramters: card - which card to create + pci - interface to PCI bus resource info + dma1ptr - playback dma buffer + dma2ptr - capture dma buffer + irqptr - interrupt resource info + + Returns: 4DWave device class private data + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + trident_t ** rtrident) +{ + trident_t *trident; + int i, err; + snd_trident_voice_t *voice; + snd_trident_pcm_mixer_t *tmix; + static snd_device_ops_t ops = { + .dev_free = snd_trident_dev_free, + }; + + *rtrident = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 30 bits */ + if (!pci_dma_supported(pci, 0x3fffffff)) { + snd_printk("architecture does not support 30bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x3fffffff); + + trident = snd_magic_kcalloc(trident_t, 0, GFP_KERNEL); + if (trident == NULL) + return -ENOMEM; + trident->device = (pci->vendor << 16) | pci->device; + trident->card = card; + trident->pci = pci; + spin_lock_init(&trident->reg_lock); + spin_lock_init(&trident->event_lock); + spin_lock_init(&trident->voice_alloc); + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + trident->ChanPCM = pcm_streams; + if (max_wavetable_size < 0 ) + max_wavetable_size = 0; + trident->synth.max_size = max_wavetable_size * 1024; + trident->port = pci_resource_start(pci, 0); + trident->irq = -1; + + trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE); + pci_set_master(pci); + trident->port = pci_resource_start(pci, 0); + + if ((trident->res_port = request_region(trident->port, 0x100, "Trident Audio")) == NULL) { + snd_trident_free(trident); + snd_printk("unable to grab I/O region 0x%lx-0x%lx\n", trident->port, trident->port + 0x100 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) { + snd_trident_free(trident); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + trident->irq = pci->irq; + + /* allocate 16k-aligned TLB for NX cards */ + trident->tlb.entries = NULL; + trident->tlb.buffer = NULL; + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if ((err = snd_trident_tlb_alloc(trident)) < 0) { + snd_trident_free(trident); + return err; + } + } + + trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + + /* initialize chip */ + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + err = snd_trident_4d_dx_init(trident); + break; + case TRIDENT_DEVICE_ID_NX: + err = snd_trident_4d_nx_init(trident); + break; + case TRIDENT_DEVICE_ID_SI7018: + err = snd_trident_sis_init(trident); + break; + default: + snd_BUG(); + break; + } + if (err < 0) { + snd_trident_free(trident); + return err; + } + + if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) { + snd_trident_free(trident); + return err; + } + + /* initialise synth voices */ + for (i = 0; i < 64; i++) { + voice = &trident->synth.voices[i]; + voice->number = i; + voice->trident = trident; + } + /* initialize pcm mixer entries */ + for (i = 0; i < 32; i++) { + tmix = &trident->pcm_mixer[i]; + tmix->vol = T4D_DEFAULT_PCM_VOL; + tmix->pan = T4D_DEFAULT_PCM_PAN; + tmix->rvol = T4D_DEFAULT_PCM_RVOL; + tmix->cvol = T4D_DEFAULT_PCM_CVOL; + } + + snd_trident_enable_eso(trident); + +#ifdef CONFIG_PM + card->set_power_state = snd_trident_set_power_state; + card->power_state_private_data = trident; +#endif + + snd_trident_proc_init(trident); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) { + snd_trident_free(trident); + return err; + } + *rtrident = trident; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_free + + Description: This routine will free the device specific class for + q the 4DWave card. + + Paramters: trident - device specific private data for 4DWave card + + Returns: None. + + ---------------------------------------------------------------------------*/ + +int snd_trident_free(trident_t *trident) +{ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + if (trident->gameport) { + gameport_unregister_port(&trident->gameport->info); + kfree(trident->gameport); + } +#endif + snd_trident_disable_eso(trident); + // Disable S/PDIF out + if (trident->device == TRIDENT_DEVICE_ID_NX) + outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + } + if (trident->tlb.buffer) { + outl(0, TRID_REG(trident, NX_TLBC)); + if (trident->tlb.memhdr) + snd_util_memhdr_free(trident->tlb.memhdr); + if (trident->tlb.silent_page) + snd_free_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + if (trident->tlb.shadow_entries) + vfree(trident->tlb.shadow_entries); + snd_free_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, trident->tlb.buffer, trident->tlb.buffer_dmaaddr); + } + if (trident->irq >= 0) + free_irq(trident->irq, (void *)trident); + if (trident->res_port) { + release_resource(trident->res_port); + kfree_nocheck(trident->res_port); + } + snd_magic_kfree(trident); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_interrupt + + Description: ISR for Trident 4DWave device + + Paramters: trident - device specific private data for 4DWave card + + Problems: It seems that Trident chips generates interrupts more than + one time in special cases. The spurious interrupts are + detected via sample timer (T4D_STIMER) and computing + corresponding delta value. The limits are detected with + the method try & fail so it is possible that it won't + work on all computers. [jaroslav] + + Returns: None. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + trident_t *trident = snd_magic_cast(trident_t, dev_id, return); + unsigned int audio_int, chn_int, stimer, channel, mask, tmp; + int delta; + snd_trident_voice_t *voice; + + audio_int = inl(TRID_REG(trident, T4D_MISCINT)); + if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0) + return; + if (audio_int & ADDRESS_IRQ) { + // get interrupt status for all channels + spin_lock(&trident->reg_lock); + stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; + chn_int = inl(TRID_REG(trident, T4D_AINT_A)); + if (chn_int == 0) + goto __skip1; + outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */ + __skip1: + chn_int = inl(TRID_REG(trident, T4D_AINT_B)); + if (chn_int == 0) + goto __skip2; + for (channel = 63; channel >= 32; channel--) { + mask = 1 << (channel&0x1f); + if ((chn_int & mask) == 0) + continue; + voice = &trident->synth.voices[channel]; + if (!voice->pcm || voice->substream == NULL) { + outl(mask, TRID_REG(trident, T4D_STOP_B)); + continue; + } + delta = (int)stimer - (int)voice->stimer; + if (delta < 0) + delta = -delta; + if ((unsigned int)delta < voice->spurious_threshold) { + /* do some statistics here */ + trident->spurious_irq_count++; + if (trident->spurious_irq_max_delta < (unsigned int)delta) + trident->spurious_irq_max_delta = delta; + continue; + } + voice->stimer = stimer; + if (voice->isync) { + if (!voice->isync3) { + tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL)); + if (trident->bDMAStart & 0x40) + tmp >>= 1; + if (tmp > 0) + tmp = voice->isync_max - tmp; + } else { + tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; + } + if (tmp < voice->isync_mark) { + if (tmp > 0x10) + tmp = voice->isync_ESO - 7; + else + tmp = voice->isync_ESO + 2; + /* update ESO for IRQ voice to preserve sync */ + snd_trident_stop_voice(trident, voice->number); + snd_trident_write_eso_reg(trident, voice, tmp); + snd_trident_start_voice(trident, voice->number); + } + } else if (voice->isync2) { + voice->isync2 = 0; + /* write original ESO and update CSO for IRQ voice to preserve sync */ + snd_trident_stop_voice(trident, voice->number); + snd_trident_write_cso_reg(trident, voice, voice->isync_mark); + snd_trident_write_eso_reg(trident, voice, voice->ESO); + snd_trident_start_voice(trident, voice->number); + } +#if 0 + if (voice->extra) { + /* update CSO for extra voice to preserve sync */ + snd_trident_stop_voice(trident, voice->extra->number); + snd_trident_write_cso_reg(trident, voice->extra, 0); + snd_trident_start_voice(trident, voice->extra->number); + } +#endif + spin_unlock(&trident->reg_lock); + snd_pcm_period_elapsed(voice->substream); + spin_lock(&trident->reg_lock); + } + outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */ + __skip2: + spin_unlock(&trident->reg_lock); + } + if (audio_int & MPU401_IRQ) { + if (trident->rmidi) { + snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data, regs); + } else { + inb(TRID_REG(trident, T4D_MPUR0)); + } + } + // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT)); +} + +/*--------------------------------------------------------------------------- + snd_trident_attach_synthesizer, snd_trident_detach_synthesizer + + Description: Attach/detach synthesizer hooks + + Paramters: trident - device specific private data for 4DWave card + + Returns: None. + + ---------------------------------------------------------------------------*/ +int snd_trident_attach_synthesizer(trident_t *trident) +{ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT, + sizeof(trident_t*), &trident->seq_dev) >= 0) { + strcpy(trident->seq_dev->name, "4DWave"); + *(trident_t**)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident; + } +#endif + return 0; +} + +int snd_trident_detach_synthesizer(trident_t *trident) +{ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (trident->seq_dev) { + snd_device_free(trident->card, trident->seq_dev); + trident->seq_dev = NULL; + } +#endif + return 0; +} + +snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port) +{ + snd_trident_voice_t *pvoice; + unsigned long flags; + int idx; + + spin_lock_irqsave(&trident->voice_alloc, flags); + if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) { + idx = snd_trident_allocate_pcm_channel(trident); + if(idx < 0) { + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; + } + pvoice = &trident->synth.voices[idx]; + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->capture = 0; + pvoice->spdif = 0; + pvoice->memblk = NULL; + pvoice->substream = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return pvoice; + } + if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) { + idx = snd_trident_allocate_synth_channel(trident); + if(idx < 0) { + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; + } + pvoice = &trident->synth.voices[idx]; + pvoice->use = 1; + pvoice->synth = 1; + pvoice->client = client; + pvoice->port = port; + pvoice->memblk = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return pvoice; + } + if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) { + } + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; +} + +void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice) +{ + unsigned long flags; + void (*private_free)(snd_trident_voice_t *); + void *private_data; + + if (voice == NULL || !voice->use) + return; + snd_trident_clear_voices(trident, voice->number, voice->number); + spin_lock_irqsave(&trident->voice_alloc, flags); + private_free = voice->private_free; + private_data = voice->private_data; + voice->private_free = NULL; + voice->private_data = NULL; + if (voice->pcm) + snd_trident_free_pcm_channel(trident, voice->number); + if (voice->synth) + snd_trident_free_synth_channel(trident, voice->number); + voice->use = voice->pcm = voice->synth = voice->midi = 0; + voice->capture = voice->spdif = 0; + voice->sample_ops = NULL; + voice->substream = NULL; + voice->extra = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + if (private_free) + private_free(voice); +} + +void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max) +{ + unsigned int i, val, mask[2] = { 0, 0 }; + + snd_assert(v_min <= 63, return); + snd_assert(v_max <= 63, return); + for (i = v_min; i <= v_max; i++) + mask[i >> 5] |= 1 << (i & 0x1f); + if (mask[0]) { + outl(mask[0], TRID_REG(trident, T4D_STOP_A)); + val = inl(TRID_REG(trident, T4D_AINTEN_A)); + outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A)); + } + if (mask[1]) { + outl(mask[1], TRID_REG(trident, T4D_STOP_B)); + val = inl(TRID_REG(trident, T4D_AINTEN_B)); + outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B)); + } +} + +#ifdef CONFIG_PM + +void snd_trident_suspend(trident_t *trident) +{ + snd_card_t *card = trident->card; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + trident->in_suspend = 1; + snd_pcm_suspend_all(trident->pcm); + if (trident->foldback) + snd_pcm_suspend_all(trident->foldback); + if (trident->spdif) + snd_pcm_suspend_all(trident->spdif); + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + break; /* TODO */ + case TRIDENT_DEVICE_ID_SI7018: + break; + } + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +void snd_trident_resume(trident_t *trident) +{ + snd_card_t *card = trident->card; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + pci_enable_device(trident->pci); + pci_set_dma_mask(trident->pci, 0x3fffffff); /* to be sure */ + pci_set_master(trident->pci); /* to be sure */ + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + snd_trident_4d_dx_init(trident); + break; + case TRIDENT_DEVICE_ID_NX: + snd_trident_4d_nx_init(trident); + break; + case TRIDENT_DEVICE_ID_SI7018: + snd_trident_sis_init(trident); + break; + } + + snd_ac97_resume(trident->ac97); + + /* restore some registers */ + outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + + snd_trident_enable_eso(trident); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + trident->in_suspend = 0; +} + +static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state) +{ + trident_t *chip = snd_magic_cast(trident_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_trident_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_trident_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +EXPORT_SYMBOL(snd_trident_alloc_voice); +EXPORT_SYMBOL(snd_trident_free_voice); +EXPORT_SYMBOL(snd_trident_start_voice); +EXPORT_SYMBOL(snd_trident_stop_voice); +EXPORT_SYMBOL(snd_trident_write_voice_regs); +EXPORT_SYMBOL(snd_trident_clear_voices); +/* trident_memory.c symbols */ +EXPORT_SYMBOL(snd_trident_synth_alloc); +EXPORT_SYMBOL(snd_trident_synth_free); +EXPORT_SYMBOL(snd_trident_synth_bzero); +EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff -urN linux-2.4.21-rc1.orig/sound/pci/trident/trident_memory.c linux/sound/pci/trident/trident_memory.c --- linux-2.4.21-rc1.orig/sound/pci/trident/trident_memory.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/trident/trident_memory.c 2003-03-09 16:16:12.000000000 -0700 @@ -0,0 +1,494 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Takashi Iwai + * Copyright (c) by Scott McNab + * + * Trident 4DWave-NX memory page allocation (TLB area) + * Trident chip can handle only 16MByte of the memory at the same time. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +/* page arguments of these two macros are Trident page (4096 bytes), not like + * aligned pages in others + */ +#define __set_tlb_bus(trident,page,ptr,addr) \ + do { (trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); \ + (trident)->tlb.shadow_entries[page] = (ptr); } while (0) +#define __tlb_to_ptr(trident,page) \ + (void*)((trident)->tlb.shadow_entries[page]) +#define __tlb_to_addr(trident,page) \ + (dma_addr_t)le32_to_cpu((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1)) + +#if PAGE_SIZE == 4096 +/* page size == SNDRV_TRIDENT_PAGE_SIZE */ +#define ALIGN_PAGE_SIZE PAGE_SIZE /* minimum page size for allocation */ +#define MAX_ALIGN_PAGES SNDRV_TRIDENT_MAX_PAGES /* maxmium aligned pages */ +/* fill TLB entrie(s) corresponding to page with ptr */ +#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr) +/* fill TLB entrie(s) corresponding to page with silence pointer */ +#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr) +/* get aligned page from offset address */ +#define get_aligned_page(offset) ((offset) >> 12) +/* get offset address from aligned page */ +#define aligned_page_offset(page) ((page) << 12) +/* get buffer address from aligned page */ +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, page) +/* get PCI physical address from aligned page */ +#define page_to_addr(trident,page) __tlb_to_addr(trident, page) + +#elif PAGE_SIZE == 8192 +/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/ +#define ALIGN_PAGE_SIZE PAGE_SIZE +#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / 2) +#define get_aligned_page(offset) ((offset) >> 13) +#define aligned_page_offset(page) ((page) << 13) +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) << 1) +#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) << 1) + +/* fill TLB entries -- we need to fill two entries */ +static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr) +{ + page <<= 1; + __set_tlb_bus(trident, page, ptr, addr); + __set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE); +} +static inline void set_silent_tlb(trident_t *trident, int page) +{ + page <<= 1; + __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + __set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); +} + +#else +/* arbitrary size */ +#define UNIT_PAGES (PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE) +#define ALIGN_PAGE_SIZE (SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES) +#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES) +/* Note: if alignment doesn't match to the maximum size, the last few blocks + * become unusable. To use such blocks, you'll need to check the validity + * of accessing page in set_tlb_bus and set_silent_tlb. search_empty() + * should also check it, too. + */ +#define get_aligned_page(offset) ((offset) / ALIGN_PAGE_SIZE) +#define aligned_page_offset(page) ((page) * ALIGN_PAGE_SIZE) +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) * UNIT_PAGES) +#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) * UNIT_PAGES) + +/* fill TLB entries -- UNIT_PAGES entries must be filled */ +static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) { + __set_tlb_bus(trident, page, ptr, addr); + ptr += SNDRV_TRIDENT_PAGE_SIZE; + addr += SNDRV_TRIDENT_PAGE_SIZE; + } +} +static inline void set_silent_tlb(trident_t *trident, int page) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) + __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); +} + +#endif /* PAGE_SIZE */ + +/* calculate buffer pointer from offset address */ +inline static void *offset_ptr(trident_t *trident, int offset) +{ + char *ptr; + ptr = page_to_ptr(trident, get_aligned_page(offset)); + ptr += offset % ALIGN_PAGE_SIZE; + return (void*)ptr; +} + +/* first and last (aligned) pages of memory block */ +#define firstpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->first_page) +#define lastpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->last_page) + +/* + * search empty pages which may contain given size + */ +static snd_util_memblk_t * +search_empty(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk, *prev; + int page, psize; + struct list_head *p; + + psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1); + prev = NULL; + page = 0; + list_for_each(p, &hdr->block) { + blk = list_entry(p, snd_util_memblk_t, list); + if (page + psize <= firstpg(blk)) + goto __found_pages; + page = lastpg(blk) + 1; + } + if (page + psize > MAX_ALIGN_PAGES) + return NULL; + +__found_pages: + /* create a new memory block */ + blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev); + if (blk == NULL) + return NULL; + blk->offset = aligned_page_offset(page); /* set aligned offset */ + firstpg(blk) = page; + lastpg(blk) = page + psize - 1; + return blk; +} + + +/* + * check if the given pointer is valid for pages + */ +static int is_valid_page(unsigned long ptr) +{ + if (ptr & ~0x3fffffffUL) { + snd_printk("max memory size is 1GB!!\n"); + return 0; + } + if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) { + snd_printk("page is not aligned\n"); + return 0; + } + return 1; +} + +/* + * page allocation for DMA (Scatter-Gather version) + */ +static snd_util_memblk_t * +snd_trident_alloc_sg_pages(trident_t *trident, snd_pcm_substream_t *substream) +{ + snd_util_memhdr_t *hdr; + snd_util_memblk_t *blk; + snd_pcm_runtime_t *runtime = substream->runtime; + int idx, page; + struct snd_sg_buf *sgbuf = runtime->dma_private; + + snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG, return NULL); + snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); + hdr = trident->tlb.memhdr; + snd_assert(hdr != NULL, return NULL); + + + + down(&hdr->block_mutex); + blk = search_empty(hdr, runtime->dma_bytes); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) { + snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk)); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + + /* set TLB entries */ + idx = 0; + for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { + dma_addr_t addr = sgbuf->table[idx].addr; + unsigned long ptr = (unsigned long)sgbuf->table[idx].buf; + if (! is_valid_page(addr)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + set_tlb_bus(trident, page, ptr, addr); + } + up(&hdr->block_mutex); + return blk; +} + +/* + * page allocation for DMA (contiguous version) + */ +static snd_util_memblk_t * +snd_trident_alloc_cont_pages(trident_t *trident, snd_pcm_substream_t *substream) +{ + snd_util_memhdr_t *hdr; + snd_util_memblk_t *blk; + int page; + snd_pcm_runtime_t *runtime = substream->runtime; + dma_addr_t addr; + unsigned long ptr; + + snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI, return NULL); + snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); + hdr = trident->tlb.memhdr; + snd_assert(hdr != NULL, return NULL); + + down(&hdr->block_mutex); + blk = search_empty(hdr, runtime->dma_bytes); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + + /* set TLB entries */ + addr = runtime->dma_addr; + ptr = (unsigned long)runtime->dma_area; + for (page = firstpg(blk); page <= lastpg(blk); page++, + ptr += SNDRV_TRIDENT_PAGE_SIZE, addr += SNDRV_TRIDENT_PAGE_SIZE) { + if (! is_valid_page(addr)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + set_tlb_bus(trident, page, ptr, addr); + } + up(&hdr->block_mutex); + return blk; +} + +/* + * page allocation for DMA + */ +snd_util_memblk_t * +snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream) +{ + snd_assert(trident != NULL, return NULL); + snd_assert(substream != NULL, return NULL); + if (substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG) + return snd_trident_alloc_sg_pages(trident, substream); + else + return snd_trident_alloc_cont_pages(trident, substream); +} + + +/* + * release DMA buffer from page table + */ +int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk) +{ + snd_util_memhdr_t *hdr; + int page; + + snd_assert(trident != NULL, return -EINVAL); + snd_assert(blk != NULL, return -EINVAL); + + hdr = trident->tlb.memhdr; + down(&hdr->block_mutex); + /* reset TLB entries */ + for (page = firstpg(blk); page <= lastpg(blk); page++) + set_silent_tlb(trident, page); + /* free memory block */ + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + + +/*---------------------------------------------------------------- + * memory allocation using multiple pages (for synth) + *---------------------------------------------------------------- + * Unlike the DMA allocation above, non-contiguous pages are + * assigned to TLB. + *----------------------------------------------------------------*/ + +/* + */ +static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk); +static int synth_free_pages(trident_t *hw, snd_util_memblk_t *blk); + +/* + * allocate a synth sample area + */ +snd_util_memblk_t * +snd_trident_synth_alloc(trident_t *hw, unsigned int size) +{ + snd_util_memblk_t *blk; + snd_util_memhdr_t *hdr = hw->tlb.memhdr; + + down(&hdr->block_mutex); + blk = __snd_util_mem_alloc(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (synth_alloc_pages(hw, blk)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + up(&hdr->block_mutex); + return blk; +} + + +/* + * free a synth sample area + */ +int +snd_trident_synth_free(trident_t *hw, snd_util_memblk_t *blk) +{ + snd_util_memhdr_t *hdr = hw->tlb.memhdr; + + down(&hdr->block_mutex); + synth_free_pages(hw, blk); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + + +/* + * reset TLB entry and free kernel page + */ +static void clear_tlb(trident_t *trident, int page) +{ + void *ptr = page_to_ptr(trident, page); + dma_addr_t addr = page_to_addr(trident, page); + set_silent_tlb(trident, page); + if (ptr) + snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); +} + +/* check new allocation range */ +static void get_single_page_range(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk, int *first_page_ret, int *last_page_ret) +{ + struct list_head *p; + snd_util_memblk_t *q; + int first_page, last_page; + first_page = firstpg(blk); + if ((p = blk->list.prev) != &hdr->block) { + q = list_entry(p, snd_util_memblk_t, list); + if (lastpg(q) == first_page) + first_page++; /* first page was already allocated */ + } + last_page = lastpg(blk); + if ((p = blk->list.next) != &hdr->block) { + q = list_entry(p, snd_util_memblk_t, list); + if (firstpg(q) == last_page) + last_page--; /* last page was already allocated */ + } + *first_page_ret = first_page; + *last_page_ret = last_page; +} + +/* + * allocate kernel pages and assign them to TLB + */ +static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) +{ + int page, first_page, last_page; + void *ptr; + dma_addr_t addr; + + firstpg(blk) = get_aligned_page(blk->offset); + lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1); + get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page); + + /* allocate a kernel page for each Trident page - + * fortunately Trident page size and kernel PAGE_SIZE is identical! + */ + for (page = first_page; page <= last_page; page++) { + ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); + if (ptr == NULL) + goto __fail; + if (! is_valid_page(addr)) { + snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); + goto __fail; + } + set_tlb_bus(hw, page, (unsigned long)ptr, addr); + } + return 0; + +__fail: + /* release allocated pages */ + last_page = page - 1; + for (page = first_page; page <= last_page; page++) + clear_tlb(hw, page); + + return -ENOMEM; +} + +/* + * free pages + */ +static int synth_free_pages(trident_t *trident, snd_util_memblk_t *blk) +{ + int page, first_page, last_page; + + get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page); + for (page = first_page; page <= last_page; page++) + clear_tlb(trident, page); + + return 0; +} + +/* + * bzero(blk + offset, size) + */ +int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size) +{ + int page, nextofs, end_offset, temp, temp1; + + offset += blk->offset; + end_offset = offset + size; + page = get_aligned_page(offset) + 1; + do { + nextofs = aligned_page_offset(page); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + memset(offset_ptr(trident, offset), 0, temp); + offset = nextofs; + page++; + } while (offset < end_offset); + return 0; +} + +/* + * copy_from_user(blk + offset, data, size) + */ +int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size) +{ + int page, nextofs, end_offset, temp, temp1; + + offset += blk->offset; + end_offset = offset + size; + page = get_aligned_page(offset) + 1; + do { + nextofs = aligned_page_offset(page); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + if (copy_from_user(offset_ptr(trident, offset), data, temp)) + return -EFAULT; + offset = nextofs; + data += temp; + page++; + } while (offset < end_offset); + return 0; +} + diff -urN linux-2.4.21-rc1.orig/sound/pci/trident/trident_synth.c linux/sound/pci/trident/trident_synth.c --- linux-2.4.21-rc1.orig/sound/pci/trident/trident_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/trident/trident_synth.c 2003-02-28 08:08:25.000000000 -0700 @@ -0,0 +1,1028 @@ +/* + * Routines for Trident 4DWave NX/DX soundcards - Synthesizer + * Copyright (c) by Scott McNab + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Scott McNab "); +MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer"); +MODULE_LICENSE("GPL"); + +/* linear to log pan conversion table (4.2 channel attenuation format) */ +static unsigned int pan_table[63] = { + 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, + 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, + 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, + 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, + 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, + 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, + 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, + 1588, 1543, 1499, 1456, 1415, 1375, 1336 +}; + +#define LOG_TABLE_SIZE 386 + +/* Linear half-attenuation to log conversion table in the format: + * {linear volume, logarithmic attenuation equivalent}, ... + * + * Provides conversion from a linear half-volume value in the range + * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB. + * Halving the linear volume is equivalent to an additional 6dB of + * logarithmic attenuation. The algorithm used in log_from_linear() + * therefore uses this table as follows: + * + * - loop and for every time the volume is less than half the maximum + * volume (16384), add another 6dB and halve the maximum value used + * for this comparison. + * - when the volume is greater than half the maximum volume, take + * the difference of the volume to half volume (in the range [0,8192]) + * and look up the log_table[] to find the nearest entry. + * - take the logarithic component of this entry and add it to the + * resulting attenuation. + * + * Thus this routine provides a linear->log conversion for a range of + * [0,16384] using only 386 table entries + * + * Note: although this table stores log attenuation in 8.8 format, values + * were only calculated for 6 bits fractional precision, since that is + * the most precision offered by the trident hardware. + */ + +static unsigned short log_table[LOG_TABLE_SIZE*2] = +{ + 4, 0x0604, 19, 0x0600, 34, 0x05fc, + 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, + 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, + 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, + 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, + 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, + 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, + 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, + 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, + 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, + 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, + 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, + 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, + 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, + 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, + 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, + 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, + 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, + 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, + 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, + 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, + 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, + 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, + 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, + 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, + 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, + 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, + 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, + 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, + 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, + 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, + 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, + 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, + 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, + 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, + 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, + 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, + 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, + 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, + 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, + 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, + 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, + 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, + 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, + 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, + 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, + 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, + 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, + 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, + 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, + 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, + 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, + 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, + 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, + 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, + 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, + 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, + 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, + 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, + 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, + 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, + 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, + 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, + 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, + 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, + 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, + 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, + 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, + 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, + 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, + 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, + 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, + 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, + 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, + 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, + 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, + 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, + 8133, 0x0008, 8162, 0x0004, 8192, 0x0000 +}; + +static unsigned short lookup_volume_table( unsigned short value ) +{ + /* This code is an optimised version of: + * int i = 0; + * while( volume_table[i*2] < value ) + * i++; + * return volume_table[i*2+1]; + */ + unsigned short *ptr = log_table; + while( *ptr < value ) + ptr += 2; + return *(ptr+1); +} + +/* this function calculates a 8.8 fixed point logarithmic attenuation + * value from a linear volume value in the range 0 to 16384 */ +static unsigned short log_from_linear( unsigned short value ) +{ + if (value >= 16384) + return 0x0000; + if (value) { + unsigned short result = 0; + int v, c; + for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) { + if( value >= v ) { + result += lookup_volume_table( (value - v) << c ); + return result; + } + result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */ + } + } + return 0xffff; +} + +/* + * Sample handling operations + */ + +static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position); +static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode); +static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq); +static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume); +static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop); +static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position); +static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data); + +static snd_trident_sample_ops_t sample_ops = +{ + sample_start, + sample_stop, + sample_freq, + sample_volume, + sample_loop, + sample_pos, + sample_private1 +}; + +static void snd_trident_simple_init(snd_trident_voice_t * voice) +{ + //voice->handler_wave = interrupt_wave; + //voice->handler_volume = interrupt_volume; + //voice->handler_effect = interrupt_effect; + //voice->volume_change = NULL; + voice->sample_ops = &sample_ops; +} + +static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position) +{ + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned long flags; + unsigned int loop_start, loop_end, sample_start, sample_end, start_offset; + unsigned int value; + unsigned int shift = 0; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + spin_lock_irqsave(&trident->reg_lock, flags); + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + voice->GVSel = 1; /* route to Wave volume */ + + voice->CTRL = 0; + voice->Alpha = 0; + voice->FMS = 0; + + loop_start = simple->loop_start >> 4; + loop_end = simple->loop_end >> 4; + sample_start = (simple->start + position) >> 4; + if( sample_start >= simple->size ) + sample_start = simple->start >> 4; + sample_end = simple->size; + start_offset = position >> 4; + + if (simple->format & SIMPLE_WAVE_16BIT) { + voice->CTRL |= 8; + shift++; + } + if (simple->format & SIMPLE_WAVE_STEREO) { + voice->CTRL |= 4; + shift++; + } + if (!(simple->format & SIMPLE_WAVE_UNSIGNED)) + voice->CTRL |= 2; + + voice->LBA = simple->address.memory; + + if (simple->format & SIMPLE_WAVE_LOOP) { + voice->CTRL |= 1; + voice->LBA += loop_start << shift; + if( start_offset >= loop_start ) { + voice->CSO = start_offset - loop_start; + voice->negCSO = 0; + } else { + voice->CSO = loop_start - start_offset; + voice->negCSO = 1; + } + voice->ESO = loop_end - loop_start - 1; + } else { + voice->LBA += start_offset << shift; + voice->CSO = sample_start; + voice->ESO = sample_end - 1; + voice->negCSO = 0; + } + + if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) { + snd_trident_stop_voice(trident, voice->number); + voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + } + + /* set CSO sign */ + value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); + if( voice->negCSO ) { + value |= 1 << (voice->number&31); + } else { + value &= ~(1 << (voice->number&31)); + } + outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); + + voice->Attribute = 0; + snd_trident_write_voice_regs(trident, voice); + snd_trident_start_voice(trident, voice->number); + voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING; + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode) +{ + unsigned long flags; + + if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING)) + return; + + switch (mode) { + default: + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_stop_voice(trident, voice->number); + voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + spin_unlock_irqrestore(&trident->reg_lock, flags); + break; + case SAMPLE_STOP_LOOP: /* disable loop only */ + voice->CTRL &= ~1; + spin_lock_irqsave(&trident->reg_lock, flags); + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC); + spin_unlock_irqrestore(&trident->reg_lock, flags); + break; + } +} + +static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq) +{ + unsigned long flags; + freq >>= 4; + + spin_lock_irqsave(&trident->reg_lock, flags); + if (freq == 44100) + voice->Delta = 0xeb3; + else if (freq == 8000) + voice->Delta = 0x2ab; + else if (freq == 48000) + voice->Delta = 0x1000; + else + voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff; + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); + outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3)); + } else { + outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA)); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume) +{ + unsigned long flags; + unsigned short value; + + spin_lock_irqsave(&trident->reg_lock, flags); + voice->GVSel = 0; /* use global music volume */ + voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */ + if (volume->volume >= 0) { + volume->volume &= 0x3fff; + /* linear volume -> logarithmic attenuation conversion + * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits) + * Vol register used when additional attenuation is required */ + voice->RVol = 0; + voice->CVol = 0; + value = log_from_linear( volume->volume ); + voice->Vol = 0; + voice->EC = (value & 0x3fff) >> 2; + if (value > 0x3fff) { + voice->EC |= 0xfc0; + if (value < 0x5f00 ) + voice->Vol = ((value >> 8) - 0x3f) << 5; + else { + voice->Vol = 0x3ff; + voice->EC = 0xfff; + } + } + } + if (volume->lr >= 0) { + volume->lr &= 0x3fff; + /* approximate linear pan by attenuating channels */ + if (volume->lr >= 0x2000) { /* attenuate left (pan right) */ + value = 0x3fff - volume->lr; + for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) + if (value >= pan_table[voice->Pan] ) + break; + } else { /* attenuate right (pan left) */ + for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) + if ((unsigned int)volume->lr >= pan_table[voice->Pan] ) + break; + voice->Pan |= 0x40; + } + } + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) | + ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) | + (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f); + outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop) +{ + unsigned long flags; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned int loop_start, loop_end; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + loop_start = loop->start >> 4; + loop_end = loop->end >> 4; + + spin_lock_irqsave(&trident->reg_lock, flags); + + voice->LBA = simple->address.memory + loop_start; + voice->CSO = 0; + voice->ESO = loop_end - loop_start - 1; + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2)); + outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2)); + outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO)); + outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + } else { + outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2)); + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position) +{ + unsigned long flags; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned int value; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + spin_lock_irqsave(&trident->reg_lock, flags); + + if (simple->format & SIMPLE_WAVE_LOOP) { + if( position >= simple->loop_start ) { + voice->CSO = (position - simple->loop_start) >> 4; + voice->negCSO = 0; + } else { + voice->CSO = (simple->loop_start - position) >> 4; + voice->negCSO = 1; + } + } else { + voice->CSO = position >> 4; + voice->negCSO = 0; + } + + /* set CSO sign */ + value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); + if( voice->negCSO ) { + value |= 1 << (voice->number&31); + } else { + value &= ~(1 << (voice->number&31)); + } + outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); + + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); + } else { + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data) +{ +} + +/* + * Memory management / sample loading + */ + +static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr, + char *data, long len, int atomic) +{ + trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + unsigned char *block = NULL; + int size = instr->size; + int shift = 0; + + if (instr->format & SIMPLE_WAVE_BACKWARD || + instr->format & SIMPLE_WAVE_BIDIR || + instr->format & SIMPLE_WAVE_ULAW) + return -EINVAL; /* not supported */ + + if (instr->format & SIMPLE_WAVE_16BIT) + shift++; + if (instr->format & SIMPLE_WAVE_STEREO) + shift++; + size <<= shift; + + if (trident->synth.current_size + size > trident->synth.max_size) + return -ENOMEM; + + if (verify_area(VERIFY_READ, data, size)) + return -EFAULT; + + if (trident->tlb.entries) { + snd_util_memblk_t *memblk; + memblk = snd_trident_synth_alloc(trident,size); + if (memblk == NULL) + return -ENOMEM; + if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { + snd_trident_synth_free(trident, memblk); + return -EFAULT; + } + instr->address.ptr = (unsigned char*)memblk; + instr->address.memory = memblk->offset; + } else { + dma_addr_t addr; + block = (unsigned char *) snd_malloc_pci_pages(trident->pci, size, &addr); + if (block == NULL) + return -ENOMEM; + + if (copy_from_user(block, data, size)) { + snd_free_pci_pages(trident->pci, size, block, addr); + return -EFAULT; + } + instr->address.ptr = block; + instr->address.memory = addr; + } + + trident->synth.current_size += size; + return 0; +} + +static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr, + char *data, long len, int atomic) +{ + //trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + int size = instr->size; + int shift = 0; + + if (instr->format & SIMPLE_WAVE_16BIT) + shift++; + if (instr->format & SIMPLE_WAVE_STEREO) + shift++; + size <<= shift; + + if (verify_area(VERIFY_WRITE, data, size)) + return -EFAULT; + + /* FIXME: not implemented yet */ + + return -EBUSY; +} + +static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr, + int atomic) +{ + trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + int size = instr->size; + + if (trident->tlb.entries) { + snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr; + if (memblk) + snd_trident_synth_free(trident, memblk); + else + return -EFAULT; + } else { + kfree(instr->address.ptr); + } + + if (instr->format & SIMPLE_WAVE_16BIT) + size <<= 1; + if (instr->format & SIMPLE_WAVE_STEREO) + size <<= 1; + + trident->synth.current_size -= size; + if (trident->synth.current_size < 0) /* shouldn't need this check... */ + trident->synth.current_size = 0; + + return 0; +} + +static void select_instrument(trident_t * trident, snd_trident_voice_t * v) +{ + snd_seq_kinstr_t *instr; + instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1); + if (instr != NULL) { + if (instr->ops) { + if (instr->ops->instr_type == snd_seq_simple_id) + snd_trident_simple_init(v); + } + snd_seq_instr_free_use(trident->synth.ilist, instr); + } +} + +/* + + */ + +static void event_sample(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.std = ev->data.sample.param.sample.std; + if (v->instr.std & 0xff000000) { /* private instrument */ + v->instr.std &= 0x00ffffff; + v->instr.std |= (unsigned int)ev->source.client << 24; + } + v->instr.bank = ev->data.sample.param.sample.bank; + v->instr.prg = ev->data.sample.param.sample.prg; + select_instrument(p->trident, v); +} + +static void event_cluster(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.cluster = ev->data.sample.param.cluster.cluster; + select_instrument(p->trident, v); +} + +static void event_start(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_start) + v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position); +} + +static void event_stop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode); +} + +static void event_freq(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_freq) + v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency); +} + +static void event_volume(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_volume) + v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume); +} + +static void event_loop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_loop) + v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop); +} + +static void event_position(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_pos) + v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position); +} + +static void event_private1(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_private1) + v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8); +} + +typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v); + +static trident_sample_event_handler_t *trident_sample_event_handlers[9] = +{ + event_sample, + event_cluster, + event_start, + event_stop, + event_freq, + event_volume, + event_loop, + event_position, + event_private1 +}; + +static void snd_trident_sample_event(snd_seq_event_t * ev, snd_trident_port_t * p) +{ + int idx, voice; + trident_t *trident = p->trident; + snd_trident_voice_t *v; + unsigned long flags; + + idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; + if (idx < 0 || idx > 8) + return; + for (voice = 0; voice < 64; voice++) { + v = &trident->synth.voices[voice]; + if (v->use && v->client == ev->source.client && + v->port == ev->source.port && + v->index == ev->data.sample.channel) { + spin_lock_irqsave(&trident->event_lock, flags); + trident_sample_event_handlers[idx] (ev, p, v); + spin_unlock_irqrestore(&trident->event_lock, flags); + return; + } + } +} + +/* + + */ + +static void snd_trident_synth_free_voices(trident_t * trident, int client, int port) +{ + int idx; + snd_trident_voice_t *voice; + + for (idx = 0; idx < 32; idx++) { + voice = &trident->synth.voices[idx]; + if (voice->use && voice->client == client && voice->port == port) + snd_trident_free_voice(trident, voice); + } +} + +static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info) +{ + snd_trident_port_t *port = (snd_trident_port_t *) private_data; + trident_t *trident = port->trident; + snd_trident_voice_t *voice; + unsigned int idx; + unsigned long flags; + + if (info->voices > 32) + return -EINVAL; + spin_lock_irqsave(&trident->reg_lock, flags); + for (idx = 0; idx < info->voices; idx++) { + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return -EBUSY; + } + voice->index = idx; + voice->Vol = 0x3ff; + voice->EC = 0x0fff; + } +#if 0 + for (idx = 0; idx < info->midi_voices; idx++) { + port->midi_has_voices = 1; + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return -EBUSY; + } + voice->Vol = 0x3ff; + voice->EC = 0x0fff; + } +#endif + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) +{ + snd_trident_port_t *port = (snd_trident_port_t *) private_data; + trident_t *trident = port->trident; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/* + + */ + +static void snd_trident_synth_free_private_instruments(snd_trident_port_t * p, int client) +{ + snd_seq_instr_header_t ifree; + + memset(&ifree, 0, sizeof(ifree)); + ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; + snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0); +} + +int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + snd_trident_port_t *p = (snd_trident_port_t *) private_data; + + if (p == NULL) + return -EINVAL; + if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && + ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { + snd_trident_sample_event(ev, p); + return 0; + } + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && + ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { + if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { + snd_trident_synth_free_private_instruments(p, ev->data.addr.client); + return 0; + } + } + if (direct) { + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { + snd_seq_instr_event(&p->trident->synth.simple_ops.kops, + p->trident->synth.ilist, ev, + p->trident->synth.seq_client, atomic, hop); + return 0; + } + } + return 0; +} + +static void snd_trident_synth_instr_notify(void *private_data, + snd_seq_kinstr_t * instr, + int what) +{ + int idx; + trident_t *trident = snd_magic_cast(trident_t, private_data, return); + snd_trident_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&trident->event_lock, flags); + for (idx = 0; idx < 64; idx++) { + pvoice = &trident->synth.voices[idx]; + if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { + if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { + pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY); + } else { + snd_trident_stop_voice(trident, pvoice->number); + pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + } + } + } + spin_unlock_irqrestore(&trident->event_lock, flags); +} + +/* + + */ + +static void snd_trident_synth_free_port(void *private_data) +{ + snd_trident_port_t *p = (snd_trident_port_t *) private_data; + + if (p) + snd_midi_channel_free_set(p->chset); +} + +static int snd_trident_synth_create_port(trident_t * trident, int idx) +{ + snd_trident_port_t *p; + snd_seq_port_callback_t callbacks; + char name[32]; + char *str; + int result; + + p = &trident->synth.seq_ports[idx]; + p->chset = snd_midi_channel_alloc_set(16); + if (p->chset == NULL) + return -ENOMEM; + p->chset->private_data = p; + p->trident = trident; + p->client = trident->synth.seq_client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_trident_synth_use; + callbacks.unuse = snd_trident_synth_unuse; + callbacks.event_input = snd_trident_synth_event_input; + callbacks.private_free = snd_trident_synth_free_port; + callbacks.private_data = p; + + str = "???"; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; + case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; + case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; + } + sprintf(name, "%s port %i", str, idx); + p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client, + &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_MIDI_GS | + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_SYNTH, + 16, 0, + name); + if (p->chset->port < 0) { + result = p->chset->port; + snd_trident_synth_free_port(p); + return result; + } + p->port = p->chset->port; + return 0; +} + +/* + + */ + +static int snd_trident_synth_new_device(snd_seq_device_t *dev) +{ + trident_t *trident; + int client, i; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + snd_seq_port_subscribe_t sub; + snd_simple_ops_t *simpleops; + char *str; + + trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (trident == NULL) + return -EINVAL; + + trident->synth.seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = trident; + callbacks.allow_output = callbacks.allow_input = 1; + client = trident->synth.seq_client = + snd_seq_create_kernel_client(trident->card, 1, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + str = "???"; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; + case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; + case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; + } + sprintf(cinfo.name, str); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + for (i = 0; i < 4; i++) + snd_trident_synth_create_port(trident, i); + + trident->synth.ilist = snd_seq_instr_list_new(); + if (trident->synth.ilist == NULL) { + snd_seq_delete_kernel_client(client); + trident->synth.seq_client = -1; + return -ENOMEM; + } + trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + + simpleops = &trident->synth.simple_ops; + snd_seq_simple_init(simpleops, trident, NULL); + simpleops->put_sample = snd_trident_simple_put_sample; + simpleops->get_sample = snd_trident_simple_get_sample; + simpleops->remove_sample = snd_trident_simple_remove_sample; + simpleops->notify = snd_trident_synth_instr_notify; + + memset(&sub, 0, sizeof(sub)); + sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + sub.dest.client = client; + sub.dest.port = 0; + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); + + return 0; +} + +static int snd_trident_synth_delete_device(snd_seq_device_t *dev) +{ + trident_t *trident; + + trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (trident == NULL) + return -EINVAL; + + if (trident->synth.seq_client >= 0) { + snd_seq_delete_kernel_client(trident->synth.seq_client); + trident->synth.seq_client = -1; + } + if (trident->synth.ilist) + snd_seq_instr_list_free(&trident->synth.ilist); + return 0; +} + +static int __init alsa_trident_synth_init(void) +{ + static snd_seq_dev_ops_t ops = + { + snd_trident_synth_new_device, + snd_trident_synth_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops, + sizeof(trident_t*)); +} + +static void __exit alsa_trident_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT); +} + +module_init(alsa_trident_synth_init) +module_exit(alsa_trident_synth_exit) diff -urN linux-2.4.21-rc1.orig/sound/pci/via82xx.c linux/sound/pci/via82xx.c --- linux-2.4.21-rc1.orig/sound/pci/via82xx.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/via82xx.c 2003-03-03 09:57:59.000000000 -0700 @@ -0,0 +1,1980 @@ +/* + * ALSA driver for VIA VT82xx (South Bridge) + * + * VT82C686A/B/C, VT8233A/C, VT8235 + * + * Copyright (c) 2000 Jaroslav Kysela + * Tjeerd.Mulder + * 2002 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Changes: + * + * Dec. 19, 2002 Takashi Iwai + * - use the DSX channels for the first pcm playback. + * (on VIA8233, 8233C and 8235 only) + * this will allow you play simultaneously up to 4 streams. + * multi-channel playback is assigned to the second device + * on these chips. + * - support the secondary capture (on VIA8233/C,8235) + * - SPDIF support + * the DSX3 channel can be used for SPDIF output. + * on VIA8233A, this channel is assigned to the second pcm + * playback. + * the card config of alsa-lib will assign the correct + * device for applications. + * - clean up the code, separate low-level initialization + * routines for each chipset. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#if 0 +#define POINTER_DEBUG +#endif + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("VIA VT82xx audio"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable audio part of VIA 82xx bridge."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); +MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:48000"); + + +/* pci ids */ +#ifndef PCI_DEVICE_ID_VIA_82C686_5 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#endif +#ifndef PCI_DEVICE_ID_VIA_8233_5 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#endif + +/* revision numbers for via686 */ +#define VIA_REV_686_A 0x10 +#define VIA_REV_686_B 0x11 +#define VIA_REV_686_C 0x12 +#define VIA_REV_686_D 0x13 +#define VIA_REV_686_E 0x14 +#define VIA_REV_686_H 0x20 + +/* revision numbers for via8233 */ +#define VIA_REV_PRE_8233 0x10 /* not in market */ +#define VIA_REV_8233C 0x20 /* 2 rec, 4 pb, 1 multi-pb */ +#define VIA_REV_8233 0x30 /* 2 rec, 4 pb, 1 multi-pb, spdif */ +#define VIA_REV_8233A 0x40 /* 1 rec, 1 multi-pb, spdf */ +#define VIA_REV_8235 0x50 /* 2 rec, 4 pb, 1 multi-pb, spdif */ + +/* + * Direct registers + */ + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) +#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x) + +/* common offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ +#define VIA_REG_STAT_PAUSED 0x40 /* RO */ +#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ +#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ +#define VIA_REG_STAT_EOL 0x02 /* RWC */ +#define VIA_REG_STAT_FLAG 0x01 /* RWC */ +#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_CTRL_START 0x80 /* WO */ +#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */ +#define VIA_REG_CTRL_AUTOSTART 0x20 +#define VIA_REG_CTRL_PAUSE 0x08 /* RW */ +#define VIA_REG_CTRL_INT_STOP 0x04 +#define VIA_REG_CTRL_INT_EOL 0x02 +#define VIA_REG_CTRL_INT_FLAG 0x01 +#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */ +#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART) +#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type (686 only) */ +#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */ +#define VIA_REG_TYPE_16BIT 0x20 /* RW */ +#define VIA_REG_TYPE_STEREO 0x10 /* RW */ +#define VIA_REG_TYPE_INT_LLINE 0x00 +#define VIA_REG_TYPE_INT_LSAMPLE 0x04 +#define VIA_REG_TYPE_INT_LESSONE 0x08 +#define VIA_REG_TYPE_INT_MASK 0x0c +#define VIA_REG_TYPE_INT_EOL 0x02 +#define VIA_REG_TYPE_INT_FLAG 0x01 +#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_OFFSET_STOP_IDX 0x08 /* dword - stop index, channel type, sample rate */ +#define VIA8233_REG_TYPE_16BIT 0x00200000 /* RW */ +#define VIA8233_REG_TYPE_STEREO 0x00100000 /* RW */ +#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ +#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index (for via8233 only) */ + +#define DEFINE_VIA_REGSET(name,val) \ +enum {\ + VIA_REG_##name##_STATUS = (val),\ + VIA_REG_##name##_CONTROL = (val) + 0x01,\ + VIA_REG_##name##_TYPE = (val) + 0x02,\ + VIA_REG_##name##_TABLE_PTR = (val) + 0x04,\ + VIA_REG_##name##_CURR_PTR = (val) + 0x04,\ + VIA_REG_##name##_STOP_IDX = (val) + 0x08,\ + VIA_REG_##name##_CURR_COUNT = (val) + 0x0c,\ +} + +/* playback block */ +DEFINE_VIA_REGSET(PLAYBACK, 0x00); +DEFINE_VIA_REGSET(CAPTURE, 0x10); +DEFINE_VIA_REGSET(FM, 0x20); + +/* AC'97 */ +#define VIA_REG_AC97 0x80 /* dword */ +#define VIA_REG_AC97_CODEC_ID_MASK (3<<30) +#define VIA_REG_AC97_CODEC_ID_SHIFT 30 +#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00 +#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01 +#define VIA_REG_AC97_SECONDARY_VALID (1<<27) +#define VIA_REG_AC97_PRIMARY_VALID (1<<25) +#define VIA_REG_AC97_BUSY (1<<24) +#define VIA_REG_AC97_READ (1<<23) +#define VIA_REG_AC97_CMD_SHIFT 16 +#define VIA_REG_AC97_CMD_MASK 0x7e +#define VIA_REG_AC97_DATA_SHIFT 0 +#define VIA_REG_AC97_DATA_MASK 0xffff + +#define VIA_REG_SGD_SHADOW 0x84 /* dword */ +/* via686 */ +#define VIA_REG_SGD_STAT_PB_FLAG (1<<0) +#define VIA_REG_SGD_STAT_CP_FLAG (1<<1) +#define VIA_REG_SGD_STAT_FM_FLAG (1<<2) +#define VIA_REG_SGD_STAT_PB_EOL (1<<4) +#define VIA_REG_SGD_STAT_CP_EOL (1<<5) +#define VIA_REG_SGD_STAT_FM_EOL (1<<6) +#define VIA_REG_SGD_STAT_PB_STOP (1<<8) +#define VIA_REG_SGD_STAT_CP_STOP (1<<9) +#define VIA_REG_SGD_STAT_FM_STOP (1<<10) +#define VIA_REG_SGD_STAT_PB_ACTIVE (1<<12) +#define VIA_REG_SGD_STAT_CP_ACTIVE (1<<13) +#define VIA_REG_SGD_STAT_FM_ACTIVE (1<<14) +/* via8233 */ +#define VIA8233_REG_SGD_STAT_FLAG (1<<0) +#define VIA8233_REG_SGD_STAT_EOL (1<<1) +#define VIA8233_REG_SGD_STAT_STOP (1<<2) +#define VIA8233_REG_SGD_STAT_ACTIVE (1<<3) +#define VIA8233_REG_SGD_SDX0_SHIFT 0 +#define VIA8233_REG_SGD_SDX1_SHIFT 4 +#define VIA8233_REG_SGD_SDX2_SHIFT 8 +#define VIA8233_REG_SGD_SDX3_SHIFT 12 +#define VIA8233_REG_SGD_MCHAN_SHIFT 16 +#define VIA8233_REG_SGD_REC0_SHIFT 24 +#define VIA8233_REG_SGD_REC1_SHIFT 28 + +#define VIA_REG_GPI_STATUS 0x88 +#define VIA_REG_GPI_INTR 0x8c + +/* multi-channel and capture registers for via8233 */ +DEFINE_VIA_REGSET(MULTPLAY, 0x40); +DEFINE_VIA_REGSET(CAPTURE_8233, 0x60); + +/* via8233-specific registers */ +#define VIA_REG_OFS_PLAYBACK_VOLUME_L 0x02 /* byte */ +#define VIA_REG_OFS_PLAYBACK_VOLUME_R 0x03 /* byte */ +#define VIA_REG_OFS_MULTPLAY_FORMAT 0x02 /* byte - format and channels */ +#define VIA_REG_MULTPLAY_FMT_8BIT 0x00 +#define VIA_REG_MULTPLAY_FMT_16BIT 0x80 +#define VIA_REG_MULTPLAY_FMT_CH_MASK 0x70 /* # channels << 4 (valid = 1,2,4,6) */ +#define VIA_REG_OFS_CAPTURE_FIFO 0x62 /* byte - bit 6 = fifo enable */ +#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40 + +#define VIA_REG_CAPTURE_CHANNEL 0x63 /* byte - input select */ +#define VIA_REG_CAPTURE_CHANNEL_MIC 0x4 +#define VIA_REG_CAPTURE_CHANNEL_LINE 0 +#define VIA_REG_CAPTURE_SELECT_CODEC 0x03 /* recording source codec (0 = primary) */ + +#define VIA_TBL_BIT_FLAG 0x40000000 +#define VIA_TBL_BIT_EOL 0x80000000 + +/* pci space */ +#define VIA_ACLINK_STAT 0x40 +#define VIA_ACLINK_C11_READY 0x20 +#define VIA_ACLINK_C10_READY 0x10 +#define VIA_ACLINK_C01_READY 0x04 /* secondary codec ready */ +#define VIA_ACLINK_LOWPOWER 0x02 /* low-power state */ +#define VIA_ACLINK_C00_READY 0x01 /* primary codec ready */ +#define VIA_ACLINK_CTRL 0x41 +#define VIA_ACLINK_CTRL_ENABLE 0x80 /* 0: disable, 1: enable */ +#define VIA_ACLINK_CTRL_RESET 0x40 /* 0: assert, 1: de-assert */ +#define VIA_ACLINK_CTRL_SYNC 0x20 /* 0: release SYNC, 1: force SYNC hi */ +#define VIA_ACLINK_CTRL_SDO 0x10 /* 0: release SDO, 1: force SDO hi */ +#define VIA_ACLINK_CTRL_VRA 0x08 /* 0: disable VRA, 1: enable VRA */ +#define VIA_ACLINK_CTRL_PCM 0x04 /* 0: disable PCM, 1: enable PCM */ +#define VIA_ACLINK_CTRL_FM 0x02 /* via686 only */ +#define VIA_ACLINK_CTRL_SB 0x01 /* via686 only */ +#define VIA_ACLINK_CTRL_INIT (VIA_ACLINK_CTRL_ENABLE|\ + VIA_ACLINK_CTRL_RESET|\ + VIA_ACLINK_CTRL_PCM|\ + VIA_ACLINK_CTRL_VRA) +#define VIA_FUNC_ENABLE 0x42 +#define VIA_FUNC_MIDI_PNP 0x80 /* FIXME: it's 0x40 in the datasheet! */ +#define VIA_FUNC_MIDI_IRQMASK 0x40 /* FIXME: not documented! */ +#define VIA_FUNC_RX2C_WRITE 0x20 +#define VIA_FUNC_SB_FIFO_EMPTY 0x10 +#define VIA_FUNC_ENABLE_GAME 0x08 +#define VIA_FUNC_ENABLE_FM 0x04 +#define VIA_FUNC_ENABLE_MIDI 0x02 +#define VIA_FUNC_ENABLE_SB 0x01 +#define VIA_PNP_CONTROL 0x43 +#define VIA_FM_NMI_CTRL 0x48 +#define VIA8233_VOLCHG_CTRL 0x48 +#define VIA8233_SPDIF_CTRL 0x49 +#define VIA8233_SPDIF_DX3 0x08 +#define VIA8233_SPDIF_SLOT_MASK 0x03 +#define VIA8233_SPDIF_SLOT_1011 0x00 +#define VIA8233_SPDIF_SLOT_34 0x01 +#define VIA8233_SPDIF_SLOT_78 0x02 +#define VIA8233_SPDIF_SLOT_69 0x03 + +/* + */ + +typedef struct _snd_via82xx via82xx_t; +typedef struct via_dev viadev_t; +#define chip_t via82xx_t + +/* + * pcm stream + */ + +struct snd_via_sg_table { + unsigned int offset; + unsigned int size; +} ; + +#define VIA_TABLE_SIZE 255 + +struct via_dev { + unsigned int reg_offset; + unsigned long port; + int direction; /* playback = 0, capture = 1 */ + snd_pcm_substream_t *substream; + int running; + unsigned int tbl_entries; /* # descriptors */ + u32 *table; /* physical address + flag */ + dma_addr_t table_addr; + struct snd_via_sg_table *idx_table; + /* for recovery from the unexpected pointer */ + unsigned int lastpos; + unsigned int bufsize; + unsigned int bufsize2; +}; + + +/* + * allocate and initialize the descriptor buffers + * periods = number of periods + * fragsize = period size in bytes + */ +static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, + struct pci_dev *pci, + unsigned int periods, unsigned int fragsize) +{ + unsigned int i, idx, ofs, rest; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); + + if (! dev->table) { + /* the start of each lists must be aligned to 8 bytes, + * but the kernel pages are much bigger, so we don't care + */ + dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table_addr); + if (! dev->table) + return -ENOMEM; + } + if (! dev->idx_table) { + dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL); + if (! dev->idx_table) + return -ENOMEM; + } + + /* fill the entries */ + idx = 0; + ofs = 0; + for (i = 0; i < periods; i++) { + rest = fragsize; + /* fill descriptors for a period. + * a period can be split to several descriptors if it's + * over page boundary. + */ + do { + unsigned int r; + unsigned int flag; + + if (idx >= VIA_TABLE_SIZE) { + snd_printk(KERN_ERR "via82xx: too much table size!\n"); + return -EINVAL; + } + dev->table[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); + r = PAGE_SIZE - (ofs % PAGE_SIZE); + if (rest < r) + r = rest; + rest -= r; + if (! rest) { + if (i == periods - 1) + flag = VIA_TBL_BIT_EOL; /* buffer boundary */ + else + flag = VIA_TBL_BIT_FLAG; /* period boundary */ + } else + flag = 0; /* period continues to the next */ + // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest); + dev->table[(idx<<1) + 1] = cpu_to_le32(r | flag); + dev->idx_table[idx].offset = ofs; + dev->idx_table[idx].size = r; + ofs += r; + idx++; + } while (rest > 0); + } + dev->tbl_entries = idx; + dev->bufsize = periods * fragsize; + dev->bufsize2 = dev->bufsize / 2; + return 0; +} + + +static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, + struct pci_dev *pci) +{ + if (dev->table) { + snd_free_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), dev->table, dev->table_addr); + dev->table = NULL; + } + if (dev->idx_table) { + kfree(dev->idx_table); + dev->idx_table = NULL; + } +} + + +/* + */ + +enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 }; +enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A }; + +#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ + +struct via_rate_lock { + spinlock_t lock; + int rate; + int used; +}; + +struct _snd_via82xx { + int irq; + + unsigned long port; + struct resource *res_port; + int chip_type; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; + + unsigned char playback_volume[2]; /* for VIA8233/C/8235; default = 0 */ + + unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */ + + struct pci_dev *pci; + snd_card_t *card; + + unsigned int num_devs; + unsigned int playback_devno, multi_devno, capture_devno; + viadev_t devs[VIA_MAX_DEVS]; + struct via_rate_lock rates[2]; /* playback and capture */ + + snd_rawmidi_t *rmidi; + + ac97_t *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; +}; + +static struct pci_device_id snd_via82xx_ids[] __devinitdata = { + { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ + { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); + +/* + * Basic I/O + */ + +static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip) +{ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val) +{ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + + while (timeout-- > 0) { + udelay(1); + if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)) + return val & 0xffff; + } + snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); + return -EIO; +} + +static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID : + VIA_REG_AC97_SECONDARY_VALID; + + while (timeout-- > 0) { + udelay(1); + if ((val = snd_via82xx_codec_xread(chip)) & stat) + return val & 0xffff; + } + snd_printk(KERN_ERR "codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); + return -EIO; +} + +static void snd_via82xx_codec_wait(ac97_t *ac97) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + int err; + err = snd_via82xx_codec_ready(chip, ac97->num); + /* here we need to wait fairly for long time.. */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); +} + +static void snd_via82xx_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + unsigned int xval; + + xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; + xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= reg << VIA_REG_AC97_CMD_SHIFT; + xval |= val << VIA_REG_AC97_DATA_SHIFT; + spin_lock(&chip->ac97_lock); + snd_via82xx_codec_xwrite(chip, xval); + snd_via82xx_codec_ready(chip, ac97->num); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return ~0); + unsigned int xval, val = 0xffff; + int again = 0; + + xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; + xval |= VIA_REG_AC97_READ; + xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; + spin_lock(&chip->ac97_lock); + while (1) { + if (again++ > 3) { + spin_unlock(&chip->ac97_lock); + return 0xffff; + } + snd_via82xx_codec_xwrite(chip, xval); + if (snd_via82xx_codec_ready(chip, ac97->num) < 0) + continue; + if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) { + udelay(25); + val = snd_via82xx_codec_xread(chip); + break; + } + } + spin_unlock(&chip->ac97_lock); + return val & 0xffff; +} + +static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev) +{ + outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, + VIADEV_REG(viadev, OFFSET_CONTROL)); + udelay(50); + /* disable interrupts */ + outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL)); + /* clear interrupts */ + outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS)); + outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */ + // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR)); + viadev->lastpos = 0; +} + + +/* + * Interrupt handler + */ + +static void snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return); + unsigned int status; + unsigned int i; + + spin_lock(&chip->reg_lock); + status = inl(VIAREG(chip, SGD_SHADOW)); + if (! (status & chip->intr_mask)) { + spin_unlock(&chip->reg_lock); + if (chip->rmidi) + /* check mpu401 interrupt */ + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + return; + } + + /* check status for each stream */ + for (i = 0; i < chip->num_devs; i++) { + viadev_t *viadev = &chip->devs[i]; + unsigned char status = inb(VIADEV_REG(viadev, OFFSET_STATUS)); + status &= (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG); + if (! status) + continue; + if (viadev->substream && viadev->running) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(viadev->substream); + spin_lock(&chip->reg_lock); + } + outb(status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */ + } + spin_unlock(&chip->reg_lock); +} + +/* + * PCM callbacks + */ + +/* + * trigger callback + */ +static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned char val; + + if (chip->chip_type != TYPE_VIA686) + val = VIA_REG_CTRL_INT; + else + val = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val |= VIA_REG_CTRL_START; + viadev->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = VIA_REG_CTRL_TERMINATE; + viadev->running = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val |= VIA_REG_CTRL_PAUSE; + viadev->running = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + viadev->running = 1; + break; + default: + return -EINVAL; + } + outb(val, VIADEV_REG(viadev, OFFSET_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via82xx_channel_reset(chip, viadev); + return 0; +} + + +/* + * pointer callbacks + */ + +/* + * calculate the linear position at the given sg-buffer index and the rest count + */ +static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, unsigned int count) +{ + unsigned int size, res; + + size = viadev->idx_table[idx].size; + res = viadev->idx_table[idx].offset + size - count; + + /* check the validity of the calculated position */ + if (size < count || (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))) { +#ifdef POINTER_DEBUG + printk("fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count); +#endif + /* count register returns full size when end of buffer is reached */ + if (size != count) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr, using last valid pointer\n"); + res = viadev->lastpos; + } else { + res = viadev->idx_table[idx].offset + size; + if (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2)) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), using last valid pointer\n"); + res = viadev->lastpos; + } + } + } + viadev->lastpos = res; /* remember the last position */ + if (res >= viadev->bufsize) + res -= viadev->bufsize; + return res; +} + +/* + * get the current pointer on via686 + */ +static snd_pcm_uframes_t snd_via686_pcm_pointer(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned int idx, ptr, count, res; + + snd_assert(viadev->tbl_entries, return 0); + if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE)) + return 0; + + spin_lock(&chip->reg_lock); + count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff; + /* The via686a does not have the current index register, + * so we need to calculate the index from CURR_PTR. + */ + ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR)); + if (ptr <= (unsigned int)viadev->table_addr) + idx = 0; + else /* CURR_PTR holds the address + 8 */ + idx = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; + res = calc_linear_pos(viadev, idx, count); + spin_unlock(&chip->reg_lock); + + return bytes_to_frames(substream->runtime, res); +} + +/* + * get the current pointer on via823x + */ +static snd_pcm_uframes_t snd_via8233_pcm_pointer(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned int idx, count, res; + + snd_assert(viadev->tbl_entries, return 0); + if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE)) + return 0; + spin_lock(&chip->reg_lock); + count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)); + idx = count >> 24; + if (idx >= viadev->tbl_entries) { +#ifdef POINTER_DEBUG + printk("fail: invalid idx = %i/%i\n", idx, viadev->tbl_entries); +#endif + res = viadev->lastpos; + } else { + count &= 0xffffff; + res = calc_linear_pos(viadev, idx, count); + } + spin_unlock(&chip->reg_lock); + + return bytes_to_frames(substream->runtime, res); +} + + +/* + * hw_params callback: + * allocate the buffer and build up the buffer description table + */ +static int snd_via82xx_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + int err; + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) + return err; + err = build_via_table(viadev, substream, chip->pci, + params_periods(hw_params), + params_period_bytes(hw_params)); + if (err < 0) + return err; + + return 0; +} + +/* + * hw_free callback: + * clean up the buffer description table and release the buffer + */ +static int snd_via82xx_hw_free(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + + clean_via_table(viadev, substream, chip->pci); + snd_pcm_lib_free_pages(substream); + return 0; +} + + +/* + * set up the table pointer + */ +static void snd_via82xx_set_table_ptr(via82xx_t *chip, viadev_t *viadev) +{ + snd_via82xx_codec_ready(chip, 0); + outl((u32)viadev->table_addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); +} + +/* + * prepare callback for playback and capture on via686 + */ +static void via686_setup_format(via82xx_t *chip, viadev_t *viadev, snd_pcm_runtime_t *runtime) +{ + snd_via82xx_channel_reset(chip, viadev); + /* this must be set after channel_reset */ + snd_via82xx_set_table_ptr(chip, viadev); + outb(VIA_REG_TYPE_AUTOSTART | + (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) | + VIA_REG_TYPE_INT_EOL | + VIA_REG_TYPE_INT_FLAG, VIADEV_REG(viadev, OFFSET_TYPE)); +} + +static int snd_via686_playback_prepare(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + via686_setup_format(chip, viadev, runtime); + return 0; +} + +static int snd_via686_capture_prepare(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + via686_setup_format(chip, viadev, runtime); + return 0; +} + +/* + * lock the current rate + */ +static int via_lock_rate(struct via_rate_lock *rec, int rate) +{ + int changed = 0; + + spin_lock(&rec->lock); + if (rec->rate) { + if (rec->rate != rate && rec->used > 1) { + spin_unlock(&rec->lock); + return -EINVAL; + } + } else { + rec->rate = rate; + changed = 1; + } + spin_unlock(&rec->lock); + return changed; +} + +/* + * prepare callback for DSX playback on via823x + */ +static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + int rate_changed; + u32 rbits; + + if ((rate_changed = via_lock_rate(&chip->rates[0], runtime->rate)) < 0) + return rate_changed; + if (rate_changed) { + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + } + rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000; + snd_assert((rbits & ~0xfffff) == 0, return -EINVAL); + snd_via82xx_channel_reset(chip, viadev); + snd_via82xx_set_table_ptr(chip, viadev); + outb(chip->playback_volume[0], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_L)); + outb(chip->playback_volume[1], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_R)); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */ + (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */ + rbits | /* rate */ + 0xff000000, /* STOP index is never reached */ + VIADEV_REG(viadev, OFFSET_STOP_IDX)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); + return 0; +} + +/* + * prepare callback for multi-channel playback on via823x + */ +static int snd_via8233_multi_prepare(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int slots; + int fmt; + + if (via_lock_rate(&chip->rates[0], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + snd_via82xx_set_table_ptr(chip, viadev); + + fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT; + fmt |= runtime->channels << 4; + outb(fmt, VIADEV_REG(viadev, OFS_MULTPLAY_FORMAT)); + /* set sample number to slot 3, 4, 7, 8, 6, 9 (for VIA8233/C,8235) */ + /* corresponding to FL, FR, RL, RR, C, LFE ?? */ + switch (runtime->channels) { + case 1: slots = (1<<0) | (1<<4); break; + case 2: slots = (1<<0) | (2<<4); break; + case 3: slots = (1<<0) | (2<<4) | (5<<8); break; + case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break; + case 5: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16); break; + case 6: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16) | (6<<20); break; + default: slots = 0; break; + } + /* STOP index is never reached */ + outl(0xff000000 | slots, VIADEV_REG(viadev, OFFSET_STOP_IDX)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); + return 0; +} + +/* + * prepare callback for capture on via823x + */ +static int snd_via8233_capture_prepare(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (via_lock_rate(&chip->rates[1], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + snd_via82xx_set_table_ptr(chip, viadev); + outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIADEV_REG(viadev, OFS_CAPTURE_FIFO)); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | + 0xff000000, /* STOP index is never reached */ + VIADEV_REG(viadev, OFFSET_STOP_IDX)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); + return 0; +} + + +/* + * pcm hardware definition, identical for both playback and capture + */ +static snd_pcm_hardware_t snd_via82xx_hw = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = VIA_TABLE_SIZE / 2, + .fifo_size = 0, +}; + + +/* + * open callback skeleton + */ +static int snd_via82xx_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + unsigned long flags; + struct via_rate_lock *ratep; + + runtime->hw = snd_via82xx_hw; + + /* set the hw rate condition */ + ratep = &chip->rates[viadev->direction]; + spin_lock_irqsave(&ratep->lock, flags); + ratep->used++; + if (! ratep->rate) { + int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC; + runtime->hw.rates = chip->ac97->rates[idx]; + if (runtime->hw.rates & SNDRV_PCM_RATE_8000) + runtime->hw.rate_min = 8000; + } else { + /* a fixed rate */ + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate; + } + spin_unlock_irqrestore(&ratep->lock, flags); + + /* we may remove following constaint when we modify table entries + in interrupt */ + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + + runtime->private_data = viadev; + viadev->substream = substream; + + return 0; +} + + +/* + * open callback for playback on via686 and via823x DSX + */ +static int snd_via82xx_playback_open(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = &chip->devs[chip->playback_devno + substream->number]; + + return snd_via82xx_pcm_open(chip, viadev, substream); +} + +/* + * open callback for playback on via823x multi-channel + */ +static int snd_via8233_multi_open(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = &chip->devs[chip->multi_devno]; + int err; + /* channels constraint for VIA8233A + * 3 and 5 channels are not supported + */ + static unsigned int channels[] = { + 1, 2, 4, 6 + }; + static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, + }; + + if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0) + return err; + substream->runtime->hw.channels_max = 6; + if (chip->chip_type == TYPE_VIA8233A) + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + return 0; +} + +/* + * open callback for capture on via686 and via823x + */ +static int snd_via82xx_capture_open(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = &chip->devs[chip->capture_devno + substream->pcm->device]; + + return snd_via82xx_pcm_open(chip, viadev, substream); +} + +/* + * close callback + */ +static int snd_via82xx_pcm_close(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned long flags; + struct via_rate_lock *ratep; + + /* release the rate lock */ + ratep = &chip->rates[viadev->direction]; + spin_lock_irqsave(&ratep->lock, flags); + ratep->used--; + if (! ratep->used) + ratep->rate = 0; + spin_unlock_irqrestore(&ratep->lock, flags); + + viadev->substream = NULL; + return 0; +} + + +/* via686 playback callbacks */ +static snd_pcm_ops_t snd_via686_playback_ops = { + .open = snd_via82xx_playback_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via686_playback_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via686_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via686 capture callbacks */ +static snd_pcm_ops_t snd_via686_capture_ops = { + .open = snd_via82xx_capture_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via686_capture_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via686_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x DSX playback callbacks */ +static snd_pcm_ops_t snd_via8233_playback_ops = { + .open = snd_via82xx_playback_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_playback_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x multi-channel playback callbacks */ +static snd_pcm_ops_t snd_via8233_multi_ops = { + .open = snd_via8233_multi_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_multi_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x capture callbacks */ +static snd_pcm_ops_t snd_via8233_capture_ops = { + .open = snd_via82xx_capture_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_capture_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + + +static void init_viadev(via82xx_t *chip, int idx, unsigned int reg_offset, int direction) +{ + chip->devs[idx].reg_offset = reg_offset; + chip->devs[idx].direction = direction; + chip->devs[idx].port = chip->port + reg_offset; +} + +/* + * create pcm instances for VIA8233, 8233C and 8235 (not 8233A) + */ +static int __devinit snd_via8233_pcm_new(via82xx_t *chip) +{ + snd_pcm_t *pcm; + int i, err; + + chip->playback_devno = 0; /* x 4 */ + chip->multi_devno = 4; /* x 1 */ + chip->capture_devno = 5; /* x 2 */ + chip->num_devs = 7; + chip->intr_mask = 0x33033333; /* FLAG|EOL for rec0-1, mc, sdx0-3 */ + + /* PCM #0: 4 DSX playbacks and 1 capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 4, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playbacks */ + for (i = 0; i < 4; i++) + init_viadev(chip, i, 0x10 * i, 0); + /* capture */ + init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + return err; + + /* PCM #1: multi-channel playback and 2nd capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playback */ + init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0); + /* set up capture */ + init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 1); + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + return err; + + return 0; +} + +/* + * create pcm instances for VIA8233A + */ +static int __devinit snd_via8233a_pcm_new(via82xx_t *chip) +{ + snd_pcm_t *pcm; + int err; + + chip->multi_devno = 0; + chip->playback_devno = 1; + chip->capture_devno = 2; + chip->num_devs = 3; + chip->intr_mask = 0x03033000; /* FLAG|EOL for rec0, mc, sdx3 */ + + /* PCM #0: multi-channel playback and capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playback */ + init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0); + /* capture */ + init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + return err; + + /* PCM #1: DXS3 playback (for spdif) */ + err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playback */ + init_viadev(chip, chip->playback_devno, 0x30, 0); + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + return err; + + return 0; +} + +/* + * create a pcm instance for via686a/b + */ +static int __devinit snd_via686_pcm_new(via82xx_t *chip) +{ + snd_pcm_t *pcm; + int err; + + chip->playback_devno = 0; + chip->capture_devno = 1; + chip->num_devs = 2; + chip->intr_mask = 0x77; /* FLAG | EOL for PB, CP, FM */ + + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0); + init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1); + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + return err; + + return 0; +} + + +/* + * Mixer part + */ + +static int snd_via8233_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[2] = { + "Line", "Mic" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_via8233_capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long port = chip->port + kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL; + ucontrol->value.enumerated.item[0] = inb(port) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0; + return 0; +} + +static int snd_via8233_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long port = chip->port + kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL; + unsigned long flags; + u8 val, oval; + + spin_lock_irqsave(&chip->reg_lock, flags); + oval = inb(port); + val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC; + if (ucontrol->value.enumerated.item[0]) + val |= VIA_REG_CAPTURE_CHANNEL_MIC; + if (val != oval) + outb(val, port); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return val != oval; +} + +static snd_kcontrol_new_t snd_via8233_capture_source __devinitdata = { + .name = "Input Source Select", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_via8233_capture_source_info, + .get = snd_via8233_capture_source_get, + .put = snd_via8233_capture_source_put, +}; + +static int snd_via8233_dxs3_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_via8233_dxs3_spdif_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u8 val; + + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val); + ucontrol->value.integer.value[0] = (val & VIA8233_SPDIF_DX3) ? 1 : 0; + return 0; +} + +static int snd_via8233_dxs3_spdif_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u8 val, oval; + + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &oval); + val = oval & ~VIA8233_SPDIF_DX3; + if (ucontrol->value.integer.value[0]) + val |= VIA8233_SPDIF_DX3; + if (val != oval) { + pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_via8233_dxs3_spdif_control __devinitdata = { + .name = "IEC958 Output Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_via8233_dxs3_spdif_info, + .get = snd_via8233_dxs3_spdif_get, + .put = snd_via8233_dxs3_spdif_put, +}; + +static int snd_via8233_dxs_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xff; + return 0; +} + +static int snd_via8233_dxs_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = 0xff - chip->playback_volume[0]; + ucontrol->value.integer.value[1] = 0xff - chip->playback_volume[1]; + return 0; +} + +static int snd_via8233_dxs_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val; + int change; + + val = 0xff - ucontrol->value.integer.value[0]; + change = val != chip->playback_volume[0]; + if (val) + chip->playback_volume[0] = val; + val = 0xff - ucontrol->value.integer.value[1]; + change |= val != chip->playback_volume[1]; + if (val) + chip->playback_volume[1] = val; + return change; +} + +static snd_kcontrol_new_t snd_via8233_dxs_volume_control __devinitdata = { + .name = "VIA DXS Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_via8233_dxs_volume_info, + .get = snd_via8233_dxs_volume_get, + .put = snd_via8233_dxs_volume_put, +}; + +/* + */ + +static void snd_via82xx_mixer_free_ac97(ac97_t *ac97) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static struct ac97_quirk ac97_quirks[] = { + { 0x1106, 0x4161, "ASRock K7VT2", AC97_TUNE_HP_ONLY }, + { } /* terminator */ +}; + +static int __devinit snd_via82xx_mixer_new(via82xx_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_via82xx_codec_write; + ac97.read = snd_via82xx_codec_read; + ac97.wait = snd_via82xx_codec_wait; + ac97.private_data = chip; + ac97.private_free = snd_via82xx_mixer_free_ac97; + ac97.clock = chip->ac97_clock; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + snd_ac97_tune_hardware(chip->ac97, chip->pci, ac97_quirks); + + if (chip->chip_type != TYPE_VIA686) { + /* use slot 10/11 */ + snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4); + } + + return 0; +} + +/* + * joystick + */ + +static int snd_via82xx_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_via82xx_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, VIA_FUNC_ENABLE, &val); + ucontrol->value.integer.value[0] = (val & VIA_FUNC_ENABLE_GAME) ? 1 : 0; + return 0; +} + +static int snd_via82xx_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, VIA_FUNC_ENABLE, &oval); + val = oval & ~VIA_FUNC_ENABLE_GAME; + if (ucontrol->value.integer.value[0]) + val |= VIA_FUNC_ENABLE_GAME; + if (val != oval) { + pci_write_config_word(chip->pci, VIA_FUNC_ENABLE, val); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_via82xx_joystick_control __devinitdata = { + .name = "Joystick", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_via82xx_joystick_info, + .get = snd_via82xx_joystick_get, + .put = snd_via82xx_joystick_put, +}; + +/* + * + */ + +static int snd_via8233_init_misc(via82xx_t *chip, int dev) +{ + int i, err, caps; + unsigned char val; + + caps = chip->chip_type == TYPE_VIA8233A ? 1 : 2; + for (i = 0; i < caps; i++) { + snd_via8233_capture_source.index = i; + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_capture_source, chip)); + if (err < 0) + return err; + } + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip)); + if (err < 0) + return err; + + /* select spdif data slot 10/11 */ + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val); + val = (val & ~VIA8233_SPDIF_SLOT_MASK) | VIA8233_SPDIF_SLOT_1011; + pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val); + + return 0; +} + +static int snd_via686_init_misc(via82xx_t *chip, int dev) +{ + unsigned char legacy, legacy_cfg; + int rev_h = 0; + + legacy = chip->old_legacy; + legacy_cfg = chip->old_legacy_cfg; + legacy |= VIA_FUNC_MIDI_IRQMASK; /* FIXME: correct? (disable MIDI) */ + legacy &= ~VIA_FUNC_ENABLE_GAME; /* disable joystick */ + if (chip->revision >= VIA_REV_686_H) { + rev_h = 1; + if (check_region(pci_resource_start(chip->pci, 2), 4)) + legacy &= ~VIA_FUNC_MIDI_PNP; /* disable PCI I/O 2 */ + else + legacy |= VIA_FUNC_MIDI_PNP; /* enable PCI I/O 2 */ + } + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy); + pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg); + if (rev_h) { + if (mpu_port[dev] >= 0x200) { /* force MIDI */ + pci_write_config_dword(chip->pci, 0x18, (mpu_port[dev] & 0xfffc) | 0x01); + } else if (legacy & VIA_FUNC_MIDI_PNP) { + mpu_port[dev] = pci_resource_start(chip->pci, 2); + if (mpu_port[dev] < 0x200) /* bad value */ + legacy &= ~VIA_FUNC_ENABLE_MIDI; /* disable MIDI */ + } + if (mpu_port[dev] >= 0x200) + legacy |= VIA_FUNC_ENABLE_MIDI; + else + legacy &= ~VIA_FUNC_ENABLE_MIDI; + } else { + switch (mpu_port[dev]) { /* force MIDI */ + case 0x300: + case 0x310: + case 0x320: + case 0x330: + legacy_cfg &= ~(3 << 2); + legacy_cfg |= (mpu_port[dev] & 0x0030) >> 2; + legacy |= VIA_FUNC_ENABLE_MIDI; + break; + default: /* no, use BIOS settings */ + if (legacy & VIA_FUNC_ENABLE_MIDI) + mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); + break; + } + } + if (legacy & VIA_FUNC_ENABLE_MIDI) { + if (check_region(mpu_port[dev], 2)) { + printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", mpu_port[dev]); + legacy &= ~VIA_FUNC_ENABLE_MIDI; + } else if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A, + mpu_port[dev], 0, + chip->irq, 0, + &chip->rmidi) < 0) { + printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", mpu_port[dev]); + legacy &= ~VIA_FUNC_ENABLE_MIDI; + } + } + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy); + pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg); + if (legacy & VIA_FUNC_ENABLE_MIDI) { + legacy &= ~VIA_FUNC_MIDI_IRQMASK; /* enable MIDI interrupt */ + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy); + } + + /* card switches */ + return snd_ctl_add(chip->card, snd_ctl_new1(&snd_via82xx_joystick_control, chip)); +} + + +/* + * + */ + +static int __devinit snd_via82xx_chip_init(via82xx_t *chip) +{ + ac97_t ac97; + unsigned int val; + int max_count; + unsigned char pval; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + +#if 0 /* broken on K7M? */ + if (chip->chip_type == TYPE_VIA686) + /* disable all legacy ports */ + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, 0); +#endif + pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); + if (! (pval & VIA_ACLINK_C00_READY)) { /* codec not ready? */ + /* deassert ACLink reset, force SYNC */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, + VIA_ACLINK_CTRL_ENABLE | + VIA_ACLINK_CTRL_RESET | + VIA_ACLINK_CTRL_SYNC); + udelay(100); +#if 1 /* FIXME: should we do full reset here for all chip models? */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, 0x00); + udelay(100); +#else + /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, + VIA_ACLINK_CTRL_RESET|VIA_ACLINK_CTRL_SYNC); + udelay(2); +#endif + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + /* note - FM data out has trouble with non VRA codecs !! */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT); + udelay(100); + } + + /* Make sure VRA is enabled, in case we didn't do a + * complete codec reset, above */ + pci_read_config_byte(chip->pci, VIA_ACLINK_CTRL, &pval); + if ((pval & VIA_ACLINK_CTRL_INIT) != VIA_ACLINK_CTRL_INIT) { + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + /* note - FM data out has trouble with non VRA codecs !! */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT); + udelay(100); + } + + /* wait until codec ready */ + max_count = ((3 * HZ) / 4) + 1; + do { + pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); + if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + + if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) + snd_printk("AC'97 codec is not ready [0x%x]\n", val); + + /* and then reset codec.. */ + snd_via82xx_codec_ready(chip, 0); + snd_via82xx_codec_write(&ac97, AC97_RESET, 0x0000); + snd_via82xx_codec_read(&ac97, 0); + +#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */ + snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + max_count = ((3 * HZ) / 4) + 1; + snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + do { + if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) { + chip->ac97_secondary = 1; + goto __ac97_ok2; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + /* This is ok, the most of motherboards have only one codec */ + + __ac97_ok2: +#endif + + if (chip->chip_type == TYPE_VIA686) { + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte(chip->pci, VIA_FM_NMI_CTRL, 0); + /* disable all GPI interrupts */ + outl(0, VIAREG(chip, GPI_INTR)); + } + + return 0; +} + +static int snd_via82xx_free(via82xx_t *chip) +{ + unsigned int i; + + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); + synchronize_irq(chip->irq); + __end_hw: + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->chip_type == TYPE_VIA686) { + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy); + pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_via82xx_dev_free(snd_device_t *device) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, device->device_data, return -ENXIO); + return snd_via82xx_free(chip); +} + +static int __devinit snd_via82xx_create(snd_card_t * card, + struct pci_dev *pci, + int chip_type, + int revision, + unsigned int ac97_clock, + via82xx_t ** r_via) +{ + via82xx_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_via82xx_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = snd_magic_kcalloc(via82xx_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + chip->chip_type = chip_type; + chip->revision = revision; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + spin_lock_init(&chip->rates[0].lock); + spin_lock_init(&chip->rates[1].lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy); + pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg); + + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 256, card->driver)) == NULL) { + snd_via82xx_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void *)chip)) { + snd_via82xx_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + synchronize_irq(chip->irq); + + if ((err = snd_via82xx_chip_init(chip)) < 0) { + snd_via82xx_free(chip); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_via82xx_free(chip); + return err; + } + + /* The 8233 ac97 controller does not implement the master bit + * in the pci command register. IMHO this is a violation of the PCI spec. + * We call pci_set_master here because it does not hurt. */ + pci_set_master(pci); + + *r_via = chip; + return 0; +} + +struct via823x_info { + int revision; + char *name; + int type; +}; +static struct via823x_info via823x_cards[] __devinitdata = { + { VIA_REV_PRE_8233, "VIA 8233-Pre", TYPE_VIA8233 }, + { VIA_REV_8233C, "VIA 8233C", TYPE_VIA8233 }, + { VIA_REV_8233, "VIA 8233", TYPE_VIA8233 }, + { VIA_REV_8233A, "VIA 8233A", TYPE_VIA8233A }, + { VIA_REV_8235, "VIA 8235", TYPE_VIA8233 }, +}; + +static int __devinit snd_via82xx_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + via82xx_t *chip; + unsigned char revision; + int chip_type = 0, card_type; + unsigned int i; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + card_type = pci_id->driver_data; + pci_read_config_byte(pci, PCI_REVISION_ID, &revision); + switch (card_type) { + case TYPE_CARD_VIA686: + strcpy(card->driver, "VIA686A"); + sprintf(card->shortname, "VIA 82C686A/B rev%x", revision); + chip_type = TYPE_VIA686; + break; + case TYPE_CARD_VIA8233: + chip_type = TYPE_VIA8233; + sprintf(card->shortname, "VIA 823x rev%x", revision); + for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) { + if (revision == via823x_cards[i].revision) { + chip_type = via823x_cards[i].type; + strcpy(card->shortname, via823x_cards[i].name); + break; + } + } + if (chip_type == TYPE_VIA8233A) + strcpy(card->driver, "VIA8233A"); + else + strcpy(card->driver, "VIA8233"); + break; + default: + snd_printk(KERN_ERR "invalid card type %d\n", card_type); + err = -EINVAL; + goto __error; + } + + if ((err = snd_via82xx_create(card, pci, chip_type, revision, ac97_clock[dev], &chip)) < 0) + goto __error; + + if ((err = snd_via82xx_mixer_new(chip)) < 0) + goto __error; + + if (chip_type == TYPE_VIA686) { + if ((err = snd_via686_pcm_new(chip)) < 0 || + (err = snd_via686_init_misc(chip, dev)) < 0) + goto __error; + } else { + if (chip_type == TYPE_VIA8233A) { + if ((err = snd_via8233a_pcm_new(chip)) < 0) + goto __error; + } else { + if ((err = snd_via8233_pcm_new(chip)) < 0) + goto __error; + } + if ((err = snd_via8233_init_misc(chip, dev)) < 0) + goto __error; + } + /* disable interrupts */ + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; + + __error: + snd_card_free(card); + return err; +} + +static void __devexit snd_via82xx_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "VIA 82xx Audio", + .id_table = snd_via82xx_ids, + .probe = snd_via82xx_probe, + .remove = __devexit_p(snd_via82xx_remove), +}; + +static int __init alsa_card_via82xx_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "VIA 82xx soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_via82xx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via82xx_init) +module_exit(alsa_card_via82xx_exit) + +#ifndef MODULE + +/* format is: snd-via82xx=enable,index,id, + mpu_port,ac97_clock */ + +static int __init alsa_card_via82xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2 && + get_option(&str,&ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-via82xx=", alsa_card_via82xx_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ymfpci/Makefile linux/sound/pci/ymfpci/Makefile --- linux-2.4.21-rc1.orig/sound/pci/ymfpci/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ymfpci/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ymfpci.o + +list-multi := snd-ymfpci.o + +snd-ymfpci-objs := ymfpci.o ymfpci_main.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o + +include $(TOPDIR)/Rules.make + +snd-ymfpci.o: $(snd-ymfpci-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ymfpci-objs) diff -urN linux-2.4.21-rc1.orig/sound/pci/ymfpci/ymfpci.c linux/sound/pci/ymfpci/ymfpci.c --- linux-2.4.21-rc1.orig/sound/pci/ymfpci/ymfpci.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ymfpci/ymfpci.c 2002-10-21 12:28:26.000000000 -0600 @@ -0,0 +1,335 @@ +/* + * The driver for the Yamaha's DS1/DS1E cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Yamaha DS-XG PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Yamaha,YMF724}," + "{Yamaha,YMF724F}," + "{Yamaha,YMF740}," + "{Yamaha,YMF740C}," + "{Yamaha,YMF744}," + "{Yamaha,YMF754}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int rear_switch[SNDRV_CARDS]; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for the Yamaha DS-XG PCI soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for the Yamaha DS-XG PCI soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Yamaha DS-XG soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(mpu_port, "MPU-401 Port."); +MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED); +MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(fm_port, "FM OPL-3 Port."); +MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED); +MODULE_PARM(rear_switch, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch"); +MODULE_PARM_SYNTAX(rear_switch, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +static struct pci_device_id snd_ymfpci_ids[] __devinitdata = { + { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */ + { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724F */ + { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740 */ + { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740C */ + { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF744 */ + { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF754 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids); + +static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + ymfpci_t *chip; + opl3_t *opl3; + char *str; + int err; + u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci_id->device) { + case 0x0004: str = "YMF724"; break; + case 0x000d: str = "YMF724F"; break; + case 0x000a: str = "YMF740"; break; + case 0x000c: str = "YMF740C"; break; + case 0x0010: str = "YMF744"; break; + case 0x0012: str = "YMF754"; break; + default: str = "???"; break; + } + + legacy_ctrl = 0; + legacy_ctrl2 = 0x0800; /* SMOD = 01 */ + + if (pci_id->device >= 0x0010) { /* YMF 744/754 */ + if (fm_port[dev] < 0) + fm_port[dev] = pci_resource_start(pci, 1); + else if (check_region(fm_port[dev], 4)) + fm_port[dev] = -1; + if (fm_port[dev] >= 0) { + legacy_ctrl |= 2; + pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]); + } + if (mpu_port[dev] < 0) + mpu_port[dev] = pci_resource_start(pci, 1) + 0x20; + else if (check_region(mpu_port[dev], 2)) + mpu_port[dev] = -1; + if (mpu_port[dev] >= 0) { + legacy_ctrl |= 8; + pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]); + //snd_printd("MPU401 supported on 0x%lx\n", mpu_port[dev]); + } + } else { + switch (fm_port[dev]) { + case 0x388: legacy_ctrl2 |= 0; break; + case 0x398: legacy_ctrl2 |= 1; break; + case 0x3a0: legacy_ctrl2 |= 2; break; + case 0x3a8: legacy_ctrl2 |= 3; break; + default: fm_port[dev] = -1; break; + } + if (fm_port[dev] > 0 && check_region(fm_port[dev], 4) == 0) + legacy_ctrl |= 2; + else { + legacy_ctrl2 &= ~3; + fm_port[dev] = -1; + } + switch (mpu_port[dev]) { + case 0x330: legacy_ctrl2 |= 0 << 4; break; + case 0x300: legacy_ctrl2 |= 1 << 4; break; + case 0x332: legacy_ctrl2 |= 2 << 4; break; + case 0x334: legacy_ctrl2 |= 3 << 4; break; + default: mpu_port[dev] = -1; break; + } + if (mpu_port[dev] > 0 && check_region(mpu_port[dev], 2) == 0) { + //snd_printd("MPU401 supported on 0x%lx\n", mpu_port[dev]); + legacy_ctrl |= 8; + } else { + legacy_ctrl2 &= ~(3 << 4); + mpu_port[dev] = -1; + } + } + if (mpu_port[dev] > 0) { + // this bit is for legacy mpu irqs + // legacy_ctrl |= 0x10; /* MPU401 irq enable */ + legacy_ctrl2 |= 1 << 15; /* IMOD */ + } + pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl); + //snd_printdd("legacy_ctrl = 0x%x\n", legacy_ctrl); + pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + //snd_printdd("legacy_ctrl2 = 0x%x\n", legacy_ctrl2); + pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); + if ((err = snd_ymfpci_create(card, pci, + old_legacy_ctrl, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_mixer(chip, rear_switch[dev])) < 0) { + snd_card_free(card); + return err; + } + if (mpu_port[dev] > 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, + mpu_port[dev], 0, + pci->irq, 0, &chip->rawmidi)) < 0) { + printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]); + mpu_port[dev] = 0; + // only for legacy mpu irqs + // legacy_ctrl &= ~0x10; /* disable MPU401 irq */ + // pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + } + } + if (fm_port[dev] > 0) { + if ((err = snd_opl3_create(card, + fm_port[dev], + fm_port[dev] + 2, + OPL3_HW_OPL3, 0, &opl3)) < 0) { + printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", fm_port[dev]); + fm_port[dev] = 0; + legacy_ctrl &= ~2; + pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + snd_printk("cannot create opl3 hwdep\n"); + return err; + } + } + if ((err = snd_ymfpci_joystick(chip)) < 0) { + printk(KERN_WARNING "ymfpci: cannot initialize joystick, skipping...\n"); + } + strcpy(card->driver, str); + sprintf(card->shortname, "Yamaha DS-XG PCI (%s)", str); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, + chip->reg_area_virt, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_ymfpci_suspend(struct pci_dev *pci, u32 state) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO); + snd_ymfpci_suspend(chip); + return 0; +} +static int snd_card_ymfpci_resume(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO); + snd_ymfpci_resume(chip); + return 0; +} +#else +static void snd_card_ymfpci_suspend(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + snd_ymfpci_suspend(chip); +} +static void snd_card_ymfpci_resume(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + snd_ymfpci_resume(chip); +} +#endif +#endif + +static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Yamaha DS-XG PCI", + .id_table = snd_ymfpci_ids, + .probe = snd_card_ymfpci_probe, + .remove = __devexit_p(snd_card_ymfpci_remove), +#ifdef CONFIG_PM + .suspend = snd_card_ymfpci_suspend, + .resume = snd_card_ymfpci_resume, +#endif +}; + +static int __init alsa_card_ymfpci_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Yamaha DS-XG PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ymfpci_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ymfpci_init) +module_exit(alsa_card_ymfpci_exit) + +#ifndef MODULE + +/* format is: snd-ymfpci=enable,index,id, + fm_port,mpu_port */ + +static int __init alsa_card_ymfpci_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2 && + get_option(&str,(int *)&fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&mpu_port[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ymfpci=", alsa_card_ymfpci_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/pci/ymfpci/ymfpci_image.h linux/sound/pci/ymfpci/ymfpci_image.h --- linux-2.4.21-rc1.orig/sound/pci/ymfpci/ymfpci_image.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ymfpci/ymfpci_image.h 2001-12-18 09:23:46.000000000 -0700 @@ -0,0 +1,1565 @@ +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09 creat +// 04/12 stop nise fix +// 06/21 WorkingOff timming +static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ diff -urN linux-2.4.21-rc1.orig/sound/pci/ymfpci/ymfpci_main.c linux/sound/pci/ymfpci/ymfpci_main.c --- linux-2.4.21-rc1.orig/sound/pci/ymfpci/ymfpci_main.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/pci/ymfpci/ymfpci_main.c 2003-01-31 08:21:25.000000000 -0700 @@ -0,0 +1,2268 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of YMF724/740/744/754 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define chip_t ymfpci_t + +/* + * constants + */ + +/* + * common I/O routines + */ + +static void snd_ymfpci_irq_wait(ymfpci_t *chip); + +static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset) +{ + return readb(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val) +{ + writeb(val, chip->reg_area_virt + offset); +} + +static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset) +{ + return readw(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val) +{ + writew(val, chip->reg_area_virt + offset); +} + +static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset) +{ + return readl(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val) +{ + writel(val, chip->reg_area_virt + offset); +} + +static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = (jiffies + ((3 * HZ) / 4)) + 1; + do { + if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg)); + return -EBUSY; +} + +static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + u32 cmd; + + snd_ymfpci_codec_ready(chip, 0, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd); +} + +static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return -ENXIO); + + if (snd_ymfpci_codec_ready(chip, 0, 0)) + return ~0; + snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (snd_ymfpci_codec_ready(chip, 0, 0)) + return ~0; + if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) { + int i; + for (i = 0; i < 600; i++) + snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); + } + return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); +} + +/* + * Misc routines + */ + +static u32 snd_ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 375) << 5; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 snd_ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 snd_ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +/* + * Hardware start management + */ + +static void snd_ymfpci_hw_start(ymfpci_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->start_count++ > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | 3); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ymfpci_hw_stop(ymfpci_t *chip) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (--chip->start_count > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0) + break; + } + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice) +{ + ymfpci_voice_t *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &chip->voices[idx]; + voice2 = pair ? &chip->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + snd_ymfpci_hw_start(chip); + if (voice2) + snd_ymfpci_hw_start(chip); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL); + + spin_lock_irqsave(&chip->voice_lock, flags); + for (;;) { + result = voice_alloc(chip, type, pair, rvoice); + if (result == 0 || type != YMFPCI_PCM) + break; + /* TODO: synth/midi voice deallocation */ + break; + } + spin_unlock_irqrestore(&chip->voice_lock, flags); + return result; +} + +int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + snd_ymfpci_hw_stop(chip); + spin_lock_irqsave(&chip->voice_lock, flags); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; + pvoice->interrupt = NULL; + spin_unlock_irqrestore(&chip->voice_lock, flags); + return 0; +} + +/* + * PCM part + */ + +static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice) +{ + ymfpci_pcm_t *ypcm; + u32 pos, delta; + + if ((ypcm = voice->ypcm) == NULL) + return; + if (ypcm->substream == NULL) + return; + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(voice->bank[chip->active_bank].start); + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + if (ypcm->period_pos >= ypcm->period_size) { + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + ypcm->period_pos %= ypcm->period_size; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(ypcm->substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + +static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + ymfpci_t *chip = ypcm->chip; + u32 pos, delta; + + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + if (ypcm->period_pos >= ypcm->period_size) { + ypcm->period_pos %= ypcm->period_size; + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + +static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + int result = 0; + + spin_lock(&chip->reg_lock); + if (ypcm->voices[0] == NULL) { + result = -EINVAL; + goto __unlock; + } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr); + if (ypcm->voices[1] != NULL) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + __unlock: + spin_unlock(&chip->reg_lock); + return result; +} +static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + int result = 0; + u32 tmp; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices) +{ + int err; + + if (ypcm->voices[1] != NULL && voices < 2) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + } + err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]); + if (err < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt; + if (voices > 1) { + ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1]; + ypcm->voices[1]->ypcm = ypcm; + } + return 0; +} + +static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, + unsigned int end, + int output_front, int output_rear) +{ + u32 format; + u32 delta = snd_ymfpci_calc_delta(rate); + u32 lpfQ = snd_ymfpci_calc_lpfQ(rate); + u32 lpfK = snd_ymfpci_calc_lpfK(rate); + snd_ymfpci_playback_bank_t *bank; + unsigned int nbank; + + snd_assert(voice != NULL, return); + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = cpu_to_le32(0x40000000); + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = cpu_to_le32(0x40000000); + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (output_front) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = cpu_to_le32(0x40000000); + } + if (output_rear) { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = cpu_to_le32(0x40000000); + } + } else { + if (output_front) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = cpu_to_le32(0x40000000); + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = cpu_to_le32(0x40000000); + } + } + if (output_rear) { + if ((voice->number & 1) == 0) { + bank->eff3_gain = + bank->eff3_gain_end = cpu_to_le32(0x40000000); + } else { + bank->format |= cpu_to_le32(1); + bank->eff2_gain = + bank->eff2_gain_end = cpu_to_le32(0x40000000); + } + } + } + } +} + +static int __devinit snd_ymfpci_ac3_init(ymfpci_t *chip) +{ + unsigned char *ptr; + dma_addr_t ptr_addr; + + if ((ptr = snd_malloc_pci_pages(chip->pci, 4096, &ptr_addr)) == NULL) + return -ENOMEM; + + chip->ac3_tmp_base = ptr; + chip->ac3_tmp_base_addr = ptr_addr; + chip->bank_effect[3][0]->base = + chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr); + chip->bank_effect[3][0]->loop_end = + chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024); + chip->bank_effect[4][0]->base = + chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr + 2048); + chip->bank_effect[4][0]->loop_end = + chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024); + + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_ac3_done(ymfpci_t *chip) +{ + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3)); + spin_unlock_irq(&chip->reg_lock); + // snd_ymfpci_irq_wait(chip); + if (chip->ac3_tmp_base) { + snd_free_pci_pages(chip->pci, 4096, chip->ac3_tmp_base, chip->ac3_tmp_base_addr); + chip->ac3_tmp_base = NULL; + } + return 0; +} + +static int snd_ymfpci_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0) + return err; + return 0; +} + +static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + if (runtime->private_data == NULL) + return 0; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + snd_pcm_lib_free_pages(substream); + if (ypcm->voices[1]) { + snd_ymfpci_voice_free(chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (ypcm->voices[0]) { + snd_ymfpci_voice_free(chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + return 0; +} + +static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream) +{ + // ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + unsigned int nvoice; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + for (nvoice = 0; nvoice < runtime->channels; nvoice++) + snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice], + runtime->channels == 2, + runtime->rate, + snd_pcm_format_width(runtime->format) == 16, + runtime->dma_addr, + ypcm->buffer_size, + ypcm->output_front, + ypcm->output_rear); + return 0; +} + +static int snd_ymfpci_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ymfpci_capture_hw_free(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ymfpci_capture_prepare(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + snd_ymfpci_capture_bank_t * bank; + int nbank; + u32 rate, format; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + ypcm->shift = 0; + rate = ((48000 * 4096) / runtime->rate) - 1; + format = 0; + if (runtime->channels == 2) { + format |= 2; + ypcm->shift++; + } + if (snd_pcm_format_width(runtime->format) == 8) + format |= 1; + else + ypcm->shift++; + switch (ypcm->capture_bank_number) { + case 0: + snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate); + break; + case 1: + snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = chip->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(runtime->dma_addr); + bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift); + bank->start = 0; + bank->num_of_loops = 0; + } + return 0; +} + +static snd_pcm_uframes_t snd_ymfpci_playback_pointer(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_voice_t *voice = ypcm->voices[0]; + + if (!(ypcm->running && voice)) + return 0; + return le32_to_cpu(voice->bank[chip->active_bank].start); +} + +static snd_pcm_uframes_t snd_ymfpci_capture_pointer(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + if (!ypcm->running) + return 0; + return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; +} + +static void snd_ymfpci_irq_wait(ymfpci_t *chip) +{ + wait_queue_t wait; + int loops = 4; + + while (loops-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0) + continue; + init_waitqueue_entry(&wait, current); + add_wait_queue(&chip->interrupt_sleep, &wait); + atomic_inc(&chip->interrupt_sleep_count); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + remove_wait_queue(&chip->interrupt_sleep, &wait); + } +} + +static void snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, dev_id, return); + u32 status, nvoice, mode; + ymfpci_voice_t *voice; + + status = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if (status & 0x80000000) { + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + spin_lock(&chip->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &chip->voices[nvoice]; + if (voice->interrupt) + voice->interrupt(chip, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + if (chip->capture_substream[nvoice]) + snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]); + } +#if 0 + for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) { + if (chip->effect_substream[nvoice]) + snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]); + } +#endif + spin_unlock(&chip->voice_lock); + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000); + mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2; + snd_ymfpci_writel(chip, YDSXGR_MODE, mode); + spin_unlock(&chip->reg_lock); + + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + } + + status = snd_ymfpci_readl(chip, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + snd_ymfpci_writel(chip, YDSXGR_INTFLAG, ~0); + } + + if (chip->rawmidi) + snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs); +} + +static snd_pcm_hardware_t snd_ymfpci_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, /* FIXME: enough? */ + .period_bytes_min = 64, + .period_bytes_max = 256 * 1024, /* FIXME: enough? */ + .periods_min = 3, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_ymfpci_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, /* FIXME: enough? */ + .period_bytes_min = 64, + .period_bytes_max = 256 * 1024, /* FIXME: enough? */ + .periods_min = 3, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + + if (ypcm) + snd_magic_kfree(ypcm); +} + +static int snd_ymfpci_playback_open_1(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = PLAYBACK_VOICE; + ypcm->substream = substream; + runtime->hw = snd_ymfpci_playback; + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +/* call with spinlock held */ +static void ymfpci_open_extension(ymfpci_t *chip) +{ + if (! chip->rear_opened) { + if (! chip->spdif_opened) /* set AC3 */ + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30)); + /* enable second codec (4CHEN) */ + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010); + } +} + +/* call with spinlock held */ +static void ymfpci_close_extension(ymfpci_t *chip) +{ + if (! chip->rear_opened) { + if (! chip->spdif_opened) + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30)); + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) & ~0x0010); + } +} + +static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open_1(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->output_front = 1; + ypcm->output_rear = chip->mode_dup4ch ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (ypcm->output_rear) { + ymfpci_open_extension(chip); + chip->rear_opened++; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open_1(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->output_front = 0; + ypcm->output_rear = 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2); + ymfpci_open_extension(chip); + chip->spdif_pcm_bits = chip->spdif_bits; + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + chip->spdif_opened++; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + return 0; +} + +static int snd_ymfpci_playback_4ch_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open_1(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->output_front = 0; + ypcm->output_rear = 1; + spin_lock_irqsave(&chip->reg_lock, flags); + ymfpci_open_extension(chip); + chip->rear_opened++; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_capture_open(snd_pcm_substream_t * substream, + u32 capture_bank_number) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = capture_bank_number + CAPTURE_REC; + ypcm->substream = substream; + ypcm->capture_bank_number = capture_bank_number; + chip->capture_substream[capture_bank_number] = substream; + runtime->hw = snd_ymfpci_capture; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + snd_ymfpci_hw_start(chip); + return 0; +} + +static int snd_ymfpci_capture_rec_open(snd_pcm_substream_t * substream) +{ + return snd_ymfpci_capture_open(substream, 0); +} + +static int snd_ymfpci_capture_ac97_open(snd_pcm_substream_t * substream) +{ + return snd_ymfpci_capture_open(substream, 1); +} + +static int snd_ymfpci_playback_close_1(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (ypcm->output_rear && chip->rear_opened > 0) { + chip->rear_opened--; + ymfpci_close_extension(chip); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return snd_ymfpci_playback_close_1(substream); +} + +static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->spdif_opened = 0; + ymfpci_close_extension(chip); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + return snd_ymfpci_playback_close_1(substream); +} + +static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->rear_opened > 0) { + chip->rear_opened--; + ymfpci_close_extension(chip); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return snd_ymfpci_playback_close_1(substream); +} + +static int snd_ymfpci_capture_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + if (ypcm != NULL) { + chip->capture_substream[ypcm->capture_bank_number] = NULL; + snd_ymfpci_hw_stop(chip); + } + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_ops = { + .open = snd_ymfpci_playback_open, + .close = snd_ymfpci_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_playback_hw_params, + .hw_free = snd_ymfpci_playback_hw_free, + .prepare = snd_ymfpci_playback_prepare, + .trigger = snd_ymfpci_playback_trigger, + .pointer = snd_ymfpci_playback_pointer, +}; + +static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = { + .open = snd_ymfpci_capture_rec_open, + .close = snd_ymfpci_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_capture_hw_params, + .hw_free = snd_ymfpci_capture_hw_free, + .prepare = snd_ymfpci_capture_prepare, + .trigger = snd_ymfpci_capture_trigger, + .pointer = snd_ymfpci_capture_pointer, +}; + +static void snd_ymfpci_pcm_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = { + .open = snd_ymfpci_capture_ac97_open, + .close = snd_ymfpci_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_capture_hw_params, + .hw_free = snd_ymfpci_capture_hw_free, + .prepare = snd_ymfpci_capture_prepare, + .trigger = snd_ymfpci_capture_trigger, + .pointer = snd_ymfpci_capture_pointer, +}; + +static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm2 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - AC'97", device, 0, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm2_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - AC'97"); + chip->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = { + .open = snd_ymfpci_playback_spdif_open, + .close = snd_ymfpci_playback_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_playback_hw_params, + .hw_free = snd_ymfpci_playback_hw_free, + .prepare = snd_ymfpci_playback_prepare, + .trigger = snd_ymfpci_playback_trigger, + .pointer = snd_ymfpci_playback_pointer, +}; + +static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm_spdif = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_spdif_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - IEC958"); + chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = { + .open = snd_ymfpci_playback_4ch_open, + .close = snd_ymfpci_playback_4ch_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_playback_hw_params, + .hw_free = snd_ymfpci_playback_hw_free, + .prepare = snd_ymfpci_playback_prepare, + .trigger = snd_ymfpci_playback_trigger, + .pointer = snd_ymfpci_playback_pointer, +}; + +static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm_4ch = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_4ch_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - Rear PCM"); + chip->pcm_4ch = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_ymfpci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irqsave(&chip->reg_lock, flags); + change = chip->spdif_bits != val; + chip->spdif_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_ymfpci_spdif_default_info, + .get = snd_ymfpci_spdif_default_get, + .put = snd_ymfpci_spdif_default_put +}; + +static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = 0x3e; + ucontrol->value.iec958.status[1] = 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_ymfpci_spdif_mask_info, + .get = snd_ymfpci_spdif_mask_get, +}; + +static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irqsave(&chip->reg_lock, flags); + change = chip->spdif_pcm_bits != val; + chip->spdif_pcm_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2)) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_ymfpci_spdif_stream_info, + .get = snd_ymfpci_spdif_stream_get, + .put = snd_ymfpci_spdif_stream_put +}; + +/* + * Mixer controls + */ + +#define YMFPCI_SINGLE(xname, xindex, reg) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ymfpci_info_single, \ + .get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \ + .private_value = reg } + +static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int mask = 1; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int shift = 0, mask = 1, invert = 0; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value; + unsigned int shift = 0, mask = 1, invert = 0; + int change; + unsigned int val, oval; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_ymfpci_readl(chip, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_ymfpci_writel(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define YMFPCI_DOUBLE(xname, xindex, reg) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ymfpci_info_double, \ + .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \ + .private_value = reg } + +static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int reg = kcontrol->private_value; + unsigned int mask = 16383; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + unsigned int val; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + val = snd_ymfpci_readl(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + int change; + unsigned int val1, val2, oval; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_ymfpci_readl(chip, reg); + val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval; + snd_ymfpci_writel(chip, reg, val1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +/* + * 4ch duplication + */ +static int snd_ymfpci_info_dup4ch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ymfpci_get_dup4ch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->mode_dup4ch; + return 0; +} + +static int snd_ymfpci_put_dup4ch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + int change; + change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch); + if (change) + chip->mode_dup4ch = !!ucontrol->value.integer.value[0]; + return change; +} + + +#define YMFPCI_CONTROLS (sizeof(snd_ymfpci_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ymfpci_controls[] __devinitdata = { +YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL), +YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL), +YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "4ch Duplication", + .info = snd_ymfpci_info_dup4ch, + .get = snd_ymfpci_get_dup4ch, + .put = snd_ymfpci_put_dup4ch, +}, +}; + + +/* + * GPIO + */ + +static int snd_ymfpci_get_gpio_out(ymfpci_t *chip, int pin) +{ + u16 reg, mode; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE); + reg &= ~(1 << (pin + 8)); + reg |= (1 << pin); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg); + /* set the level mode for input line */ + mode = snd_ymfpci_readw(chip, YDSXGR_GPIOTYPECONFIG); + mode &= ~(3 << (pin * 2)); + snd_ymfpci_writew(chip, YDSXGR_GPIOTYPECONFIG, mode); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8))); + mode = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return (mode >> pin) & 1; +} + +static int snd_ymfpci_set_gpio_out(ymfpci_t *chip, int pin, int enable) +{ + u16 reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE); + reg &= ~(1 << pin); + reg &= ~(1 << (pin + 8)); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg); + snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8))); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +static int snd_ymfpci_gpio_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ymfpci_gpio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + int pin = (int)kcontrol->private_value; + ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin); + return 0; +} + +static int snd_ymfpci_gpio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + int pin = (int)kcontrol->private_value; + + if (snd_ymfpci_get_gpio_out(chip, pin) != ucontrol->value.integer.value[0]) { + snd_ymfpci_set_gpio_out(chip, pin, !!ucontrol->value.integer.value[0]); + ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_rear_shared __devinitdata = { + .name = "Shared Rear/Line-In Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ymfpci_gpio_sw_info, + .get = snd_ymfpci_gpio_sw_get, + .put = snd_ymfpci_gpio_sw_put, + .private_value = 2, +}; + + +/* + * Mixer routines + */ + +static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch) +{ + ac97_t ac97; + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ymfpci_codec_write; + ac97.read = snd_ymfpci_codec_read; + ac97.private_data = chip; + ac97.private_free = snd_ymfpci_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + for (idx = 0; idx < YMFPCI_CONTROLS; idx++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) + return err; + } + + /* add S/PDIF control */ + snd_assert(chip->pcm_spdif != NULL, return -EIO); + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + chip->spdif_pcm_ctl = kctl; + + /* + * shared rear/line-in + */ + if (rear_switch) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0) + return err; + } + + return 0; +} + + +/* + * joystick support + */ + +static int ymfpci_joystick_ports[4] = { + 0x201, 0x202, 0x204, 0x205 +}; + +static void setup_joystick_base(ymfpci_t *chip) +{ + if (chip->pci->device >= 0x0010) /* YMF 744/754 */ + pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, + ymfpci_joystick_ports[chip->joystick_port]); + else { + u16 legacy_ctrl2; + pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY, &legacy_ctrl2); + legacy_ctrl2 &= ~(3 << 6); + legacy_ctrl2 |= chip->joystick_port << 6; + pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); + } +} + +static int snd_ymfpci_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ymfpci_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &val); + ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0; + return 0; +} + +static int snd_ymfpci_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &oval); + val = oval & ~0x04; + if (ucontrol->value.integer.value[0]) + val |= 0x04; + if (val != oval) { + setup_joystick_base(chip); + pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, val); + return 1; + } + return 0; +} + +static int snd_ymfpci_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + sprintf(uinfo->value.enumerated.name, "port 0x%x", ymfpci_joystick_ports[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ymfpci_joystick_addr_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = chip->joystick_port; + return 0; +} + +static int snd_ymfpci_joystick_addr_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.enumerated.item[0] != chip->joystick_port) { + snd_assert(ucontrol->value.enumerated.item[0] >= 0 && ucontrol->value.enumerated.item[0] < 4, return -EINVAL); + chip->joystick_port = ucontrol->value.enumerated.item[0]; + setup_joystick_base(chip); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_control_joystick __devinitdata = { + .name = "Joystick", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_ymfpci_joystick_info, + .get = snd_ymfpci_joystick_get, + .put = snd_ymfpci_joystick_put, +}; + +static snd_kcontrol_new_t snd_ymfpci_control_joystick_addr __devinitdata = { + .name = "Joystick Address", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_ymfpci_joystick_addr_info, + .get = snd_ymfpci_joystick_addr_get, + .put = snd_ymfpci_joystick_addr_put, +}; + +int __devinit snd_ymfpci_joystick(ymfpci_t *chip) +{ + int err; + + chip->joystick_port = 0; /* default */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick, chip))) < 0) + return err; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick_addr, chip))) < 0) + return err; + return 0; +} + + +/* + * proc interface + */ + +static void snd_ymfpci_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + // ymfpci_t *chip = snd_magic_cast(ymfpci_t, private_data, return); + + snd_iprintf(buffer, "YMFPCI\n\n"); +} + +static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(card, "ymfpci", &entry)) + snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); + return 0; +} + +/* + * initialization routines + */ + +static void snd_ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd); +#if 0 // force to reset + if (cmd & 0x03) { +#endif + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0); +#if 0 + } +#endif +} + +static void snd_ymfpci_enable_dsp(ymfpci_t *chip) +{ + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001); +} + +static void snd_ymfpci_disable_dsp(ymfpci_t *chip) +{ + u32 val; + int timeout = 1000; + + val = snd_ymfpci_readl(chip, YDSXGR_CONFIG); + if (val) + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#include "ymfpci_image.h" + +static void snd_ymfpci_download_image(ymfpci_t *chip) +{ + int i; + u16 ctrl; + unsigned long *inst; + + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + + /* setup control instruction code */ + switch (chip->device_id) { + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + inst = CntrlInst1E; + break; + default: + inst = CntrlInst; + break; + } + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]); + + snd_ymfpci_enable_dsp(chip); +} + +static int __devinit snd_ymfpci_memalloc(ymfpci_t *chip) +{ + long size, playback_ctrl_size; + int voice, bank, reg; + u8 *ptr; + dma_addr_t ptr_addr; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2; + chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2; + chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2; + chip->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + + ((chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0x00ff) & ~0x00ff) + + ((chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0x00ff) & ~0x00ff) + + ((chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0x00ff) & ~0x00ff) + + chip->work_size; + /* work_ptr must be aligned to 256 bytes, but it's already + covered with the kernel page allocation mechanism */ + if ((ptr = snd_malloc_pci_pages(chip->pci, size, &ptr_addr)) == NULL) + return -ENOMEM; + memset(ptr, 0, size); /* for sure */ + chip->work_ptr = ptr; + chip->work_ptr_addr = ptr_addr; + chip->work_ptr_size = size; + + chip->bank_base_playback = ptr; + chip->bank_base_playback_addr = ptr_addr; + chip->ctrl_playback = (u32 *)ptr; + chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + ptr_addr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + chip->voices[voice].number = voice; + chip->voices[voice].bank = (snd_ymfpci_playback_bank_t *)ptr; + chip->voices[voice].bank_addr = ptr_addr; + for (bank = 0; bank < 2; bank++) { + chip->bank_playback[voice][bank] = (snd_ymfpci_playback_bank_t *)ptr; + ptr += chip->bank_size_playback; + ptr_addr += chip->bank_size_playback; + } + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->bank_base_capture = ptr; + chip->bank_base_capture_addr = ptr_addr; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_capture[voice][bank] = (snd_ymfpci_capture_bank_t *)ptr; + ptr += chip->bank_size_capture; + ptr_addr += chip->bank_size_capture; + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->bank_base_effect = ptr; + chip->bank_base_effect_addr = ptr_addr; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_effect[voice][bank] = (snd_ymfpci_effect_bank_t *)ptr; + ptr += chip->bank_size_effect; + ptr_addr += chip->bank_size_effect; + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->work_base = ptr; + chip->work_base_addr = ptr_addr; + + snd_assert(ptr + chip->work_size == chip->work_ptr + chip->work_ptr_size, ); + + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2); + + /* S/PDIF output initialization */ + chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff; + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + + /* S/PDIF input initialization */ + snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0); + + /* digital mixer setup */ + for (reg = 0x80; reg < 0xc0; reg += 4) + snd_ymfpci_writel(chip, reg, 0); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0x3fff3fff); + + return 0; +} + +static int snd_ymfpci_free(ymfpci_t *chip) +{ + u16 ctrl; + + snd_assert(chip != NULL, return -EINVAL); + + if (chip->res_reg_area) { /* don't touch busy hardware */ + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + } + + snd_ymfpci_ac3_done(chip); + + /* Set PCI device to D3 state */ +#if 0 + /* FIXME: temporarily disabled, otherwise we cannot fire up + * the chip again unless reboot. ACPI bug? + */ + pci_set_power_state(chip->pci, 3); +#endif + +#ifdef CONFIG_PM + if (chip->saved_regs) + vfree(chip->saved_regs); +#endif + if (chip->reg_area_virt) + iounmap((void *)chip->reg_area_virt); + if (chip->work_ptr) + snd_free_pci_pages(chip->pci, chip->work_ptr_size, chip->work_ptr, chip->work_ptr_addr); + + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->res_reg_area) { + release_resource(chip->res_reg_area); + kfree_nocheck(chip->res_reg_area); + } + + pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_ymfpci_dev_free(snd_device_t *device) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, device->device_data, return -ENXIO); + return snd_ymfpci_free(chip); +} + +#ifdef CONFIG_PM +static int saved_regs_index[] = { + /* spdif */ + YDSXGR_SPDIFOUTCTRL, + YDSXGR_SPDIFOUTSTATUS, + YDSXGR_SPDIFINCTRL, + /* volumes */ + YDSXGR_PRIADCLOOPVOL, + YDSXGR_NATIVEDACINVOL, + YDSXGR_NATIVEDACOUTVOL, + // YDSXGR_BUF441OUTVOL, + YDSXGR_NATIVEADCINVOL, + YDSXGR_SPDIFLOOPVOL, + YDSXGR_SPDIFOUTVOL, + YDSXGR_ZVOUTVOL, + YDSXGR_LEGACYOUTVOL, + /* address bases */ + YDSXGR_PLAYCTRLBASE, + YDSXGR_RECCTRLBASE, + YDSXGR_EFFCTRLBASE, + YDSXGR_WORKBASE, + /* capture set up */ + YDSXGR_MAPOFREC, + YDSXGR_RECFORMAT, + YDSXGR_RECSLOTSR, + YDSXGR_ADCFORMAT, + YDSXGR_ADCSLOTSR, +}; +#define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index) + +void snd_ymfpci_suspend(ymfpci_t *chip) +{ + snd_card_t *card = chip->card; + unsigned int i; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + snd_pcm_suspend_all(chip->pcm); + snd_pcm_suspend_all(chip->pcm2); + snd_pcm_suspend_all(chip->pcm_spdif); + snd_pcm_suspend_all(chip->pcm_4ch); + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); + chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_disable_dsp(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +void snd_ymfpci_resume(ymfpci_t *chip) +{ + snd_card_t *card = chip->card; + unsigned int i; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + pci_enable_device(chip->pci); + pci_set_master(chip->pci); + snd_ymfpci_aclink_reset(chip->pci); + snd_ymfpci_codec_ready(chip, 0, 0); + snd_ymfpci_download_image(chip); + udelay(100); + + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]); + + snd_ac97_resume(chip->ac97); + + /* start hw again */ + if (chip->start_count > 0) { + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT); + spin_unlock(&chip->reg_lock); + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +static int snd_ymfpci_set_power_state(snd_card_t *card, unsigned int power_state) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_ymfpci_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_ymfpci_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif /* CONFIG_PM */ + +int __devinit snd_ymfpci_create(snd_card_t * card, + struct pci_dev * pci, + unsigned short old_legacy_ctrl, + ymfpci_t ** rchip) +{ + ymfpci_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_ymfpci_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(ymfpci_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->old_legacy_ctrl = old_legacy_ctrl; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->voice_lock); + init_waitqueue_head(&chip->interrupt_sleep); + atomic_set(&chip->interrupt_sleep_count, 0); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->device_id = pci->device; + pci_read_config_byte(pci, PCI_REVISION_ID, (u8 *)&chip->rev); + chip->reg_area_phys = pci_resource_start(pci, 0); + chip->reg_area_virt = (unsigned long)ioremap_nocache(chip->reg_area_phys, 0x8000); + pci_set_master(pci); + + if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) { + snd_ymfpci_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) { + snd_ymfpci_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + snd_ymfpci_aclink_reset(pci); + if (snd_ymfpci_codec_ready(chip, 0, 1) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + + snd_ymfpci_download_image(chip); + + udelay(100); /* seems we need a delay after downloading image.. */ + + if (snd_ymfpci_memalloc(chip) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + + if ((err = snd_ymfpci_ac3_init(chip)) < 0) + return err; + +#ifdef CONFIG_PM + chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32)); + if (chip->saved_regs == NULL) { + snd_ymfpci_free(chip); + return -ENOMEM; + } + card->set_power_state = snd_ymfpci_set_power_state; + card->power_state_private_data = chip; +#endif + + snd_ymfpci_proc_init(card, chip); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ymfpci_free(chip); + return err; + } + + *rchip = chip; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/ppc/Config.in linux/sound/ppc/Config.in --- linux-2.4.21-rc1.orig/sound/ppc/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/Config.in 2003-04-29 05:02:23.000000000 -0600 @@ -0,0 +1,8 @@ +# ALSA PowerMac drivers + +mainmenu_option next_comment +comment 'ALSA PowerMac devices' + +dep_tristate 'PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)' CONFIG_SND_POWERMAC $CONFIG_SND + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/ppc/Makefile linux/sound/ppc/Makefile --- linux-2.4.21-rc1.orig/sound/ppc/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := ppc.o + +list-multi := snd-powermac.o + +snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o + +include $(TOPDIR)/Rules.make + +snd-powermac.o: $(snd-powermac-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-powermac-objs) diff -urN linux-2.4.21-rc1.orig/sound/ppc/awacs.c linux/sound/ppc/awacs.c --- linux-2.4.21-rc1.orig/sound/ppc/awacs.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/awacs.c 2002-09-23 09:20:51.000000000 -0600 @@ -0,0 +1,867 @@ +/* + * PMac AWACS lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "pmac.h" + +#define chip_t pmac_t + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) || defined(CONFIG_ADB_CUDA) +#define PMAC_AMP_AVAIL +#endif + +#ifdef PMAC_AMP_AVAIL +typedef struct awacs_amp { + unsigned char amp_master; + unsigned char amp_vol[2][2]; + unsigned char amp_tone[2]; +} awacs_amp_t; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define CHECK_CUDA_AMP() (adb_hardware == ADB_VIACUDA) +#else +#define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA) +#endif + +#endif /* PMAC_AMP_AVAIL */ + + +/* + * write AWACS register + */ +static void +snd_pmac_awacs_write(pmac_t *chip, int val) +{ + long timeout = 5000000; + + while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) { + if (! --timeout) { + snd_printd("snd_pmac_awacs_write timeout\n"); + break; + } + } + out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22)); +} + +static void +snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val) +{ + snd_pmac_awacs_write(chip, val | (reg << 12)); + chip->awacs_reg[reg] = val; +} + +static void +snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val) +{ + snd_pmac_awacs_write(chip, val | (reg << 12)); +} + +static void do_mdelay(int msec, int can_schedule) +{ + if (can_schedule) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((msec * HZ + 999) / 1000); + } else + mdelay(msec); +} + +#ifdef CONFIG_PMAC_PBOOK +/* Recalibrate chip */ +static void screamer_recalibrate(pmac_t *chip, int can_schedule) +{ + if (chip->model != PMAC_SCREAMER) + return; + + /* Sorry for the horrible delays... I hope to get that improved + * by making the whole PM process asynchronous in a future version + */ + do_mdelay(750, can_schedule); + snd_pmac_awacs_write_noreg(chip, 1, + chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); + do_mdelay(1000, can_schedule); + snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); +} + +#else +#define screamer_recalibrate(chip, can_schedule) /* NOP */ +#endif + + +/* + * additional callback to set the pcm format + */ +static void snd_pmac_awacs_set_format(pmac_t *chip) +{ + chip->awacs_reg[1] &= ~MASK_SAMPLERATE; + chip->awacs_reg[1] |= chip->rate_index << 3; + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); +} + + +/* + * AWACS volume callbacks + */ +/* + * volumes: 0-15 stereo + */ +static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int lshift = (kcontrol->private_value >> 8) & 0xff; + int inverted = (kcontrol->private_value >> 16) & 1; + unsigned long flags; + int vol[2]; + + spin_lock_irqsave(&chip->reg_lock, flags); + vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf; + vol[1] = chip->awacs_reg[reg] & 0xf; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (inverted) { + vol[0] = 0x0f - vol[0]; + vol[1] = 0x0f - vol[1]; + } + ucontrol->value.integer.value[0] = vol[0]; + ucontrol->value.integer.value[1] = vol[1]; + return 0; +} + +static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int lshift = (kcontrol->private_value >> 8) & 0xff; + int inverted = (kcontrol->private_value >> 16) & 1; + int val, oldval; + unsigned long flags; + int vol[2]; + + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (inverted) { + vol[0] = 0x0f - vol[0]; + vol[1] = 0x0f - vol[1]; + } + vol[0] &= 0x0f; + vol[1] &= 0x0f; + spin_lock_irqsave(&chip->reg_lock, flags); + oldval = chip->awacs_reg[reg]; + val = oldval & ~(0xf | (0xf << lshift)); + val |= vol[0] << lshift; + val |= vol[1]; + if (oldval != val) + snd_pmac_awacs_write_reg(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return oldval != reg; +} + + +#define AWACS_VOLUME(xname, xreg, xshift, xinverted) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = snd_pmac_awacs_info_volume, \ + .get = snd_pmac_awacs_get_volume, \ + .put = snd_pmac_awacs_put_volume, \ + .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) } + +/* + * mute master/ogain for AWACS: mono + */ +static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 16) & 1; + int val; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->awacs_reg[reg] >> shift) & 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + val = 1 - val; + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 16) & 1; + int mask = 1 << shift; + int val, changed; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + val = chip->awacs_reg[reg] & ~mask; + if (ucontrol->value.integer.value[0] != invert) + val |= mask; + changed = chip->awacs_reg[reg] != val; + if (changed) + snd_pmac_awacs_write_reg(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return changed; +} + +#define AWACS_SWITCH(xname, xreg, xshift, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = snd_pmac_boolean_mono_info, \ + .get = snd_pmac_awacs_get_switch, \ + .put = snd_pmac_awacs_put_switch, \ + .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) } + + +#ifdef PMAC_AMP_AVAIL +/* + * controls for perch/whisper extension cards, e.g. G3 desktop + * + * TDA7433 connected via i2c address 0x45 (= 0x8a), + * accessed through cuda + */ +static void awacs_set_cuda(int reg, int val) +{ + struct adb_request req; + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val); + while (! req.complete) + cuda_poll(); +} + +/* + * level = 0 - 14, 7 = 0 dB + */ +static void awacs_amp_set_tone(awacs_amp_t *amp, int bass, int treble) +{ + amp->amp_tone[0] = bass; + amp->amp_tone[1] = treble; + if (bass > 7) + bass = (14 - bass) + 8; + if (treble > 7) + treble = (14 - treble) + 8; + awacs_set_cuda(2, (bass << 4) | treble); +} + +/* + * vol = 0 - 31 (attenuation), 32 = mute bit, stereo + */ +static int awacs_amp_set_vol(awacs_amp_t *amp, int index, int lvol, int rvol, int do_check) +{ + if (do_check && amp->amp_vol[index][0] == lvol && + amp->amp_vol[index][1] == rvol) + return 0; + awacs_set_cuda(3 + index, lvol); + awacs_set_cuda(5 + index, rvol); + amp->amp_vol[index][0] = lvol; + amp->amp_vol[index][1] = rvol; + return 1; +} + +/* + * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB + */ +static void awacs_amp_set_master(awacs_amp_t *amp, int vol) +{ + amp->amp_master = vol; + if (vol <= 79) + vol = 32 + (79 - vol); + else + vol = 32 - (vol - 79); + awacs_set_cuda(1, vol); +} + +static void awacs_amp_free(pmac_t *chip) +{ + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return); + kfree(amp); + chip->mixer_data = NULL; + chip->mixer_free = NULL; +} + + +/* + * mixer controls + */ +static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + snd_assert(index >= 0 && index <= 1, return -EINVAL); + ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31); + ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31); + return 0; +} + +static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + int vol[2]; + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + snd_assert(index >= 0 && index <= 1, return -EINVAL); + + vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32); + vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32); + return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); +} + +static int snd_pmac_awacs_get_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + snd_assert(index >= 0 && index <= 1, return -EINVAL); + ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1; + ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1; + return 0; +} + +static int snd_pmac_awacs_put_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + int vol[2]; + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + snd_assert(index >= 0 && index <= 1, return -EINVAL); + + vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31); + vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31); + return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); +} + +static int snd_pmac_awacs_info_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 14; + return 0; +} + +static int snd_pmac_awacs_get_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + snd_assert(index >= 0 && index <= 1, return -EINVAL); + ucontrol->value.integer.value[0] = amp->amp_tone[index]; + return 0; +} + +static int snd_pmac_awacs_put_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + snd_assert(index >= 0 && index <= 1, return -EINVAL); + if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { + amp->amp_tone[index] = ucontrol->value.integer.value[0]; + awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); + return 1; + } + return 0; +} + +static int snd_pmac_awacs_info_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 99; + return 0; +} + +static int snd_pmac_awacs_get_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + ucontrol->value.integer.value[0] = amp->amp_master; + return 0; +} + +static int snd_pmac_awacs_put_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + awacs_amp_t *amp = chip->mixer_data; + snd_assert(amp, return -EINVAL); + if (ucontrol->value.integer.value[0] != amp->amp_master) { + amp->amp_master = ucontrol->value.integer.value[0]; + awacs_amp_set_master(amp, amp->amp_master); + return 1; + } + return 0; +} + +#define AMP_CH_SPK 0 +#define AMP_CH_HD 1 + +static snd_kcontrol_new_t snd_pmac_awacs_amp_vol[] __initdata = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PC Speaker Playback Volume", + .info = snd_pmac_awacs_info_volume_amp, + .get = snd_pmac_awacs_get_volume_amp, + .put = snd_pmac_awacs_put_volume_amp, + .private_value = AMP_CH_SPK, + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Volume", + .info = snd_pmac_awacs_info_volume_amp, + .get = snd_pmac_awacs_get_volume_amp, + .put = snd_pmac_awacs_put_volume_amp, + .private_value = AMP_CH_HD, + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Tone Control - Bass", + .info = snd_pmac_awacs_info_tone_amp, + .get = snd_pmac_awacs_get_tone_amp, + .put = snd_pmac_awacs_put_tone_amp, + .private_value = 0, + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Tone Control - Treble", + .info = snd_pmac_awacs_info_tone_amp, + .get = snd_pmac_awacs_get_tone_amp, + .put = snd_pmac_awacs_put_tone_amp, + .private_value = 1, + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Amp Master Playback Volume", + .info = snd_pmac_awacs_info_master_amp, + .get = snd_pmac_awacs_get_master_amp, + .put = snd_pmac_awacs_put_master_amp, + }, +}; + +static snd_kcontrol_new_t snd_pmac_awacs_amp_hp_sw __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Switch", + .info = snd_pmac_boolean_stereo_info, + .get = snd_pmac_awacs_get_switch_amp, + .put = snd_pmac_awacs_put_switch_amp, + .private_value = AMP_CH_HD, +}; + +static snd_kcontrol_new_t snd_pmac_awacs_amp_spk_sw __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PC Speaker Playback Switch", + .info = snd_pmac_boolean_stereo_info, + .get = snd_pmac_awacs_get_switch_amp, + .put = snd_pmac_awacs_put_switch_amp, + .private_value = AMP_CH_SPK, +}; + +#endif /* PMAC_AMP_AVAIL */ + + +/* + * mic boost for screamer + */ +static int snd_pmac_screamer_mic_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 2; + return 0; +} + +static int snd_pmac_screamer_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int val; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->awacs_reg[6] & MASK_MIC_BOOST) + val = 2; + else if (chip->awacs_reg[0] & MASK_GAINLINE) + val = 1; + else + val = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_pmac_screamer_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int val0, val6; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + val0 = chip->awacs_reg[0] & ~MASK_GAINLINE; + val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST; + if (ucontrol->value.integer.value[0] > 0) { + val0 |= MASK_GAINLINE; + if (ucontrol->value.integer.value[0] > 1) + val6 |= MASK_MIC_BOOST; + } + if (val0 != chip->awacs_reg[0]) { + snd_pmac_awacs_write_reg(chip, 0, val0); + changed = 1; + } + if (val6 != chip->awacs_reg[6]) { + snd_pmac_awacs_write_reg(chip, 6, val6); + changed = 1; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return changed; +} + +/* + * lists of mixer elements + */ +static snd_kcontrol_new_t snd_pmac_awacs_mixers[] __initdata = { + AWACS_VOLUME("Master Playback Volume", 2, 6, 1), + AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), + AWACS_VOLUME("Capture Volume", 0, 4, 0), + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), + AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0), +}; + +static snd_kcontrol_new_t snd_pmac_awacs_master_sw __initdata = +AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1); + +static snd_kcontrol_new_t snd_pmac_awacs_mic_boost[] __initdata = { + AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0), +}; + +static snd_kcontrol_new_t snd_pmac_screamer_mic_boost[] __initdata = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Boost", + .info = snd_pmac_screamer_mic_boost_info, + .get = snd_pmac_screamer_mic_boost_get, + .put = snd_pmac_screamer_mic_boost_put, + }, +}; + +static snd_kcontrol_new_t snd_pmac_awacs_speaker_vol[] __initdata = { + AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1), +}; +static snd_kcontrol_new_t snd_pmac_awacs_speaker_sw __initdata = +AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); + + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +/* + * add new mixer elements to the card + */ +static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) +{ + int i, err; + + for (i = 0; i < nums; i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) + return err; + } + return 0; +} + + +/* + * restore all registers + */ +static void awacs_restore_all_regs(pmac_t *chip, int can_schedule) +{ + snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); + snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); + snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]); + snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]); + if (chip->model == PMAC_SCREAMER) { + snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]); + do_mdelay(100, can_schedule); + snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); + mdelay(2); + snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); + snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]); + snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); + } +} + +#ifdef CONFIG_PMAC_PBOOK +static void snd_pmac_awacs_resume(pmac_t *chip) +{ + awacs_restore_all_regs(chip, 0); + screamer_recalibrate(chip, 0); +#ifdef PMAC_AMP_AVAIL + if (chip->mixer_data) { + awacs_amp_t *amp = chip->mixer_data; + awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0); + awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0); + awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); + awacs_amp_set_master(amp, amp->amp_master); + } +#endif +} +#endif /* CONFIG_PMAC_PBOOK */ + +#ifdef PMAC_SUPPORT_AUTOMUTE +/* + * auto-mute stuffs + */ +static int snd_pmac_awacs_detect_headphone(pmac_t *chip) +{ + return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; +} + +#ifdef PMAC_AMP_AVAIL +static int toggle_amp_mute(awacs_amp_t *amp, int index, int mute) +{ + int vol[2]; + vol[0] = amp->amp_vol[index][0] & 31; + vol[1] = amp->amp_vol[index][1] & 31; + if (mute) { + vol[0] |= 32; + vol[1] |= 32; + } + return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); +} +#endif + +static void snd_pmac_awacs_update_automute(pmac_t *chip, int do_notify) +{ + if (chip->auto_mute) { +#ifdef PMAC_AMP_AVAIL + if (chip->mixer_data) { + awacs_amp_t *amp = chip->mixer_data; + int changed; + if (snd_pmac_awacs_detect_headphone(chip)) { + changed = toggle_amp_mute(amp, AMP_CH_HD, 0); + changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1); + } else { + changed = toggle_amp_mute(amp, AMP_CH_HD, 1); + changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0); + } + if (do_notify && ! changed) + return; + } else +#endif + { + int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE); + if (snd_pmac_awacs_detect_headphone(chip)) + reg &= ~MASK_HDMUTE; + else + reg &= ~MASK_SPKMUTE; + if (do_notify && reg == chip->awacs_reg[1]) + return; + snd_pmac_awacs_write_reg(chip, 1, reg); + } + if (do_notify) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_sw_ctl->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->speaker_sw_ctl->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->hp_detect_ctl->id); + } + } +} +#endif /* PMAC_SUPPORT_AUTOMUTE */ + + +/* + * initialize chip + */ +int __init +snd_pmac_awacs_init(pmac_t *chip) +{ + int err, vol; + + /* looks like MASK_GAINLINE triggers something, so we set here + * as start-up + */ + chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE; + chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE; + /* FIXME: Only machines with external SRS module need MASK_PAROUT */ + if (chip->has_iic || chip->device_id == 0x5 || + /*chip->_device_id == 0x8 || */ + chip->device_id == 0xb) + chip->awacs_reg[1] |= MASK_PAROUT; + /* get default volume from nvram */ + // vol = (~nvram_read_byte(0x1308) & 7) << 1; + // vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 ); + vol = 0x0f; /* no, on alsa, muted as default */ + vol = vol + (vol << 6); + chip->awacs_reg[2] = vol; + chip->awacs_reg[4] = vol; + if (chip->model == PMAC_SCREAMER) { + chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */ + chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ + chip->awacs_reg[7] = 0; + } + + awacs_restore_all_regs(chip, 1); + screamer_recalibrate(chip, 1); + + chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf; +#ifdef PMAC_AMP_AVAIL + if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) { + awacs_amp_t *amp = kmalloc(sizeof(*amp), GFP_KERNEL); + if (! amp) + return -ENOMEM; + chip->mixer_data = amp; + memset(amp, 0, sizeof(*amp)); + chip->mixer_free = awacs_amp_free; + awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */ + awacs_amp_set_vol(amp, 1, 63, 63, 0); + awacs_amp_set_tone(amp, 7, 7); /* 0 dB */ + awacs_amp_set_master(amp, 79); /* 0 dB */ + } +#endif /* PMAC_AMP_AVAIL */ + + if (chip->hp_stat_mask == 0) { + /* set headphone-jack detection bit */ + switch (chip->model) { + case PMAC_AWACS: + chip->hp_stat_mask = 0x04; + break; + case PMAC_SCREAMER: + switch (chip->device_id) { + case 0x08: + /* 1 = side jack, 2 = front jack */ + chip->hp_stat_mask = 0x03; + break; + case 0x00: + case 0x05: + chip->hp_stat_mask = 0x04; + break; + default: + chip->hp_stat_mask = 0x08; + break; + } + break; + default: + snd_BUG(); + break; + } + } + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac AWACS"); + + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers), + snd_pmac_awacs_mixers)) < 0) + return err; + chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + return err; +#ifdef PMAC_AMP_AVAIL + if (chip->mixer_data) { + /* use amplifier. the signal is connected from route A + * to the amp. the amp has its headphone and speaker + * volumes and mute switches, so we use them instead of + * screamer registers. + * in this case, it seems the route C is not used. + */ + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_amp_vol), + snd_pmac_awacs_amp_vol)) < 0) + return err; + /* overwrite */ + chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + return err; + chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + return err; + } else +#endif /* PMAC_AMP_AVAIL */ + { + /* route A = headphone, route C = speaker */ + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_speaker_vol), + snd_pmac_awacs_speaker_vol)) < 0) + return err; + chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + return err; + } + + if (chip->model == PMAC_SCREAMER) { + if ((err = build_mixers(chip, num_controls(snd_pmac_screamer_mic_boost), + snd_pmac_screamer_mic_boost)) < 0) + return err; + } else { + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mic_boost), + snd_pmac_awacs_mic_boost)) < 0) + return err; + } + + /* + * set lowlevel callbacks + */ + chip->set_format = snd_pmac_awacs_set_format; +#ifdef CONFIG_PMAC_PBOOK + chip->resume = snd_pmac_awacs_resume; +#endif +#ifdef PMAC_SUPPORT_AUTOMUTE + if ((err = snd_pmac_add_automute(chip)) < 0) + return err; + chip->detect_headphone = snd_pmac_awacs_detect_headphone; + chip->update_automute = snd_pmac_awacs_update_automute; + snd_pmac_awacs_update_automute(chip, 0); /* update the status only */ +#endif + if (chip->model == PMAC_SCREAMER) { + snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); + snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); + } + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/ppc/awacs.h linux/sound/ppc/awacs.h --- linux-2.4.21-rc1.orig/sound/ppc/awacs.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/awacs.h 2002-05-15 10:31:40.000000000 -0600 @@ -0,0 +1,192 @@ +/* + * Driver for PowerMac AWACS onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __AWACS_H +#define __AWACS_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 + +/* additional registers of screamer */ +#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ +#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ +#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ + +/* 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) /* Disable Mic preamp */ +#define MASK_GAINMIC (0x0 << 8) /* Enable Mic preamp */ +#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 SHIFT_GAINLINE 8 +#define SHIFT_MUX_CD 9 +#define SHIFT_MUX_MIC 10 +#define SHIFT_MUX_LINE 11 + +#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 SHIFT_LOOPTHRU 6 +#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ +#define MASK_SPKMUTE MASK_CMUTE +#define SHIFT_SPKMUTE 7 +#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ +#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ +#define MASK_HDMUTE MASK_AMUTE +#define SHIFT_HDMUTE 9 +#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) + +/* address 6 */ +#define MASK_MIC_BOOST (0x4) /* screamer mic boost */ +#define SHIFT_MIC_BOOST 2 + +/* 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_H */ diff -urN linux-2.4.21-rc1.orig/sound/ppc/burgundy.c linux/sound/ppc/burgundy.c --- linux-2.4.21-rc1.orig/sound/ppc/burgundy.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/burgundy.c 2002-08-16 09:08:49.000000000 -0600 @@ -0,0 +1,434 @@ +/* + * PMac Burgundy lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "pmac.h" +#include "burgundy.h" + +#define chip_t pmac_t + + +/* Waits for busy flag to clear */ +inline static void +snd_pmac_burgundy_busy_wait(pmac_t *chip) +{ + while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +snd_pmac_burgundy_extend_wait(pmac_t *chip) +{ + while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val) +{ + out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); +} + +static unsigned +snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + spin_lock_irqsave(&chip->reg_lock, flags); + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100100); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100200); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100300); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24; + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return val; +} + +static void +snd_pmac_burgundy_wcb(pmac_t *chip, unsigned int addr, unsigned int val) +{ + out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + snd_pmac_burgundy_busy_wait(chip); +} + +static unsigned +snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + spin_lock_irqsave(&chip->reg_lock, flags); + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return val; +} + +/* + * Burgundy volume: 0 - 100, stereo + */ +static void +snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +{ + int hardvolume, lvolume, rvolume; + + lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; + rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; + + hardvolume = lvolume + (rvolume << shift); + if (shift == 8) + hardvolume |= hardvolume << 16; + + snd_pmac_burgundy_wcw(chip, address, hardvolume); +} + +static void +snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +{ + int wvolume; + + wvolume = snd_pmac_burgundy_rcw(chip, address); + + volume[0] = wvolume & 0xff; + if (volume[0] >= BURGUNDY_VOLUME_OFFSET) + volume[0] -= BURGUNDY_VOLUME_OFFSET; + else + volume[0] = 0; + volume[1] = (wvolume >> shift) & 0xff; + if (volume[1] >= BURGUNDY_VOLUME_OFFSET) + volume[1] -= BURGUNDY_VOLUME_OFFSET; + else + volume[1] = 0; +} + + +/* + */ + +#define BASE2ADDR(base) ((base) << 12) +#define ADDR2BASE(addr) ((addr) >> 12) + +static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_burgundy_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int shift = (kcontrol->private_value >> 8) & 0xff; + snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, shift); + return 0; +} + +static int snd_pmac_burgundy_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int shift = (kcontrol->private_value >> 8) & 0xff; + long nvoices[2]; + + snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, shift); + snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); + return (nvoices[0] != ucontrol->value.integer.value[0] || + nvoices[1] != ucontrol->value.integer.value[1]); +} + +#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_volume,\ + .get = snd_pmac_burgundy_get_volume,\ + .put = snd_pmac_burgundy_put_volume,\ + .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } + +/* lineout/speaker */ + +static int snd_pmac_burgundy_info_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_burgundy_get_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; + if (stereo) + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; + return 0; +} + +static int snd_pmac_burgundy_put_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val, oval; + oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + val = oval & ~(lmask | rmask); + if (ucontrol->value.integer.value[0]) + val |= lmask; + if (stereo && ucontrol->value.integer.value[1]) + val |= rmask; + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); + return val != oval; +} + +#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_switch_out,\ + .get = snd_pmac_burgundy_get_switch_out,\ + .put = snd_pmac_burgundy_put_switch_out,\ + .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } + +/* line/speaker output volume */ +static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_burgundy_get_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int oval; + + oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + ucontrol->value.integer.value[0] = oval & 0xf; + if (stereo) + ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; + return 0; +} + +static int snd_pmac_burgundy_put_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int oval, val; + + oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + val = ucontrol->value.integer.value[0]; + if (stereo) + val |= ucontrol->value.integer.value[1] << 4; + else + val |= ucontrol->value.integer.value[0] << 4; + val = ~val & 0xff; + snd_pmac_burgundy_wcb(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_volume_out,\ + .get = snd_pmac_burgundy_get_volume_out,\ + .put = snd_pmac_burgundy_put_volume_out,\ + .private_value = (ADDR2BASE(addr) | ((stereo) << 24)) } + +static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] __initdata = { + BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), + BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16), + BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16), + BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0), + /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/ + BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1), +}; +static snd_kcontrol_new_t snd_pmac_burgundy_master_sw __initdata = +BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static snd_kcontrol_new_t snd_pmac_burgundy_speaker_sw __initdata = +BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0); + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + + +#ifdef PMAC_SUPPORT_AUTOMUTE +/* + * auto-mute stuffs + */ +static int snd_pmac_burgundy_detect_headphone(pmac_t *chip) +{ + return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; +} + +static void snd_pmac_burgundy_update_automute(pmac_t *chip, int do_notify) +{ + if (chip->auto_mute) { + int reg, oreg; + reg = oreg = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + reg &= ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT | BURGUNDY_OUTPUT_INTERN); + if (snd_pmac_burgundy_detect_headphone(chip)) + reg |= BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT; + else + reg |= BURGUNDY_OUTPUT_INTERN; + if (do_notify && reg == oreg) + return; + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); + if (do_notify) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_sw_ctl->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->speaker_sw_ctl->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->hp_detect_ctl->id); + } + } +} +#endif /* PMAC_SUPPORT_AUTOMUTE */ + + +/* + * initialize burgundy + */ +int __init snd_pmac_burgundy_init(pmac_t *chip) +{ + int i, err; + + /* Checks to see the chip is alive and kicking */ + if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) { + printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n"); + return 1; + } + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + + if (chip->hp_stat_mask == 0) + /* set headphone-jack detection bit */ + chip->hp_stat_mask = 0x04; + + /* + * build burgundy mixers + */ + strcpy(chip->card->mixername, "PowerMac Burgundy"); + + for (i = 0; i < num_controls(snd_pmac_burgundy_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) + return err; + } + chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_master_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + return err; + chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_speaker_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + return err; +#ifdef PMAC_SUPPORT_AUTOMUTE + if ((err = snd_pmac_add_automute(chip)) < 0) + return err; + + chip->detect_headphone = snd_pmac_burgundy_detect_headphone; + chip->update_automute = snd_pmac_burgundy_update_automute; + snd_pmac_burgundy_update_automute(chip, 0); /* update the status only */ +#endif + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/ppc/burgundy.h linux/sound/ppc/burgundy.h --- linux-2.4.21-rc1.orig/sound/ppc/burgundy.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/burgundy.h 2001-12-30 02:26:48.000000000 -0700 @@ -0,0 +1,95 @@ +/* + * Driver for PowerMac Burgundy onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __BURGUNDY_H +#define __BURGUNDY_H + +#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) +#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) + +#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) + +#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) + +#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) + +#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) + +#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) +#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) +#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) +#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) +#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) +#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + +#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) +#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) +#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) +#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + + +/* These are all default values for the burgundy */ +#define DEF_BURGUNDY_INPSEL21 (0xAA) +#define DEF_BURGUNDY_INPSEL3 (0x0A) + +#define DEF_BURGUNDY_GAINCD (0x33) +#define DEF_BURGUNDY_GAINLINE (0x44) +#define DEF_BURGUNDY_GAINMIC (0x44) +#define DEF_BURGUNDY_GAINMODEM (0x06) + +/* Remember: lowest volume here is 0x9b */ +#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) +#define DEF_BURGUNDY_VOLLINE (0x00000000) +#define DEF_BURGUNDY_VOLMIC (0x00000000) +#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) + +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) + +/* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */ +#define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD) + +#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) + +#define DEF_BURGUNDY_ATTENSPEAKER (0x44) +#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) +#define DEF_BURGUNDY_ATTENHP (0xCC) + +/* OUTPUTENABLES bits */ +#define BURGUNDY_OUTPUT_LEFT 0x02 +#define BURGUNDY_OUTPUT_RIGHT 0x04 +#define BURGUNDY_OUTPUT_INTERN 0x80 + +/* volume offset */ +#define BURGUNDY_VOLUME_OFFSET 155 + +#endif /* __BURGUNDY_H */ diff -urN linux-2.4.21-rc1.orig/sound/ppc/daca.c linux/sound/ppc/daca.c --- linux-2.4.21-rc1.orig/sound/ppc/daca.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/daca.c 2002-08-16 09:08:49.000000000 -0600 @@ -0,0 +1,284 @@ +/* + * PMac DACA lowlevel functions + * + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "pmac.h" + +#define chip_t pmac_t + +/* i2c address */ +#define DACA_I2C_ADDR 0x4d + +/* registers */ +#define DACA_REG_SR 0x01 +#define DACA_REG_AVOL 0x02 +#define DACA_REG_GCFG 0x03 + +/* maximum volume value */ +#define DACA_VOL_MAX 0x38 + + +typedef struct pmac_daca_t { + pmac_keywest_t i2c; + int left_vol, right_vol; + unsigned int deemphasis : 1; + unsigned int amp_on : 1; +} pmac_daca_t; + + +/* + * initialize / detect DACA + */ +static int daca_init_client(pmac_keywest_t *i2c) +{ + unsigned short wdata = 0x00; + /* SR: no swap, 1bit delay, 32-48kHz */ + /* GCFG: power amp inverted, DAC on */ + if (snd_pmac_keywest_write_byte(i2c, DACA_REG_SR, 0x08) < 0 || + snd_pmac_keywest_write_byte(i2c, DACA_REG_GCFG, 0x05) < 0) + return -EINVAL; + return snd_pmac_keywest_write(i2c, DACA_REG_AVOL, 2, (unsigned char*)&wdata); +} + +/* + * update volume + */ +static int daca_set_volume(pmac_daca_t *mix) +{ + unsigned char data[2]; + + if (! mix->i2c.client) + return -ENODEV; + + if (mix->right_vol > DACA_VOL_MAX) + data[0] = DACA_VOL_MAX; + else + data[0] = mix->right_vol; + if (mix->left_vol > DACA_VOL_MAX) + data[1] = DACA_VOL_MAX; + else + data[1] = mix->left_vol; + data[1] |= mix->deemphasis ? 0x40 : 0; + if (snd_pmac_keywest_write(&mix->i2c, DACA_REG_AVOL, 2, data) < 0) { + snd_printk("failed to set volume \n"); + return -EINVAL; + } + return 0; +} + + +/* deemphasis switch */ +static int daca_info_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int daca_get_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0; + return 0; +} + +static int daca_put_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->deemphasis != ucontrol->value.integer.value[0]; + if (change) { + mix->deemphasis = ucontrol->value.integer.value[0]; + daca_set_volume(mix); + } + return change; +} + +/* output volume */ +static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = DACA_VOL_MAX; + return 0; +} + +static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->left_vol; + ucontrol->value.integer.value[1] = mix->right_vol; + return 0; +} + +static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->left_vol != ucontrol->value.integer.value[0] || + mix->right_vol != ucontrol->value.integer.value[1]; + if (change) { + mix->left_vol = ucontrol->value.integer.value[0]; + mix->right_vol = ucontrol->value.integer.value[1]; + daca_set_volume(mix); + } + return change; +} + +/* amplifier switch */ +#define daca_info_amp daca_info_deemphasis + +static int daca_get_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0; + return 0; +} + +static int daca_put_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->amp_on != ucontrol->value.integer.value[0]; + if (change) { + mix->amp_on = ucontrol->value.integer.value[0]; + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG, + mix->amp_on ? 0x05 : 0x04); + } + return change; +} + +static snd_kcontrol_new_t daca_mixers[] = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Deemphasis Switch", + .info = daca_info_deemphasis, + .get = daca_get_deemphasis, + .put = daca_put_deemphasis + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = daca_info_volume, + .get = daca_get_volume, + .put = daca_put_volume + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Power Amplifier Switch", + .info = daca_info_amp, + .get = daca_get_amp, + .put = daca_put_amp + }, +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + + +#ifdef CONFIG_PMAC_PBOOK +static void daca_resume(pmac_t *chip) +{ + pmac_daca_t *mix = chip->mixer_data; + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_SR, 0x08); + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG, + mix->amp_on ? 0x05 : 0x04); + daca_set_volume(mix); +} +#endif /* CONFIG_PMAC_PBOOK */ + + +static void daca_cleanup(pmac_t *chip) +{ + pmac_daca_t *mix = chip->mixer_data; + if (! mix) + return; + snd_pmac_keywest_cleanup(&mix->i2c); + kfree(mix); + chip->mixer_data = NULL; +} + +/* exported */ +int __init snd_pmac_daca_init(pmac_t *chip) +{ + int i, err; + pmac_daca_t *mix; + +#ifdef CONFIG_KMOD + request_module("i2c-keywest"); +#endif /* CONFIG_KMOD */ + + mix = kmalloc(sizeof(*mix), GFP_KERNEL); + if (! mix) + return -ENOMEM; + memset(mix, 0, sizeof(*mix)); + chip->mixer_data = mix; + chip->mixer_free = daca_cleanup; + mix->amp_on = 1; /* default on */ + + mix->i2c.addr = DACA_I2C_ADDR; + mix->i2c.init_client = daca_init_client; + mix->i2c.name = "DACA"; + if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0) + return err; + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac DACA"); + + for (i = 0; i < num_controls(daca_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0) + return err; + } + +#ifdef CONFIG_PMAC_PBOOK + chip->resume = daca_resume; +#endif + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/ppc/keywest.c linux/sound/ppc/keywest.c --- linux-2.4.21-rc1.orig/sound/ppc/keywest.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/keywest.c 2002-12-23 07:16:19.000000000 -0700 @@ -0,0 +1,134 @@ +/* + * common keywest i2c layer + * + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "pmac.h" + +/* + * we have to keep a static variable here since i2c attach_adapter + * callback cannot pass a private data. + */ +static pmac_keywest_t *keywest_ctx; + + +#define I2C_DRIVERID_KEYWEST 0xFEBA + +static int keywest_attach_adapter(struct i2c_adapter *adapter); +static int keywest_detach_client(struct i2c_client *client); + +struct i2c_driver keywest_driver = { + .name = "PMac Keywest Audio", + .id = I2C_DRIVERID_KEYWEST, + .flags = I2C_DF_NOTIFY, + .attach_adapter = &keywest_attach_adapter, + .detach_client = &keywest_detach_client, +}; + + +static int keywest_attach_adapter(struct i2c_adapter *adapter) +{ + int err; + struct i2c_client *new_client; + + if (! keywest_ctx) + return -EINVAL; + + if (strncmp(adapter->name, "mac-io", 6)) + return 0; /* ignored */ + + new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (! new_client) + return -ENOMEM; + + new_client->addr = keywest_ctx->addr; + new_client->data = keywest_ctx; + new_client->adapter = adapter; + new_client->driver = &keywest_driver; + new_client->flags = 0; + + strcpy(new_client->name, keywest_ctx->name); + + new_client->id = keywest_ctx->id++; /* Automatically unique */ + keywest_ctx->client = new_client; + + if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) { + snd_printk(KERN_ERR "tumbler: cannot initialize the MCS\n"); + goto __err; + } + + /* Tell the i2c layer a new client has arrived */ + if (i2c_attach_client(new_client)) { + snd_printk(KERN_ERR "tumbler: cannot attach i2c client\n"); + err = -ENODEV; + goto __err; + } + + return 0; + + __err: + kfree(new_client); + keywest_ctx->client = NULL; + return err; +} + +static int keywest_detach_client(struct i2c_client *client) +{ + if (! keywest_ctx) + return 0; + if (client == keywest_ctx->client) + keywest_ctx->client = NULL; + + i2c_detach_client(client); + kfree(client); + return 0; +} + +/* exported */ +void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c) +{ + if (keywest_ctx && keywest_ctx == i2c) { + i2c_del_driver(&keywest_driver); + keywest_ctx = NULL; + } +} + +/* exported */ +int __init snd_pmac_keywest_init(pmac_keywest_t *i2c) +{ + int err; + + if (keywest_ctx) + return -EBUSY; + + keywest_ctx = i2c; + + if ((err = i2c_add_driver(&keywest_driver))) { + snd_printk(KERN_ERR "cannot register keywest i2c driver\n"); + return err; + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/ppc/pmac.c linux/sound/ppc/pmac.c --- linux-2.4.21-rc1.orig/sound/ppc/pmac.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/pmac.c 2003-02-09 10:25:43.000000000 -0700 @@ -0,0 +1,1582 @@ +/* + * PMac DBDMA lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmac.h" +#include +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS +#include +#else +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define pmu_suspend() /**/ +#define pmu_resume() /**/ +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18) +#define request_OF_resource(io,num,str) 1 +#define release_OF_resource(io,num) /**/ +#endif + + +#define chip_t pmac_t + + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +static int snd_pmac_register_sleep_notifier(pmac_t *chip); +static int snd_pmac_unregister_sleep_notifier(pmac_t *chip); +static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state); +#endif + + +/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */ +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; +/* fixed frequency table for tumbler */ +static int tumbler_freqs[2] = { + 48000, 44100 +}; + +/* + * allocate DBDMA command arrays + */ +static int snd_pmac_dbdma_alloc(pmac_dbdma_t *rec, int size) +{ + rec->space = kmalloc(sizeof(struct dbdma_cmd) * (size + 1), GFP_KERNEL); + if (rec->space == NULL) + return -ENOMEM; + rec->size = size; + memset(rec->space, 0, sizeof(struct dbdma_cmd) * (size + 1)); + rec->cmds = (void*)DBDMA_ALIGN(rec->space); + rec->addr = virt_to_bus(rec->cmds); + return 0; +} + +static void snd_pmac_dbdma_free(pmac_dbdma_t *rec) +{ + if (rec && rec->space) + kfree(rec->space); +} + + +/* + * pcm stuff + */ + +/* + * look up frequency table + */ + +static unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) +{ + int i, ok, found; + + ok = rec->cur_freqs; + if (rate > chip->freq_table[0]) + return 0; + found = 0; + for (i = 0; i < chip->num_freqs; i++, ok >>= 1) { + if (! (ok & 1)) continue; + found = i; + if (rate >= chip->freq_table[i]) + break; + } + return found; +} + +/* + * check whether another stream is active + */ +static inline int another_stream(int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; +} + +/* + * allocate buffers + */ +static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params)); +} + +/* + * release buffers + */ +static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs) +{ + snd_pcm_lib_free_pages(subs); + return 0; +} + +/* + * get a stream of the opposite direction + */ +static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) +{ + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + return &chip->playback; + case SNDRV_PCM_STREAM_CAPTURE: + return &chip->capture; + default: + snd_BUG(); + return NULL; + } +} + +/* + * wait while run status is on + */ +inline static void +snd_pmac_wait_ack(pmac_stream_t *rec) +{ + int timeout = 50000; + while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0) + udelay(1); +} + +/* + * set the format and rate to the chip. + * call the lowlevel function if defined (e.g. for AWACS). + */ +static void snd_pmac_pcm_set_format(pmac_t *chip) +{ + /* set up frequency and format */ + out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8)); + out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE ? 1 : 0); + if (chip->set_format) + chip->set_format(chip); +} + +/* + * stop the DMA transfer + */ +inline static void snd_pmac_dma_stop(pmac_stream_t *rec) +{ + out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + snd_pmac_wait_ack(rec); +} + +/* + * set the command pointer address + */ +inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) +{ + out_le32(&rec->dma->cmdptr, cmd->addr); +} + +/* + * start the DMA + */ +inline static void snd_pmac_dma_run(pmac_stream_t *rec, int status) +{ + out_le32(&rec->dma->control, status | (status << 16)); +} + + +/* + * prepare playback/capture stream + */ +static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + int i; + volatile struct dbdma_cmd *cp; + unsigned long flags; + snd_pcm_runtime_t *runtime = subs->runtime; + int rate_index; + long offset; + pmac_stream_t *astr; + + rec->dma_size = snd_pcm_lib_buffer_bytes(subs); + rec->period_size = snd_pcm_lib_period_bytes(subs); + rec->nperiods = rec->dma_size / rec->period_size; + rec->cur_period = 0; + rate_index = snd_pmac_rate_index(chip, rec, runtime->rate); + + /* set up constraints */ + astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); + snd_runtime_check(astr, return -EINVAL); + astr->cur_freqs = 1 << rate_index; + astr->cur_formats = 1 << runtime->format; + chip->rate_index = rate_index; + chip->format = runtime->format; + + /* We really want to execute a DMA stop command, after the AWACS + * is initialized. + * For reasons I don't understand, it stops the hissing noise + * common to many PowerBook G3 systems (like mine :-). + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_dma_stop(rec); + if (rec->stream == SNDRV_PCM_STREAM_PLAYBACK) { + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + } + /* continuous DMA memory type doesn't provide the physical address, + * so we need to resolve the address here... + */ + offset = virt_to_bus(runtime->dma_area); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) { + st_le32(&cp->phy_addr, offset); + st_le16(&cp->req_count, rec->period_size); + /*st_le16(&cp->res_count, 0);*/ + st_le16(&cp->xfer_status, 0); + offset += rec->period_size; + } + /* make loop */ + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, rec->cmd.addr); + + snd_pmac_dma_stop(rec); + snd_pmac_dma_set_command(rec, &rec->cmd); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + + +/* + * stop beep if running (no spinlock!) + */ +static void snd_pmac_beep_stop(pmac_t *chip) +{ + pmac_beep_t *beep = chip->beep; + + if (beep && beep->running) { + beep->running = 0; + del_timer(&beep->timer); + snd_pmac_dma_stop(&chip->playback); + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + } +} + +/* + * PCM trigger/stop + */ +static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, + snd_pcm_substream_t *subs, int cmd) +{ + unsigned long flags; + volatile struct dbdma_cmd *cp; + int i, command; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (rec->running) + return -EBUSY; + command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ? + OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_beep_stop(chip); + snd_pmac_pcm_set_format(chip); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) + out_le16(&cp->command, command); + snd_pmac_dma_set_command(rec, &rec->cmd); + (void)in_le32(&rec->dma->status); + snd_pmac_dma_run(rec, RUN|WAKE); + rec->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + spin_lock_irqsave(&chip->reg_lock, flags); + rec->running = 0; + /*printk("stopped!!\n");*/ + snd_pmac_dma_stop(rec); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) + out_le16(&cp->command, DBDMA_STOP); + spin_unlock_irqrestore(&chip->reg_lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * return the current pointer + */ +inline +static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, + snd_pcm_substream_t *subs) +{ + int count = 0; + +#if 1 /* hmm.. how can we get the current dma pointer?? */ + int stat; + volatile struct dbdma_cmd *cp = &rec->cmd.cmds[rec->cur_period]; + stat = ld_le16(&cp->xfer_status); + if (stat & (ACTIVE|DEAD)) { + count = in_le16(&cp->res_count); + count = rec->period_size - count; + } +#endif + count += rec->cur_period * rec->period_size; + /*printk("pointer=%d\n", count);*/ + return bytes_to_frames(subs->runtime, count); +} + +/* + * playback + */ + +static int snd_pmac_playback_prepare(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_prepare(chip, &chip->playback, subs); +} + +static int snd_pmac_playback_trigger(snd_pcm_substream_t *subs, + int cmd) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd); +} + +static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_pointer(chip, &chip->playback, subs); +} + + +/* + * capture + */ + +static int snd_pmac_capture_prepare(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_prepare(chip, &chip->capture, subs); +} + +static int snd_pmac_capture_trigger(snd_pcm_substream_t *subs, + int cmd) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd); +} + +static snd_pcm_uframes_t snd_pmac_capture_pointer(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_pointer(chip, &chip->capture, subs); +} + + +/* + * update playback/capture pointer from interrupts + */ +static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) +{ + volatile struct dbdma_cmd *cp; + int c; + int stat; + + spin_lock(&chip->reg_lock); + if (rec->running) { + cp = &rec->cmd.cmds[rec->cur_period]; + for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ + stat = ld_le16(&cp->xfer_status); + if (! (stat & ACTIVE)) + break; + /*printk("update frag %d\n", rec->cur_period);*/ + st_le16(&cp->xfer_status, 0); + st_le16(&cp->req_count, rec->period_size); + /*st_le16(&cp->res_count, 0);*/ + rec->cur_period++; + if (rec->cur_period >= rec->nperiods) { + rec->cur_period = 0; + cp = rec->cmd.cmds; + } else + cp++; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(rec->substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + + +/* + * hw info + */ + +static snd_pcm_hardware_t snd_pmac_playback = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_44100, + .rate_min = 7350, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 256, + .period_bytes_max = 16384, + .periods_min = 1, + .periods_max = PMAC_MAX_FRAGS, +}; + +static snd_pcm_hardware_t snd_pmac_capture = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_44100, + .rate_min = 7350, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 256, + .period_bytes_max = 16384, + .periods_min = 1, + .periods_max = PMAC_MAX_FRAGS, +}; + + +#if 0 // NYI +static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + pmac_t *chip = rule->private; + pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + int i, freq_table[8], num_freqs; + + snd_runtime_check(rec, return -EINVAL); + num_freqs = 0; + for (i = chip->num_freqs - 1; i >= 0; i--) { + if (rec->cur_freqs & (1 << i)) + freq_table[num_freqs++] = chip->freq_table[i]; + } + + return snd_interval_list(hw_param_interval(params, rule->var), + num_freqs, freq_table, 0); +} + +static int snd_pmac_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + pmac_t *chip = rule->private; + pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + + snd_runtime_check(rec, return -EINVAL); + return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + rec->cur_formats); +} +#endif // NYI + +static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + int i, j, fflags; + static int typical_freqs[] = { + 48000, + 44100, + 22050, + 11025, + 0, + }; + static int typical_freq_flags[] = { + SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_44100, + SNDRV_PCM_RATE_22050, + SNDRV_PCM_RATE_11025, + 0, + }; + + /* look up frequency table and fill bit mask */ + runtime->hw.rates = 0; + fflags = chip->freqs_ok; + for (i = 0; typical_freqs[i]; i++) { + for (j = 0; j < chip->num_freqs; j++) { + if ((chip->freqs_ok & (1 << j)) && + chip->freq_table[j] == typical_freqs[i]) { + runtime->hw.rates |= typical_freq_flags[i]; + fflags &= ~(1 << j); + break; + } + } + } + if (fflags) /* rest */ + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + + /* check for minimum and maximum rates */ + for (i = 0; i < chip->num_freqs; i++) { + if (chip->freqs_ok & (1 << i)) { + runtime->hw.rate_max = chip->freq_table[i]; + break; + } + } + for (i = chip->num_freqs - 1; i >= 0; i--) { + if (chip->freqs_ok & (1 << i)) { + runtime->hw.rate_min = chip->freq_table[i]; + break; + } + } + runtime->hw.formats = chip->formats_ok; + if (chip->can_capture) { + if (! chip->can_duplex) + runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + } + runtime->private_data = rec; + rec->substream = subs; + +#if 0 /* FIXME: still under development.. */ + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pmac_hw_rule_rate, chip, rec->stream, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pmac_hw_rule_format, chip, rec->stream, -1); +#endif + + runtime->hw.periods_max = rec->cmd.size - 1; + + if (chip->can_duplex) + snd_pcm_set_sync(subs); + + return 0; +} + +static int snd_pmac_pcm_close(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + pmac_stream_t *astr; + + snd_pmac_dma_stop(rec); + + astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); + snd_runtime_check(astr, return -EINVAL); + + /* reset constraints */ + astr->cur_freqs = chip->freqs_ok; + astr->cur_formats = chip->formats_ok; + + return 0; +} + +static int snd_pmac_playback_open(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + subs->runtime->hw = snd_pmac_playback; + return snd_pmac_pcm_open(chip, &chip->playback, subs); +} + +static int snd_pmac_capture_open(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + subs->runtime->hw = snd_pmac_capture; + return snd_pmac_pcm_open(chip, &chip->capture, subs); +} + +static int snd_pmac_playback_close(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + return snd_pmac_pcm_close(chip, &chip->playback, subs); +} + +static int snd_pmac_capture_close(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + return snd_pmac_pcm_close(chip, &chip->capture, subs); +} + +/* + */ + +static snd_pcm_ops_t snd_pmac_playback_ops = { + .open = snd_pmac_playback_open, + .close = snd_pmac_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_pmac_pcm_hw_params, + .hw_free = snd_pmac_pcm_hw_free, + .prepare = snd_pmac_playback_prepare, + .trigger = snd_pmac_playback_trigger, + .pointer = snd_pmac_playback_pointer, +}; + +static snd_pcm_ops_t snd_pmac_capture_ops = { + .open = snd_pmac_capture_open, + .close = snd_pmac_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_pmac_pcm_hw_params, + .hw_free = snd_pmac_pcm_hw_free, + .prepare = snd_pmac_capture_prepare, + .trigger = snd_pmac_capture_trigger, + .pointer = snd_pmac_capture_pointer, +}; + +static void pmac_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __init snd_pmac_pcm_new(pmac_t *chip) +{ + snd_pcm_t *pcm; + int err; + int num_captures = 1; + + if (! chip->can_capture) + num_captures = 0; + err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops); + if (chip->can_capture) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); + + pcm->private_data = chip; + pcm->private_free = pmac_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; + if (chip->can_byte_swap) + chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE; + + chip->playback.cur_formats = chip->formats_ok; + chip->capture.cur_formats = chip->formats_ok; + chip->playback.cur_freqs = chip->freqs_ok; + chip->capture.cur_freqs = chip->freqs_ok; + + /* preallocate 64k buffer */ + snd_pcm_lib_preallocate_pages_for_all(pcm, 64 * 1024, 64 * 1024, GFP_KERNEL); + + return 0; +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + +/* + * beep stuff + */ + +/* + * 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_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static void snd_pmac_beep_stop_callback(unsigned long data) +{ + pmac_t *chip = snd_magic_cast(pmac_t, (void*)data,); + + spin_lock(&chip->reg_lock); + snd_pmac_beep_stop(chip); + snd_pmac_pcm_set_format(chip); + spin_unlock(&chip->reg_lock); +} + +/* because mksound callback takes no private argument, we must keep + the chip pointer here as static variable. + This means that only one chip can beep. Well, it's ok - + anyway we don't like hearing loud beeps from every chip + at the same time :) +*/ + +static pmac_t *beeping_chip = NULL; + +static void snd_pmac_mksound(unsigned int hz, unsigned int ticks) +{ + pmac_t *chip; + pmac_stream_t *rec; + pmac_beep_t *beep; + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + + if ((chip = beeping_chip) == NULL || (beep = chip->beep) == NULL) + return; + rec = &chip->playback; + + beep_speed = snd_pmac_rate_index(chip, rec, BEEP_SRATE); + srate = chip->freq_table[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->playback.running || chip->capture.running || beep->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + beep->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + if (hz == beep->hz && beep->volume == beep->volume_play) { + nsamples = beep->nsamples; + } 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 = hz; + beep->volume_play = beep->volume; + beep->nsamples = nsamples; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + if (beep->running) { + if (ticks <= 0) + ticks = 1; + del_timer(&beep->timer); + beep->timer.expires = jiffies + ticks; + beep->timer.function = snd_pmac_beep_stop_callback; + beep->timer.data = (unsigned long)chip; + add_timer(&beep->timer); + snd_pmac_dma_stop(rec); + st_le16(&chip->extra_dma.cmds->req_count, nsamples * 4); + st_le16(&chip->extra_dma.cmds->xfer_status, 0); + st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); + st_le32(&chip->extra_dma.cmds->phy_addr, beep->addr); + st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); + out_le32(&chip->awacs->control, + (in_le32(&chip->awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&chip->awacs->byteswap, 0); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * beep volume mixer + */ +static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + snd_runtime_check(chip->beep, return -ENXIO); + ucontrol->value.integer.value[0] = chip->beep->volume; + return 0; +} + +static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int oval; + snd_runtime_check(chip->beep, return -ENXIO); + oval = chip->beep->volume; + chip->beep->volume = ucontrol->value.integer.value[0]; + return oval != chip->beep->volume; +} + +static snd_kcontrol_new_t snd_pmac_beep_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Beep Playback Volume", + .index = 0, + .info = snd_pmac_info_beep, + .get = snd_pmac_get_beep, + .put = snd_pmac_put_beep, +}; + +static void snd_pmac_beep_free(snd_kcontrol_t *control) +{ + pmac_t *chip = snd_magic_cast(pmac_t, _snd_kcontrol_chip(control),); + if (chip->beep) { + /* restore */ + kd_mksound = chip->beep->orig_mksound; + kfree(chip->beep->buf); + kfree(chip->beep); + chip->beep = NULL; + } +} + +/* Initialize beep stuff */ +int __init snd_pmac_attach_beep(pmac_t *chip) +{ + pmac_beep_t *beep; + int err; + + beep = kmalloc(sizeof(*beep), GFP_KERNEL); + if (! beep) + return -ENOMEM; + + beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (! beep->buf) { + kfree(beep); + return -ENOMEM; + } + beep->addr = virt_to_bus(beep->buf); + init_timer(&beep->timer); + beep->timer.function = snd_pmac_beep_stop_callback; + beep->timer.data = (unsigned long) chip; + beep->orig_mksound = kd_mksound; + beep->volume = BEEP_VOLUME; + beep->running = 0; + beep->control = snd_ctl_new1(&snd_pmac_beep_mixer, chip); + if (beep->control == NULL) { + kfree(beep); + return -ENOMEM; + } + beep->control->private_free = snd_pmac_beep_free; + if ((err = snd_ctl_add(chip->card, beep->control)) < 0) { + kfree(beep); + return err; + } + + /* hook */ + beeping_chip = chip; + chip->beep = beep; + kd_mksound = snd_pmac_mksound; + + return 0; +} + +#endif /* beep stuff */ + +static void snd_pmac_dbdma_reset(pmac_t *chip) +{ + out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + snd_pmac_wait_ack(&chip->playback); + out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + snd_pmac_wait_ack(&chip->capture); +} + + +/* + * interrupt handlers + */ +static void +snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + snd_pmac_pcm_update(chip, &chip->playback); +} + + +static void +snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + snd_pmac_pcm_update(chip, &chip->capture); +} + + +static void +snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + int ctrl = in_le32(&chip->awacs->control); + + /*printk("pmac: control interrupt.. 0x%x\n", ctrl);*/ + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + if (chip->update_automute) + chip->update_automute(chip, 1); + } + if (ctrl & MASK_CNTLERR) { + int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err && chip->model <= PMAC_SCREAMER) + snd_printk(KERN_DEBUG "error %x\n", err); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&chip->awacs->control, ctrl); +} + + +/* + * a wrapper to feature call for compatibility + */ +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +static void snd_pmac_sound_feature(pmac_t *chip, int enable) +{ +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS + ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); +#else + if (chip->is_pbook_G3) { + pmu_suspend(); + feature_clear(chip->node, FEATURE_Sound_power); + feature_clear(chip->node, FEATURE_Sound_CLK_enable); + mdelay(1000); /* XXX */ + pmu_resume(); + } + if (chip->is_pbook_3400) { + feature_set(chip->node, FEATURE_IOBUS_enable); + udelay(10); + } +#endif +} +#else /* CONFIG_PM && CONFIG_PMAC_PBOOK */ +#define snd_pmac_sound_feature(chip,enable) /**/ +#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ + +/* + * release resources + */ + +static int snd_pmac_free(pmac_t *chip) +{ + int i; + + /* stop sounds */ + if (chip->initialized) { + snd_pmac_dbdma_reset(chip); + /* disable interrupts from awacs interface */ + out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); + } + + snd_pmac_sound_feature(chip, 0); +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + snd_pmac_unregister_sleep_notifier(chip); +#endif + + /* clean up mixer if any */ + if (chip->mixer_free) + chip->mixer_free(chip); + + /* release resources */ + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + if (chip->tx_irq >= 0) + free_irq(chip->tx_irq, (void*)chip); + if (chip->rx_irq >= 0) + free_irq(chip->rx_irq, (void*)chip); + snd_pmac_dbdma_free(&chip->playback.cmd); + snd_pmac_dbdma_free(&chip->capture.cmd); + snd_pmac_dbdma_free(&chip->extra_dma); + if (chip->macio_base) + iounmap(chip->macio_base); + if (chip->latch_base) + iounmap(chip->latch_base); + if (chip->awacs) + iounmap((void*)chip->awacs); + if (chip->playback.dma) + iounmap((void*)chip->playback.dma); + if (chip->capture.dma) + iounmap((void*)chip->capture.dma); + if (chip->node) { + for (i = 0; i < 3; i++) { + if (chip->of_requested & (1 << i)) + release_OF_resource(chip->node, i); + } + } + snd_magic_kfree(chip); + return 0; +} + + +/* + * free the device + */ +static int snd_pmac_dev_free(snd_device_t *device) +{ + pmac_t *chip = snd_magic_cast(pmac_t, device->device_data, return -ENXIO); + return snd_pmac_free(chip); +} + + +/* + * check the machine support byteswap (little-endian) + */ + +static void __init detect_byte_swap(pmac_t *chip) +{ + struct device_node *mio; + + /* if seems that Keylargo can't byte-swap */ + for (mio = chip->node->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0) { + if (device_is_compatible(mio, "Keylargo")) + chip->can_byte_swap = 0; + break; + } + } + + /* it seems the Pismo & iBook can't byte-swap in hardware. */ + if (machine_is_compatible("PowerBook3,1") || + machine_is_compatible("PowerBook2,1")) + chip->can_byte_swap = 0 ; + + if (machine_is_compatible("PowerBook2,1")) + chip->can_duplex = 0; +} + + +/* + * detect a sound chip + */ +static int __init snd_pmac_detect(pmac_t *chip) +{ + struct device_node *sound; + unsigned int *prop, l; + + if (_machine != _MACH_Pmac) + return -ENODEV; + + chip->subframe = 0; + chip->revision = 0; + chip->freqs_ok = 0xff; /* all ok */ + chip->model = PMAC_AWACS; + chip->can_byte_swap = 1; + chip->can_duplex = 1; + chip->can_capture = 1; + chip->num_freqs = 8; + chip->freq_table = awacs_freqs; + + chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ + + /* check machine type */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + chip->is_pbook_3400 = 1; + else if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) + chip->is_pbook_G3 = 1; + chip->node = find_devices("awacs"); + if (chip->node) + return 0; /* ok */ + + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + chip->node = find_devices("davbus"); + /* + * if we didn't find a davbus device, try 'i2s-a' since + * this seems to be what iBooks have + */ + if (! chip->node) + chip->node = find_devices("i2s-a"); + if (! chip->node) + return -ENODEV; + sound = find_devices("sound"); + while (sound && sound->parent != chip->node) + sound = sound->next; + if (! sound) + return -ENODEV; + prop = (unsigned int *) get_property(sound, "sub-frame", 0); + if (prop && *prop < 16) + chip->subframe = *prop; + /* This should be verified on older screamers */ + if (device_is_compatible(sound, "screamer")) { + chip->model = PMAC_SCREAMER; + // chip->can_byte_swap = 0; /* FIXME: check this */ + } + if (device_is_compatible(sound, "burgundy")) { + chip->model = PMAC_BURGUNDY; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "daca")) { + chip->model = PMAC_DACA; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + // chip->can_byte_swap = 0; /* FIXME: check this */ + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "tumbler")) { + chip->model = PMAC_TUMBLER; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + // chip->can_byte_swap = 0; /* FIXME: check this */ + chip->num_freqs = 2; + chip->freq_table = tumbler_freqs; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "snapper")) { + chip->model = PMAC_SNAPPER; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + // chip->can_byte_swap = 0; /* FIXME: check this */ + chip->num_freqs = 2; + chip->freq_table = tumbler_freqs; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + prop = (unsigned int *)get_property(sound, "device-id", 0); + if (prop) + chip->device_id = *prop; + chip->has_iic = (find_devices("perch") != NULL); + + detect_byte_swap(chip); + + /* look for a property saying what sample rates + are available */ + prop = (unsigned int *) get_property(sound, "sample-rates", &l); + if (! prop) + prop = (unsigned int *) get_property(sound, "output-frame-rates", &l); + if (prop) { + int i; + chip->freqs_ok = 0; + for (l /= sizeof(int); l > 0; --l) { + unsigned int r = *prop++; + /* Apple 'Fixed' format */ + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < chip->num_freqs; ++i) { + if (r == chip->freq_table[i]) { + chip->freqs_ok |= (1 << i); + break; + } + } + } + } else { + /* assume only 44.1khz */ + chip->freqs_ok = 1; + } + + return 0; +} + +/* + * exported - boolean info callbacks for ease of programming + */ +int snd_pmac_boolean_stereo_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +#ifdef PMAC_SUPPORT_AUTOMUTE +/* + * auto-mute + */ +static int pmac_auto_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->auto_mute; + return 0; +} + +static int pmac_auto_mute_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] != chip->auto_mute) { + chip->auto_mute = ucontrol->value.integer.value[0]; + if (chip->update_automute) + chip->update_automute(chip, 1); + return 1; + } + return 0; +} + +static int pmac_hp_detect_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + if (chip->detect_headphone) + ucontrol->value.integer.value[0] = chip->detect_headphone(chip); + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static snd_kcontrol_new_t auto_mute_controls[] __initdata = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Auto Mute Switch", + .info = snd_pmac_boolean_mono_info, + .get = pmac_auto_mute_get, + .put = pmac_auto_mute_put, + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Detection", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_pmac_boolean_mono_info, + .get = pmac_hp_detect_get, + }, +}; + +int __init snd_pmac_add_automute(pmac_t *chip) +{ + int err; + chip->auto_mute = 1; + err = snd_ctl_add(chip->card, snd_ctl_new1(&auto_mute_controls[0], chip)); + if (err < 0) + return err; + chip->hp_detect_ctl = snd_ctl_new1(&auto_mute_controls[1], chip); + return snd_ctl_add(chip->card, chip->hp_detect_ctl); +} +#endif /* PMAC_SUPPORT_AUTOMUTE */ + +/* + * create and detect a pmac chip record + */ +int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) +{ + pmac_t *chip; + struct device_node *np; + int i, err; + static snd_device_ops_t ops = { + .dev_free = snd_pmac_dev_free, + }; + + snd_runtime_check(chip_return, return -EINVAL); + *chip_return = NULL; + + chip = snd_magic_kcalloc(pmac_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->card = card; + + spin_lock_init(&chip->reg_lock); + chip->irq = chip->tx_irq = chip->rx_irq = -1; + + chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK; + chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE; + + if ((err = snd_pmac_detect(chip)) < 0) + goto __error; + + if (snd_pmac_dbdma_alloc(&chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || + snd_pmac_dbdma_alloc(&chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || + snd_pmac_dbdma_alloc(&chip->extra_dma, 2) < 0) { + err = -ENOMEM; + goto __error; + } + + np = chip->node; + if (np->n_addrs < 3 || np->n_intrs < 3) { + err = -ENODEV; + goto __error; + } + + for (i = 0; i < 3; i++) { + static char *name[3] = { NULL, "- Tx DMA", "- Rx DMA" }; + if (! request_OF_resource(np, i, name[i])) { + snd_printk(KERN_ERR "pmac: can't request resource %d!\n", i); + err = -ENODEV; + goto __error; + } + chip->of_requested |= (1 << i); + } + + chip->awacs = (volatile struct awacs_regs *) ioremap(np->addrs[0].address, 0x1000); + chip->playback.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[1].address, 0x100); + chip->capture.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[2].address, 0x100); + if (chip->model <= PMAC_BURGUNDY) { + if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0, + "PMac", (void*)chip)) { + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[0].line); + err = -EBUSY; + goto __error; + } + chip->irq = np->intrs[0].line; + } + if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0, + "PMac Output", (void*)chip)) { + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[1].line); + err = -EBUSY; + goto __error; + } + chip->tx_irq = np->intrs[1].line; + if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0, + "PMac Input", (void*)chip)) { + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[2].line); + err = -EBUSY; + goto __error; + } + chip->rx_irq = np->intrs[2].line; + + snd_pmac_sound_feature(chip, 1); + + /* reset */ + out_le32(&chip->awacs->control, 0x11); + + /* Powerbooks have odd ways of enabling inputs such as + an expansion-bay CD or sound from an internal modem + or a PC-card modem. */ + if (chip->is_pbook_3400) { + /* Enable CD and PC-card sound inputs. */ + /* This is done by reading from address + * f301a000, + 0x10 to enable the expansion-bay + * CD sound input, + 0x80 to enable the PC-card + * sound input. The 0x100 enables the SCSI bus + * terminator power. + */ + chip->latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000); + in_8(chip->latch_base + 0x190); + } else if (chip->is_pbook_G3) { + struct device_node* mio; + for (mio = chip->node->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) { + chip->macio_base = (unsigned char *) ioremap + (mio->addrs[0].address, 0x40); + break; + } + } + /* Enable CD sound input. */ + /* The relevant bits for writing to this byte are 0x8f. + * I haven't found out what the 0x80 bit does. + * For the 0xf bits, writing 3 or 7 enables the CD + * input, any other value disables it. Values + * 1, 3, 5, 7 enable the microphone. Values 0, 2, + * 4, 6, 8 - f enable the input from the modem. + */ + if (chip->macio_base) + out_8(chip->macio_base + 0x37, 3); + } + + /* Reset dbdma channels */ + snd_pmac_dbdma_reset(chip); + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + /* add sleep notifier */ + snd_pmac_register_sleep_notifier(chip); + card->set_power_state = snd_pmac_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; + + *chip_return = chip; + return 0; + + __error: + snd_pmac_free(chip); + return err; +} + + +/* + * sleep notify for powerbook + */ + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + +/* + * Save state when going to sleep, restore it afterwards. + */ + +static void snd_pmac_suspend(pmac_t *chip) +{ + unsigned long flags; + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + + if (chip->suspend) + chip->suspend(chip); + snd_pcm_suspend_all(chip->pcm); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->beep && chip->beep->running) + snd_pmac_beep_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + disable_irq(chip->irq); + disable_irq(chip->tx_irq); + disable_irq(chip->rx_irq); + snd_pmac_sound_feature(chip, 0); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static void snd_pmac_resume(pmac_t *chip) +{ + snd_card_t *card = chip->card; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + + snd_pmac_sound_feature(chip, 1); + if (chip->resume) + chip->resume(chip); + /* enable CD sound input */ + if (chip->macio_base && chip->is_pbook_G3) { + out_8(chip->macio_base + 0x37, 3); + } else if (chip->is_pbook_3400) { + in_8(chip->latch_base + 0x190); + } + + snd_pmac_pcm_set_format(chip); + + enable_irq(chip->irq); + enable_irq(chip->tx_irq); + enable_irq(chip->rx_irq); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +/* the chip is stored statically by snd_pmac_register_sleep_notifier + * because we can't have any private data for notify callback. + */ +static pmac_t *sleeping_pmac = NULL; + +static int snd_pmac_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + pmac_t *chip; + + chip = sleeping_pmac; + snd_runtime_check(chip, return 0); + + switch (when) { + case PBOOK_SLEEP_NOW: + snd_pmac_suspend(chip); + break; + case PBOOK_WAKE: + snd_pmac_resume(chip); + break; + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier snd_pmac_sleep_notifier = { + snd_pmac_sleep_notify, SLEEP_LEVEL_SOUND, +}; + +static int __init snd_pmac_register_sleep_notifier(pmac_t *chip) +{ + /* should be protected here.. */ + if (sleeping_pmac) { + snd_printd("sleep notifier already reigistered\n"); + return -EBUSY; + } + sleeping_pmac = chip; + pmu_register_sleep_notifier(&snd_pmac_sleep_notifier); + chip->sleep_registered = 1; + return 0; +} + +static int snd_pmac_unregister_sleep_notifier(pmac_t *chip) +{ + if (! chip->sleep_registered) + return 0; + /* should be protected here.. */ + if (sleeping_pmac != chip) + return -ENODEV; + pmu_unregister_sleep_notifier(&snd_pmac_sleep_notifier); + sleeping_pmac = NULL; + return 0; +} + +/* callback */ +static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state) +{ + pmac_t *chip = snd_magic_cast(pmac_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_pmac_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_pmac_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ diff -urN linux-2.4.21-rc1.orig/sound/ppc/pmac.h linux/sound/ppc/pmac.h --- linux-2.4.21-rc1.orig/sound/ppc/pmac.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/pmac.h 2002-08-29 10:20:05.000000000 -0600 @@ -0,0 +1,226 @@ +/* + * Driver for PowerMac onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __PMAC_H +#define __PMAC_H + +#include +#include +#include "awacs.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#include +#include +#include +#else /* 2.4.0 kernel */ +#include +#ifdef CONFIG_ADB_CUDA +#include +#endif +#ifdef CONFIG_ADB_PMU +#include +#endif +#endif +#include +#include +#include +#include +#include +#include + +/* maximum number of fragments */ +#define PMAC_MAX_FRAGS 32 + + +#define PMAC_SUPPORT_AUTOMUTE + +/* + * typedefs + */ +typedef struct snd_pmac pmac_t; +typedef struct snd_pmac_stream pmac_stream_t; +typedef struct snd_pmac_beep pmac_beep_t; +typedef struct snd_pmac_dbdma pmac_dbdma_t; + + +/* + * DBDMA space + */ +struct snd_pmac_dbdma { + unsigned long addr; + struct dbdma_cmd *cmds; + void *space; + int size; +}; + +/* + * playback/capture stream + */ +struct snd_pmac_stream { + int running; /* boolean */ + + int stream; /* PLAYBACK/CAPTURE */ + + int dma_size; /* in bytes */ + int period_size; /* in bytes */ + int buffer_size; /* in kbytes */ + int nperiods, cur_period; + + pmac_dbdma_t cmd; + volatile struct dbdma_regs *dma; + + snd_pcm_substream_t *substream; + + unsigned int cur_freqs; /* currently available frequences */ + unsigned int cur_formats; /* currently available formats */ +}; + + +/* + * beep using pcm + */ +struct snd_pmac_beep { + int running; /* boolean */ + int volume; /* mixer volume: 0-100 */ + int volume_play; /* currently playing volume */ + int hz; + int nsamples; + short *buf; /* allocated wave buffer */ + unsigned long addr; /* physical address of buffer */ + struct timer_list timer; /* timer list for stopping beep */ + void (*orig_mksound)(unsigned int, unsigned int); + /* pointer to restore */ + snd_kcontrol_t *control; /* mixer element */ +}; + + +/* + */ + +enum snd_pmac_model { + PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, PMAC_SNAPPER +}; + +struct snd_pmac { + snd_card_t *card; + + /* h/w info */ + struct device_node *node; + unsigned int revision; + unsigned int subframe; + unsigned int device_id; + enum snd_pmac_model model; + + unsigned int has_iic : 1; + unsigned int is_pbook_3400 : 1; + unsigned int is_pbook_G3 : 1; + + unsigned int can_byte_swap : 1; + unsigned int can_duplex : 1; + unsigned int can_capture : 1; + + unsigned int auto_mute : 1; + unsigned int initialized : 1; + unsigned int feature_is_set : 1; + + unsigned int of_requested; + + int num_freqs; + int *freq_table; + unsigned int freqs_ok; /* bit flags */ + unsigned int formats_ok; /* pcm hwinfo */ + int active; + int rate_index; + int format; /* current format */ + + spinlock_t reg_lock; + volatile struct awacs_regs *awacs; + int awacs_reg[8]; /* register cache */ + unsigned int hp_stat_mask; + + unsigned char *latch_base; + unsigned char *macio_base; + + pmac_stream_t playback; + pmac_stream_t capture; + + pmac_dbdma_t extra_dma; + + int irq, tx_irq, rx_irq; + + snd_pcm_t *pcm; + + pmac_beep_t *beep; + + unsigned int control_mask; /* control mask */ + + /* mixer stuffs */ + void *mixer_data; + void (*mixer_free)(pmac_t *); + snd_kcontrol_t *master_sw_ctl; + snd_kcontrol_t *speaker_sw_ctl; + snd_kcontrol_t *hp_detect_ctl; + + /* lowlevel callbacks */ + void (*set_format)(pmac_t *chip); + void (*update_automute)(pmac_t *chip, int do_notify); + int (*detect_headphone)(pmac_t *chip); +#ifdef CONFIG_PMAC_PBOOK + unsigned int sleep_registered : 1; + void (*suspend)(pmac_t *chip); + void (*resume)(pmac_t *chip); +#endif + +}; + + +/* exported functions */ +int snd_pmac_new(snd_card_t *card, pmac_t **chip_return); +int snd_pmac_pcm_new(pmac_t *chip); +int snd_pmac_attach_beep(pmac_t *chip); + +/* initialize mixer */ +int snd_pmac_awacs_init(pmac_t *chip); +int snd_pmac_burgundy_init(pmac_t *chip); +int snd_pmac_daca_init(pmac_t *chip); +int snd_pmac_tumbler_init(pmac_t *chip); + +/* i2c functions */ +typedef struct snd_pmac_keywest { + int addr; + struct i2c_client *client; + int id; + int (*init_client)(struct snd_pmac_keywest *i2c); + char *name; +} pmac_keywest_t; + +int snd_pmac_keywest_init(pmac_keywest_t *i2c); +void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c); +#define snd_pmac_keywest_write(i2c,cmd,len,data) i2c_smbus_write_block_data((i2c)->client, cmd, len, data) +#define snd_pmac_keywest_write_byte(i2c,cmd,data) i2c_smbus_write_byte_data((i2c)->client, cmd, data) + +/* misc */ +int snd_pmac_boolean_stereo_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); +int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); + +int snd_pmac_add_automute(pmac_t *chip); + +#endif /* __PMAC_H */ diff -urN linux-2.4.21-rc1.orig/sound/ppc/powermac.c linux/sound/ppc/powermac.c --- linux-2.4.21-rc1.orig/sound/ppc/powermac.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/powermac.c 2003-02-09 10:25:44.000000000 -0700 @@ -0,0 +1,192 @@ +/* + * Driver for PowerMac AWACS + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#define SNDRV_GET_ID +#include +#include "pmac.h" +#include "awacs.h" +#include "burgundy.h" + +#define CHIP_NAME "PMac" + +MODULE_DESCRIPTION("PowerMac"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Apple,PowerMac}}"); +MODULE_LICENSE("GPL"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int enable = 1; +static int enable_beep = 1; + +MODULE_PARM(index, "i"); +MODULE_PARM_DESC(index, "Index value for " CHIP_NAME " soundchip."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "s"); +MODULE_PARM_DESC(id, "ID string for " CHIP_NAME " soundchip."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "i"); +MODULE_PARM_DESC(enable, "Enable this soundchip."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(enable_beep, "i"); +MODULE_PARM_DESC(enable_beep, "Enable beep using PCM."); +MODULE_PARM_SYNTAX(enable_beep, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); + + +/* + * card entry + */ + +static snd_card_t *snd_pmac_card = NULL; + +/* + */ + +static int __init snd_pmac_probe(void) +{ + snd_card_t *card; + pmac_t *chip; + char *name_ext; + int err; + + card = snd_card_new(index, id, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_pmac_new(card, &chip)) < 0) + goto __error; + + switch (chip->model) { + case PMAC_BURGUNDY: + strcpy(card->driver, "PMac Burgundy"); + strcpy(card->shortname, "PowerMac Burgundy"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_burgundy_init(chip)) < 0) + goto __error; + break; + case PMAC_DACA: + strcpy(card->driver, "PMac DACA"); + strcpy(card->shortname, "PowerMac DACA"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_daca_init(chip)) < 0) + goto __error; + break; + case PMAC_TUMBLER: + case PMAC_SNAPPER: + name_ext = chip->model == PMAC_TUMBLER ? "Tumbler" : "Snapper"; + sprintf(card->driver, "PMac %s", name_ext); + sprintf(card->shortname, "PowerMac %s", name_ext); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_tumbler_init(chip)) < 0) + goto __error; + break; + case PMAC_AWACS: + case PMAC_SCREAMER: + name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS"; + sprintf(card->driver, "PMac %s", name_ext); + sprintf(card->shortname, "PowerMac %s", name_ext); + if (chip->is_pbook_3400) + name_ext = " [PB3400]"; + else if (chip->is_pbook_G3) + name_ext = " [PBG3]"; + else + name_ext = ""; + sprintf(card->longname, "%s%s Rev %d", + card->shortname, name_ext, chip->revision); + if ((err = snd_pmac_awacs_init(chip)) < 0) + goto __error; + break; + default: + snd_printk("unsupported hardware %d\n", chip->model); + err = -EINVAL; + goto __error; + } + + if ((err = snd_pmac_pcm_new(chip)) < 0) + goto __error; + + chip->initialized = 1; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + if (enable_beep) + snd_pmac_attach_beep(chip); +#endif + + if ((err = snd_card_register(card)) < 0) + goto __error; + + snd_pmac_card = card; + return 0; + +__error: + snd_card_free(card); + return err; +} + + +/* + * MODULE sutff + */ + +static int __init alsa_card_pmac_init(void) +{ + int err; + if ((err = snd_pmac_probe()) < 0) { +#ifdef MODULE + printk(KERN_ERR "no PMac soundchip found\n"); +#endif + return err; + } + return 0; + +} + +static void __exit alsa_card_pmac_exit(void) +{ + if (snd_pmac_card) + snd_card_free(snd_pmac_card); +} + +module_init(alsa_card_pmac_init) +module_exit(alsa_card_pmac_exit) + +#ifndef MODULE + +/* format is: snd-pmac=enable,index,id,enable_beep + */ + +static int __init alsa_card_pmac_setup(char *str) +{ + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&index) == 2 && + get_id(&str,&id) == 2 && + get_option(&str,&enable_beep) == 2 + ); + return 1; +} + +__setup("snd-pmac=", alsa_card_pmac_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/ppc/tumbler.c linux/sound/ppc/tumbler.c --- linux-2.4.21-rc1.orig/sound/ppc/tumbler.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/tumbler.c 2003-02-09 10:25:44.000000000 -0700 @@ -0,0 +1,1032 @@ +/* + * PMac Tumbler/Snapper lowlevel functions + * + * Copyright (c) by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS +#include +#endif +#include "pmac.h" +#include "tumbler_volume.h" + +#define chip_t pmac_t + +/* i2c address for tumbler */ +#define TAS_I2C_ADDR 0x34 + +/* registers */ +#define TAS_REG_MCS 0x01 +#define TAS_REG_DRC 0x02 +#define TAS_REG_VOL 0x04 +#define TAS_REG_TREBLE 0x05 +#define TAS_REG_BASS 0x06 +#define TAS_REG_INPUT1 0x07 +#define TAS_REG_INPUT2 0x08 + +/* tas3001c */ +#define TAS_REG_PCM TAS_REG_INPUT1 + +/* tas3004 */ +#define TAS_REG_LMIX TAS_REG_INPUT1 +#define TAS_REG_RMIX TAS_REG_INPUT2 + +/* mono volumes for tas3001c/tas3004 */ +enum { + VOL_IDX_PCM_MONO, /* tas3001c only */ + VOL_IDX_BASS, VOL_IDX_TREBLE, + VOL_IDX_LAST_MONO +}; + +/* stereo volumes for tas3004 */ +enum { + VOL_IDX_PCM, VOL_IDX_PCM2, VOL_IDX_ADC, + VOL_IDX_LAST_MIX +}; + +typedef struct pmac_gpio { +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS + unsigned int addr; +#else + void *addr; +#endif + int active_state; +} pmac_gpio_t; + +typedef struct pmac_tumber_t { + pmac_keywest_t i2c; + pmac_gpio_t audio_reset; + pmac_gpio_t amp_mute; + pmac_gpio_t hp_mute; + pmac_gpio_t hp_detect; + int headphone_irq; + unsigned int master_vol[2]; + unsigned int master_switch[2]; + unsigned int mono_vol[VOL_IDX_LAST_MONO]; + unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */ + int drc_range; + int drc_enable; +} pmac_tumbler_t; + + +#define number_of(ary) (sizeof(ary) / sizeof(ary[0])) + +/* + */ + +static int tumbler_init_client(pmac_keywest_t *i2c) +{ + int err, count = 10; + do { + /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ + err = snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS, + (1<<6)+(2<<4)+(2<<2)+0); + if (err >= 0) + return err; + mdelay(10); + } while (count--); + return -ENXIO; +} + + +/* + * gpio access + */ +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS +#define do_gpio_write(gp, val) \ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val) +#define do_gpio_read(gp) \ + pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0) +#define tumbler_gpio_free(gp) /* NOP */ +#else +#define do_gpio_write(gp, val) writeb(val, (gp)->addr) +#define do_gpio_read(gp) readb((gp)->addr) +static inline void tumbler_gpio_free(pmac_gpio_t *gp) +{ + if (gp->addr) { + iounmap(gp->addr); + gp->addr = 0; + } +} +#endif /* CONFIG_PPC_HAS_FEATURE_CALLS */ + +static void write_audio_gpio(pmac_gpio_t *gp, int active) +{ + if (! gp->addr) + return; + active = active ? gp->active_state : !gp->active_state; + do_gpio_write(gp, active ? 0x05 : 0x04); +} + +static int read_audio_gpio(pmac_gpio_t *gp) +{ + int ret; + if (! gp->addr) + return 0; + ret = ((do_gpio_read(gp) & 0x02) !=0); + return ret == gp->active_state; +} + +/* + * update master volume + */ +static int tumbler_set_master_volume(pmac_tumbler_t *mix) +{ + unsigned char block[6]; + unsigned int left_vol, right_vol; + + if (! mix->i2c.client) + return -ENODEV; + + if (! mix->master_switch[0]) + left_vol = 0; + else { + left_vol = mix->master_vol[0]; + if (left_vol >= number_of(master_volume_table)) + left_vol = number_of(master_volume_table) - 1; + left_vol = master_volume_table[left_vol]; + } + if (! mix->master_switch[1]) + right_vol = 0; + else { + right_vol = mix->master_vol[1]; + if (right_vol >= number_of(master_volume_table)) + right_vol = number_of(master_volume_table) - 1; + right_vol = master_volume_table[right_vol]; + } + + block[0] = (left_vol >> 16) & 0xff; + block[1] = (left_vol >> 8) & 0xff; + block[2] = (left_vol >> 0) & 0xff; + + block[3] = (right_vol >> 16) & 0xff; + block[4] = (right_vol >> 8) & 0xff; + block[5] = (right_vol >> 0) & 0xff; + + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_VOL, 6, block) < 0) { + snd_printk("failed to set volume \n"); + return -EINVAL; + } + return 0; +} + + +/* output volume */ +static int tumbler_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = number_of(master_volume_table) - 1; + return 0; +} + +static int tumbler_get_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix = chip->mixer_data; + snd_assert(mix, return -ENODEV); + ucontrol->value.integer.value[0] = mix->master_vol[0]; + ucontrol->value.integer.value[1] = mix->master_vol[1]; + return 0; +} + +static int tumbler_put_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix = chip->mixer_data; + int change; + + snd_assert(mix, return -ENODEV); + change = mix->master_vol[0] != ucontrol->value.integer.value[0] || + mix->master_vol[1] != ucontrol->value.integer.value[1]; + if (change) { + mix->master_vol[0] = ucontrol->value.integer.value[0]; + mix->master_vol[1] = ucontrol->value.integer.value[1]; + tumbler_set_master_volume(mix); + } + return change; +} + +/* output switch */ +static int tumbler_get_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix = chip->mixer_data; + snd_assert(mix, return -ENODEV); + ucontrol->value.integer.value[0] = mix->master_switch[0]; + ucontrol->value.integer.value[1] = mix->master_switch[1]; + return 0; +} + +static int tumbler_put_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix = chip->mixer_data; + int change; + + snd_assert(mix, return -ENODEV); + change = mix->master_switch[0] != ucontrol->value.integer.value[0] || + mix->master_switch[1] != ucontrol->value.integer.value[1]; + if (change) { + mix->master_switch[0] = !!ucontrol->value.integer.value[0]; + mix->master_switch[1] = !!ucontrol->value.integer.value[1]; + tumbler_set_master_volume(mix); + } + return change; +} + + +/* + * TAS3001c dynamic range compression + */ + +#define TAS3001_DRC_MAX 0x5f + +static int tumbler_set_drc(pmac_tumbler_t *mix) +{ + unsigned char val[2]; + + if (! mix->i2c.client) + return -ENODEV; + + if (mix->drc_enable) { + val[0] = 0xc1; /* enable, 3:1 compression */ + if (mix->drc_range > TAS3001_DRC_MAX) + val[1] = 0xf0; + else if (mix->drc_range < 0) + val[1] = 0x91; + else + val[1] = mix->drc_range + 0x91; + } else { + val[0] = 0; + val[1] = 0; + } + + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_DRC, 2, val) < 0) { + snd_printk("failed to set DRC\n"); + return -EINVAL; + } + return 0; +} + +/* + * TAS3004 + */ + +#define TAS3004_DRC_MAX 0xef + +static int snapper_set_drc(pmac_tumbler_t *mix) +{ + unsigned char val[6]; + + if (! mix->i2c.client) + return -ENODEV; + + if (mix->drc_enable) + val[0] = 0x50; /* 3:1 above threshold */ + else + val[0] = 0x51; /* disabled */ + val[1] = 0x02; /* 1:1 below threshold */ + if (mix->drc_range > 0xef) + val[2] = 0xef; + else if (mix->drc_range < 0) + val[2] = 0x00; + else + val[2] = mix->drc_range; + val[3] = 0xb0; + val[4] = 0x60; + val[5] = 0xa0; + + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_DRC, 6, val) < 0) { + snd_printk("failed to set DRC\n"); + return -EINVAL; + } + return 0; +} + +static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = + chip->model == PMAC_TUMBLER ? TAS3001_DRC_MAX : TAS3004_DRC_MAX; + return 0; +} + +static int tumbler_get_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->drc_range; + return 0; +} + +static int tumbler_put_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->drc_range != ucontrol->value.integer.value[0]; + if (change) { + mix->drc_range = ucontrol->value.integer.value[0]; + if (chip->model == PMAC_TUMBLER) + tumbler_set_drc(mix); + else + snapper_set_drc(mix); + } + return change; +} + +static int tumbler_get_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->drc_enable; + return 0; +} + +static int tumbler_put_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->drc_enable != ucontrol->value.integer.value[0]; + if (change) { + mix->drc_enable = !!ucontrol->value.integer.value[0]; + if (chip->model == PMAC_TUMBLER) + tumbler_set_drc(mix); + else + snapper_set_drc(mix); + } + return change; +} + + +/* + * mono volumes + */ + +struct tumbler_mono_vol { + int index; + int reg; + int bytes; + unsigned int max; + unsigned int *table; +}; + +static int tumbler_set_mono_volume(pmac_tumbler_t *mix, struct tumbler_mono_vol *info) +{ + unsigned char block[4]; + unsigned int vol; + int i; + + if (! mix->i2c.client) + return -ENODEV; + + vol = mix->mono_vol[info->index]; + if (vol >= info->max) + vol = info->max - 1; + vol = info->table[vol]; + for (i = 0; i < info->bytes; i++) + block[i] = (vol >> ((info->bytes - i - 1) * 8)) & 0xff; + if (snd_pmac_keywest_write(&mix->i2c, info->reg, info->bytes, block) < 0) { + snd_printk("failed to set mono volume %d\n", info->index); + return -EINVAL; + } + return 0; +} + +static int tumbler_info_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = info->max; + return 0; +} + +static int tumbler_get_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->mono_vol[info->index]; + return 0; +} + +static int tumbler_put_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0]; + if (change) { + mix->mono_vol[info->index] = ucontrol->value.integer.value[0]; + tumbler_set_mono_volume(mix, info); + } + return change; +} + +/* TAS3001c mono volumes */ +static struct tumbler_mono_vol tumbler_pcm_vol_info = { + .index = VOL_IDX_PCM_MONO, + .reg = TAS_REG_PCM, + .bytes = 3, + .max = number_of(mixer_volume_table), + .table = mixer_volume_table, +}; + +static struct tumbler_mono_vol tumbler_bass_vol_info = { + .index = VOL_IDX_BASS, + .reg = TAS_REG_BASS, + .bytes = 1, + .max = number_of(bass_volume_table), + .table = bass_volume_table, +}; + +static struct tumbler_mono_vol tumbler_treble_vol_info = { + .index = VOL_IDX_TREBLE, + .reg = TAS_REG_TREBLE, + .bytes = 1, + .max = number_of(treble_volume_table), + .table = treble_volume_table, +}; + +/* TAS3004 mono volumes */ +static struct tumbler_mono_vol snapper_bass_vol_info = { + .index = VOL_IDX_BASS, + .reg = TAS_REG_BASS, + .bytes = 1, + .max = number_of(snapper_bass_volume_table), + .table = snapper_bass_volume_table, +}; + +static struct tumbler_mono_vol snapper_treble_vol_info = { + .index = VOL_IDX_TREBLE, + .reg = TAS_REG_TREBLE, + .bytes = 1, + .max = number_of(snapper_treble_volume_table), + .table = snapper_treble_volume_table, +}; + + +#define DEFINE_MONO(xname,type) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = tumbler_info_mono, \ + .get = tumbler_get_mono, \ + .put = tumbler_put_mono, \ + .private_value = (unsigned long)(&tumbler_##type##_vol_info), \ +} + +#define DEFINE_SNAPPER_MONO(xname,type) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = tumbler_info_mono, \ + .get = tumbler_get_mono, \ + .put = tumbler_put_mono, \ + .private_value = (unsigned long)(&snapper_##type##_vol_info), \ +} + + +/* + * snapper mixer volumes + */ + +static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg) +{ + int i, j, vol; + unsigned char block[9]; + + vol = mix->mix_vol[idx][ch]; + if (vol >= number_of(mixer_volume_table)) { + vol = number_of(mixer_volume_table) - 1; + mix->mix_vol[idx][ch] = vol; + } + + for (i = 0; i < 3; i++) { + vol = mix->mix_vol[i][ch]; + vol = mixer_volume_table[vol]; + for (j = 0; j < 3; j++) + block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff; + } + if (snd_pmac_keywest_write(&mix->i2c, reg, 9, block) < 0) { + snd_printk("failed to set mono volume %d\n", reg); + return -EINVAL; + } + return 0; +} + +static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx) +{ + if (! mix->i2c.client) + return -ENODEV; + if (snapper_set_mix_vol1(mix, idx, 0, TAS_REG_LMIX) < 0 || + snapper_set_mix_vol1(mix, idx, 1, TAS_REG_RMIX) < 0) + return -EINVAL; + return 0; +} + +static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = number_of(mixer_volume_table) - 1; + return 0; +} + +static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int idx = (int)kcontrol->private_value; + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->mix_vol[idx][0]; + ucontrol->value.integer.value[1] = mix->mix_vol[idx][1]; + return 0; +} + +static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int idx = (int)kcontrol->private_value; + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] || + mix->mix_vol[idx][1] != ucontrol->value.integer.value[1]; + if (change) { + mix->mix_vol[idx][0] = ucontrol->value.integer.value[0]; + mix->mix_vol[idx][1] = ucontrol->value.integer.value[1]; + snapper_set_mix_vol(mix, idx); + } + return change; +} + + +/* + * mute switches + */ + +enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP }; + +static int tumbler_get_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + pmac_gpio_t *gp; + if (! (mix = chip->mixer_data)) + return -ENODEV; + gp = (kcontrol->private_value == TUMBLER_MUTE_HP) ? &mix->hp_mute : &mix->amp_mute; + ucontrol->value.integer.value[0] = ! read_audio_gpio(gp); + return 0; +} + +static int tumbler_put_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + pmac_gpio_t *gp; + int val; + if (! (mix = chip->mixer_data)) + return -ENODEV; + gp = (kcontrol->private_value == TUMBLER_MUTE_HP) ? &mix->hp_mute : &mix->amp_mute; + val = ! read_audio_gpio(gp); + if (val != ucontrol->value.integer.value[0]) { + write_audio_gpio(gp, ! ucontrol->value.integer.value[0]); + return 1; + } + return 0; +} + +#define DEFINE_SNAPPER_MIX(xname,idx,ofs) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = snapper_info_mix, \ + .get = snapper_get_mix, \ + .put = snapper_put_mix, \ + .index = idx,\ + .private_value = ofs, \ +} + + +/* + */ +static snd_kcontrol_new_t tumbler_mixers[] __initdata = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = tumbler_info_master_volume, + .get = tumbler_get_master_volume, + .put = tumbler_put_master_volume + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_pmac_boolean_stereo_info, + .get = tumbler_get_master_switch, + .put = tumbler_put_master_switch + }, + DEFINE_MONO("Tone Control - Bass", bass), + DEFINE_MONO("Tone Control - Treble", treble), + DEFINE_MONO("PCM Playback Volume", pcm), + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Switch", + .info = snd_pmac_boolean_mono_info, + .get = tumbler_get_drc_switch, + .put = tumbler_put_drc_switch + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Range", + .info = tumbler_info_drc_value, + .get = tumbler_get_drc_value, + .put = tumbler_put_drc_value + }, +}; + +static snd_kcontrol_new_t snapper_mixers[] __initdata = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = tumbler_info_master_volume, + .get = tumbler_get_master_volume, + .put = tumbler_put_master_volume + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_pmac_boolean_stereo_info, + .get = tumbler_get_master_switch, + .put = tumbler_put_master_switch + }, + DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM), + DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2), + DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC), + DEFINE_SNAPPER_MONO("Tone Control - Bass", bass), + DEFINE_SNAPPER_MONO("Tone Control - Treble", treble), + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Switch", + .info = snd_pmac_boolean_mono_info, + .get = tumbler_get_drc_switch, + .put = tumbler_put_drc_switch + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Range", + .info = tumbler_info_drc_value, + .get = tumbler_get_drc_value, + .put = tumbler_put_drc_value + }, +}; + +static snd_kcontrol_new_t tumbler_hp_sw __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Switch", + .info = snd_pmac_boolean_mono_info, + .get = tumbler_get_mute_switch, + .put = tumbler_put_mute_switch, + .private_value = TUMBLER_MUTE_HP, +}; +static snd_kcontrol_new_t tumbler_speaker_sw __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PC Speaker Playback Switch", + .info = snd_pmac_boolean_mono_info, + .get = tumbler_get_mute_switch, + .put = tumbler_put_mute_switch, + .private_value = TUMBLER_MUTE_AMP, +}; + +#ifdef PMAC_SUPPORT_AUTOMUTE +/* + * auto-mute stuffs + */ +static int tumbler_detect_headphone(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + return read_audio_gpio(&mix->hp_detect); +} + +static void check_mute(pmac_t *chip, pmac_gpio_t *gp, int val, int do_notify, snd_kcontrol_t *sw) +{ + //pmac_tumbler_t *mix = chip->mixer_data; + if (val != read_audio_gpio(gp)) { + write_audio_gpio(gp, val); + if (do_notify) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &sw->id); + } +} + +static void tumbler_update_automute(pmac_t *chip, int do_notify) +{ + if (chip->auto_mute) { + pmac_tumbler_t *mix = chip->mixer_data; + snd_assert(mix, return); + if (tumbler_detect_headphone(chip)) { + /* mute speaker */ + check_mute(chip, &mix->amp_mute, 1, do_notify, chip->speaker_sw_ctl); + check_mute(chip, &mix->hp_mute, 0, do_notify, chip->master_sw_ctl); + } else { + /* unmute speaker */ + check_mute(chip, &mix->amp_mute, 0, do_notify, chip->speaker_sw_ctl); + check_mute(chip, &mix->hp_mute, 1, do_notify, chip->master_sw_ctl); + } + if (do_notify) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->hp_detect_ctl->id); + } +} +#endif /* PMAC_SUPPORT_AUTOMUTE */ + + +/* interrupt - headphone plug changed */ +static void headphone_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + if (chip->update_automute && chip->initialized) + chip->update_automute(chip, 1); +} + +/* look for audio-gpio device */ +static struct device_node *find_audio_device(const char *name) +{ + struct device_node *np; + + if (! (np = find_devices("gpio"))) + return NULL; + + for (np = np->child; np; np = np->sibling) { + char *property = get_property(np, "audio-gpio", NULL); + if (property && strcmp(property, name) == 0) + return np; + } + return NULL; +} + +/* look for audio-gpio device */ +static struct device_node *find_compatible_audio_device(const char *name) +{ + struct device_node *np; + + if (! (np = find_devices("gpio"))) + return NULL; + + for (np = np->child; np; np = np->sibling) { + if (device_is_compatible(np, name)) + return np; + } + return NULL; +} + +/* find an audio device and get its address */ +static unsigned long tumbler_find_device(const char *device, pmac_gpio_t *gp, int is_compatible) +{ + struct device_node *node; + u32 *base; + + if (is_compatible) + node = find_compatible_audio_device(device); + else + node = find_audio_device(device); + if (! node) { + snd_printdd("cannot find device %s\n", device); + return -ENODEV; + } + + base = (u32 *)get_property(node, "AAPL,address", NULL); + if (! base) { + snd_printd("cannot find address for device %s\n", device); + return -ENODEV; + } + +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS + gp->addr = (*base) & 0x0000ffff; +#else + gp->addr = (void*)ioremap((unsigned long)(*base), 1); +#endif + base = (u32 *)get_property(node, "audio-gpio-active-state", NULL); + if (base) + gp->active_state = *base; + else + gp->active_state = 1; + + + return (node->n_intrs > 0) ? node->intrs[0].line : 0; +} + +/* reset audio */ +static void tumbler_reset_audio(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + + write_audio_gpio(&mix->audio_reset, 1); + mdelay(100); + write_audio_gpio(&mix->audio_reset, 0); + mdelay(100); +} + +#ifdef CONFIG_PMAC_PBOOK +/* resume mixer */ +static void tumbler_resume(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + snd_assert(mix, return); + tumbler_reset_audio(chip); + if (mix->i2c.client) + tumbler_init_client(&mix->i2c); + if (chip->model == PMAC_TUMBLER) { + tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info); + tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); + tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); + } else { + snapper_set_mix_vol(mix, VOL_IDX_PCM); + snapper_set_mix_vol(mix, VOL_IDX_PCM2); + snapper_set_mix_vol(mix, VOL_IDX_ADC); + tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); + tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); + } + tumbler_set_drc(mix); + tumbler_set_master_volume(mix); + if (chip->update_automute) + chip->update_automute(chip, 0); +} +#endif + +/* initialize tumbler */ +static int __init tumbler_init(pmac_t *chip) +{ + int irq, err; + pmac_tumbler_t *mix = chip->mixer_data; + snd_assert(mix, return -EINVAL); + + tumbler_find_device("audio-hw-reset", &mix->audio_reset, 0); + tumbler_find_device("amp-mute", &mix->amp_mute, 0); + tumbler_find_device("headphone-mute", &mix->hp_mute, 0); + irq = tumbler_find_device("headphone-detect", &mix->hp_detect, 0); + if (irq < 0) + irq = tumbler_find_device("keywest-gpio15", &mix->hp_detect, 1); + + tumbler_reset_audio(chip); + + /* activate headphone status interrupts */ + if (irq >= 0) { + unsigned char val; + if ((err = request_irq(irq, headphone_intr, 0, + "Tumbler Headphone Detection", chip)) < 0) + return err; + /* activate headphone status interrupts */ + val = do_gpio_read(&mix->hp_detect); + do_gpio_write(&mix->hp_detect, val | 0x80); + } + mix->headphone_irq = irq; + + return 0; +} + +static void tumbler_cleanup(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + if (! mix) + return; + + if (mix->headphone_irq >= 0) + free_irq(mix->headphone_irq, chip); + tumbler_gpio_free(&mix->audio_reset); + tumbler_gpio_free(&mix->amp_mute); + tumbler_gpio_free(&mix->hp_mute); + tumbler_gpio_free(&mix->hp_detect); + snd_pmac_keywest_cleanup(&mix->i2c); + kfree(mix); + chip->mixer_data = NULL; +} + +/* exported */ +int __init snd_pmac_tumbler_init(pmac_t *chip) +{ + int i, err; + pmac_tumbler_t *mix; + u32 *paddr; + struct device_node *tas_node; + char *chipname; + +#ifdef CONFIG_KMOD + request_module("i2c-keywest"); +#endif /* CONFIG_KMOD */ + + mix = kmalloc(sizeof(*mix), GFP_KERNEL); + if (! mix) + return -ENOMEM; + memset(mix, 0, sizeof(*mix)); + mix->headphone_irq = -1; + + chip->mixer_data = mix; + chip->mixer_free = tumbler_cleanup; + + if ((err = tumbler_init(chip)) < 0) + return err; + + /* set up TAS */ + tas_node = find_devices("deq"); + if (tas_node == NULL) + return -ENODEV; + + paddr = (u32 *)get_property(tas_node, "i2c-address", NULL); + if (paddr) + mix->i2c.addr = (*paddr) >> 1; + else + mix->i2c.addr = TAS_I2C_ADDR; + + mix->i2c.init_client = tumbler_init_client; + if (chip->model == PMAC_TUMBLER) { + mix->i2c.name = "TAS3001c"; + chipname = "Tumbler"; + } else { + mix->i2c.name = "TAS3004"; + chipname = "Snapper"; + } + + if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0) + return err; + + /* + * build mixers + */ + sprintf(chip->card->mixername, "PowerMac %s", chipname); + + if (chip->model == PMAC_TUMBLER) { + for (i = 0; i < number_of(tumbler_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0) + return err; + } + } else { + for (i = 0; i < number_of(snapper_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0) + return err; + } + } + chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + return err; + chip->speaker_sw_ctl = snd_ctl_new1(&tumbler_speaker_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + return err; + +#ifdef CONFIG_PMAC_PBOOK + chip->resume = tumbler_resume; +#endif + +#ifdef PMAC_SUPPORT_AUTOMUTE + if (mix->headphone_irq >=0 && (err = snd_pmac_add_automute(chip)) < 0) + return err; + chip->detect_headphone = tumbler_detect_headphone; + chip->update_automute = tumbler_update_automute; + tumbler_update_automute(chip, 0); /* update the status only */ +#endif + + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/ppc/tumbler_volume.h linux/sound/ppc/tumbler_volume.h --- linux-2.4.21-rc1.orig/sound/ppc/tumbler_volume.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/ppc/tumbler_volume.h 2002-08-27 07:46:15.000000000 -0600 @@ -0,0 +1,250 @@ +/* volume tables, taken from TAS3001c data manual */ +/* volume gain values */ +/* 0 = -70 dB, 175 = 18.0 dB in 0.5 dB step */ +static unsigned int master_volume_table[] = { + 0x00000015, 0x00000016, 0x00000017, + 0x00000019, 0x0000001a, 0x0000001c, + 0x0000001d, 0x0000001f, 0x00000021, + 0x00000023, 0x00000025, 0x00000027, + 0x00000029, 0x0000002c, 0x0000002e, + 0x00000031, 0x00000034, 0x00000037, + 0x0000003a, 0x0000003e, 0x00000042, + 0x00000045, 0x0000004a, 0x0000004e, + 0x00000053, 0x00000057, 0x0000005d, + 0x00000062, 0x00000068, 0x0000006e, + 0x00000075, 0x0000007b, 0x00000083, + 0x0000008b, 0x00000093, 0x0000009b, + 0x000000a5, 0x000000ae, 0x000000b9, + 0x000000c4, 0x000000cf, 0x000000dc, + 0x000000e9, 0x000000f6, 0x00000105, + 0x00000114, 0x00000125, 0x00000136, + 0x00000148, 0x0000015c, 0x00000171, + 0x00000186, 0x0000019e, 0x000001b6, + 0x000001d0, 0x000001eb, 0x00000209, + 0x00000227, 0x00000248, 0x0000026b, + 0x0000028f, 0x000002b6, 0x000002df, + 0x0000030b, 0x00000339, 0x0000036a, + 0x0000039e, 0x000003d5, 0x0000040f, + 0x0000044c, 0x0000048d, 0x000004d2, + 0x0000051c, 0x00000569, 0x000005bb, + 0x00000612, 0x0000066e, 0x000006d0, + 0x00000737, 0x000007a5, 0x00000818, + 0x00000893, 0x00000915, 0x0000099f, + 0x00000a31, 0x00000acc, 0x00000b6f, + 0x00000c1d, 0x00000cd5, 0x00000d97, + 0x00000e65, 0x00000f40, 0x00001027, + 0x0000111c, 0x00001220, 0x00001333, + 0x00001456, 0x0000158a, 0x000016d1, + 0x0000182b, 0x0000199a, 0x00001b1e, + 0x00001cb9, 0x00001e6d, 0x0000203a, + 0x00002223, 0x00002429, 0x0000264e, + 0x00002893, 0x00002afa, 0x00002d86, + 0x00003039, 0x00003314, 0x0000361b, + 0x00003950, 0x00003cb5, 0x0000404e, + 0x0000441d, 0x00004827, 0x00004c6d, + 0x000050f4, 0x000055c0, 0x00005ad5, + 0x00006037, 0x000065ea, 0x00006bf4, + 0x0000725a, 0x00007920, 0x0000804e, + 0x000087e8, 0x00008ff6, 0x0000987d, + 0x0000a186, 0x0000ab19, 0x0000b53c, + 0x0000bff9, 0x0000cb59, 0x0000d766, + 0x0000e429, 0x0000f1ae, 0x00010000, + 0x00010f2b, 0x00011f3d, 0x00013042, + 0x00014249, 0x00015562, 0x0001699c, + 0x00017f09, 0x000195bc, 0x0001adc6, + 0x0001c73d, 0x0001e237, 0x0001feca, + 0x00021d0e, 0x00023d1d, 0x00025f12, + 0x0002830b, 0x0002a925, 0x0002d182, + 0x0002fc42, 0x0003298b, 0x00035983, + 0x00038c53, 0x0003c225, 0x0003fb28, + 0x0004378b, 0x00047783, 0x0004bb44, + 0x0005030a, 0x00054f10, 0x00059f98, + 0x0005f4e5, 0x00064f40, 0x0006aef6, + 0x00071457, 0x00077fbb, 0x0007f17b, +}; + +/* treble table for TAS3001c */ +/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ +static unsigned int treble_volume_table[] = { + 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, + 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, + 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, + 0x81, 0x80, 0x7f, + 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, + 0x78, 0x77, 0x76, + 0x75, 0x74, 0x73, + 0x72, 0x71, 0x70, + 0x6e, 0x6d, 0x6c, + 0x6b, 0x69, 0x68, + 0x66, 0x65, 0x63, + 0x62, 0x60, 0x5e, + 0x5c, 0x5a, 0x57, + 0x55, 0x52, 0x4f, + 0x4c, 0x49, 0x45, + 0x42, 0x3e, 0x3a, + 0x36, 0x32, 0x2d, + 0x28, 0x22, 0x1c, + 0x16, 0x10, 0x09, + 0x01, +}; + +/* bass table for TAS3001c */ +/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ +static unsigned int bass_volume_table[] = { + 0x86, 0x82, 0x7f, + 0x7d, 0x7a, 0x78, + 0x76, 0x74, 0x72, + 0x70, 0x6e, 0x6d, + 0x6b, 0x69, 0x66, + 0x64, 0x61, 0x5f, + 0x5d, 0x5c, 0x5a, + 0x59, 0x58, 0x56, + 0x55, 0x54, 0x53, + 0x51, 0x4f, 0x4d, + 0x4b, 0x49, 0x46, + 0x44, 0x42, 0x40, + 0x3e, 0x3c, 0x3b, + 0x39, 0x38, 0x36, + 0x35, 0x33, 0x31, + 0x30, 0x2e, 0x2c, + 0x2b, 0x29, 0x28, + 0x26, 0x25, 0x23, + 0x21, 0x1f, 0x1c, + 0x19, 0x18, 0x17, + 0x16, 0x14, 0x13, + 0x12, 0x10, 0x0f, + 0x0d, 0x0b, 0x0a, + 0x08, 0x06, 0x03, + 0x01, +}; + +/* mixer (pcm) volume table */ +/* 0 = -70 dB, 175 = 18.0 dB in 0.5 dB step */ +static unsigned int mixer_volume_table[] = { + 0x00014b, 0x00015f, 0x000174, + 0x00018a, 0x0001a1, 0x0001ba, + 0x0001d4, 0x0001f0, 0x00020d, + 0x00022c, 0x00024d, 0x000270, + 0x000295, 0x0002bc, 0x0002e6, + 0x000312, 0x000340, 0x000372, + 0x0003a6, 0x0003dd, 0x000418, + 0x000456, 0x000498, 0x0004de, + 0x000528, 0x000576, 0x0005c9, + 0x000620, 0x00067d, 0x0006e0, + 0x000748, 0x0007b7, 0x00082c, + 0x0008a8, 0x00092b, 0x0009b6, + 0x000a49, 0x000ae5, 0x000b8b, + 0x000c3a, 0x000cf3, 0x000db8, + 0x000e88, 0x000f64, 0x00104e, + 0x001145, 0x00124b, 0x001361, + 0x001487, 0x0015be, 0x001708, + 0x001865, 0x0019d8, 0x001b60, + 0x001cff, 0x001eb7, 0x002089, + 0x002276, 0x002481, 0x0026ab, + 0x0028f5, 0x002b63, 0x002df5, + 0x0030ae, 0x003390, 0x00369e, + 0x0039db, 0x003d49, 0x0040ea, + 0x0044c3, 0x0048d6, 0x004d27, + 0x0051b9, 0x005691, 0x005bb2, + 0x006121, 0x0066e3, 0x006cfb, + 0x007370, 0x007a48, 0x008186, + 0x008933, 0x009154, 0x0099f1, + 0x00a310, 0x00acba, 0x00b6f6, + 0x00c1cd, 0x00cd49, 0x00d973, + 0x00e655, 0x00f3fb, 0x010270, + 0x0111c0, 0x0121f9, 0x013328, + 0x01455b, 0x0158a2, 0x016d0e, + 0x0182af, 0x019999, 0x01b1de, + 0x01cb94, 0x01e6cf, 0x0203a7, + 0x022235, 0x024293, 0x0264db, + 0x02892c, 0x02afa3, 0x02d862, + 0x03038a, 0x033142, 0x0361af, + 0x0394fa, 0x03cb50, 0x0404de, + 0x0441d5, 0x048268, 0x04c6d0, + 0x050f44, 0x055c04, 0x05ad50, + 0x06036e, 0x065ea5, 0x06bf44, + 0x07259d, 0x079207, 0x0804dc, + 0x087e80, 0x08ff59, 0x0987d5, + 0x0a1866, 0x0ab189, 0x0b53be, + 0x0bff91, 0x0cb591, 0x0d765a, + 0x0e4290, 0x0f1adf, 0x100000, + 0x10f2b4, 0x11f3c9, 0x13041a, + 0x14248e, 0x15561a, 0x1699c0, + 0x17f094, 0x195bb8, 0x1adc61, + 0x1c73d5, 0x1e236d, 0x1fec98, + 0x21d0d9, 0x23d1cd, 0x25f125, + 0x2830af, 0x2a9254, 0x2d1818, + 0x2fc420, 0x3298b0, 0x35982f, + 0x38c528, 0x3c224c, 0x3fb278, + 0x437880, 0x477828, 0x4bb446, + 0x5030a1, 0x54f106, 0x59f980, + 0x5f4e52, 0x64f403, 0x6aef5d, + 0x714575, 0x77fbaa, 0x7f17af, +}; + + +/* treble table for TAS3004 */ +/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ +static unsigned int snapper_treble_volume_table[] = { + 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, + 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, + 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, + 0x81, 0x80, 0x7f, + 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, + 0x78, 0x77, 0x76, + 0x75, 0x74, 0x73, + 0x72, 0x71, 0x70, + 0x6f, 0x6d, 0x6c, + 0x6b, 0x69, 0x68, + 0x67, 0x65, 0x63, + 0x62, 0x60, 0x5d, + 0x5b, 0x59, 0x56, + 0x53, 0x51, 0x4d, + 0x4a, 0x47, 0x43, + 0x3f, 0x3b, 0x36, + 0x31, 0x2c, 0x26, + 0x20, 0x1a, 0x13, + 0x08, 0x04, 0x01, + 0x01, +}; + +/* bass table for TAS3004 */ +/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ +static unsigned int snapper_bass_volume_table[] = { + 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, + 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, + 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, + 0x81, 0x80, 0x7f, + 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, + 0x78, 0x77, 0x76, + 0x75, 0x74, 0x73, + 0x72, 0x71, 0x6f, + 0x6e, 0x6d, 0x6b, + 0x6a, 0x69, 0x67, + 0x66, 0x65, 0x63, + 0x62, 0x61, 0x5f, + 0x5d, 0x5b, 0x58, + 0x55, 0x52, 0x4f, + 0x4c, 0x49, 0x46, + 0x43, 0x3f, 0x3b, + 0x37, 0x33, 0x2e, + 0x29, 0x24, 0x1e, + 0x18, 0x11, 0x0a, + 0x01, +}; + diff -urN linux-2.4.21-rc1.orig/sound/sound_core.c linux/sound/sound_core.c --- linux-2.4.21-rc1.orig/sound/sound_core.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/sound_core.c 2003-02-26 01:53:13.000000000 -0700 @@ -0,0 +1,576 @@ +/* + * Sound core handling. Breaks out sound functions to submodules + * + * Author: Alan Cox + * + * 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 don't 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 it's 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOUND_STEP 16 + + +struct sound_unit +{ + int unit_minor; + struct file_operations *unit_fops; + struct sound_unit *next; + devfs_handle_t de; +}; + +#ifdef CONFIG_SOUND_MSNDCLAS +extern int msnd_classic_init(void); +#endif +#ifdef CONFIG_SOUND_MSNDPIN +extern int msnd_pinnacle_init(void); +#endif + +/* + * 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 index, int low, int top) +{ + int n=low; + + if (index < 0) { /* first free */ + + while (*list && (*list)->unit_minornext); + + while(nunit_minor>n) + break; + list=&((*list)->next); + n+=SOUND_STEP; + } + + if(n>=top) + return -ENOENT; + } else { + n = low+(index*16); + while (*list) { + if ((*list)->unit_minor==n) + return -EBUSY; + if ((*list)->unit_minor>n) + break; + list=&((*list)->next); + } + } + + /* + * Fill it in + */ + + s->unit_minor=n; + s->unit_fops=fops; + + /* + * Link it + */ + + s->next=*list; + *list=s; + + + return n; +} + +/* + * Remove a node from the chain. Called with the lock asserted + */ + +static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit) +{ + while(*list) + { + struct sound_unit *p=*list; + if(p->unit_minor==unit) + { + *list=p->next; + return p; + } + list=&(p->next); + } + printk(KERN_ERR "Sound device %d went missing!\n", unit); + return NULL; +} + +/* + * 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 index, int low, int top, const char *name, umode_t mode) +{ + int r; + struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); + char name_buf[32]; + + if(s==NULL) + return -ENOMEM; + + spin_lock(&sound_loader_lock); + r=__sound_insert_unit(s,list,fops,index,low,top); + spin_unlock(&sound_loader_lock); + + if(r<0) + { + kfree(s); + return r; + } + + if (r < SOUND_STEP) + sprintf (name_buf, "sound/%s", name); + else + sprintf (name_buf, "sound/%s%d", name, r / SOUND_STEP); + s->de = devfs_register (NULL, name_buf, + DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, + S_IFCHR | mode, fops, NULL); + 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) +{ + struct sound_unit *p; + + spin_lock(&sound_loader_lock); + p = __sound_remove_unit(list, unit); + spin_unlock(&sound_loader_lock); + if (p) { + devfs_unregister (p->de); + kfree(p); + } +} + +/* + * 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 raw synthesizer access + * 10 *16 unused + * 11 *16 unused + * 12 *16 unused + * 13 *16 unused + * 14 *16 unused + * 15 *16 unused + */ + +static struct sound_unit *chains[SOUND_STEP]; + +/** + * register_sound_special - register a special sound node + * @fops: File operations for the driver + * @unit: Unit number to allocate + * + * Allocate a special sound device by minor number from the sound + * subsystem. The allocated number is returned on succes. On failure + * a negative error code is returned. + */ + +int register_sound_special(struct file_operations *fops, int unit) +{ + const int chain = unit % SOUND_STEP; + int max_unit = 128 + chain; + const char *name; + char _name[16]; + + switch (chain) { + case 0: + name = "mixer"; + break; + case 1: + name = "sequencer"; + if (unit >= SOUND_STEP) + goto __unknown; + max_unit = unit + 1; + break; + case 2: + name = "midi"; + break; + case 3: + name = "dsp"; + break; + case 4: + name = "audio"; + break; + case 8: + name = "sequencer2"; + if (unit >= SOUND_STEP) + goto __unknown; + max_unit = unit + 1; + break; + case 9: + name = "dmmidi"; + break; + case 10: + name = "dmfm"; + break; + case 12: + name = "adsp"; + break; + case 13: + name = "amidi"; + break; + case 14: + name = "admmidi"; + break; + default: + { + __unknown: + sprintf(_name, "unknown%d", chain); + if (unit >= SOUND_STEP) + strcat(_name, "-"); + name = _name; + } + break; + } + return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, + name, S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_special); + +/** + * register_sound_mixer - register a mixer device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a mixer device. Unit is the number of the mixer requested. + * Pass -1 to request the next free mixer unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + +int register_sound_mixer(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[0], fops, dev, 0, 128, + "mixer", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_mixer); + +/** + * register_sound_midi - register a midi device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a midi device. Unit is the number of the midi device requested. + * Pass -1 to request the next free midi unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + +int register_sound_midi(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[2], fops, dev, 2, 130, + "midi", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_midi); + +/* + * DSP's are registered as a triple. Register only one and cheat + * in open - see below. + */ + +/** + * register_sound_dsp - register a DSP device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a DSP device. Unit is the number of the DSP requested. + * Pass -1 to request the next free DSP unit. On success the allocated + * number is returned, on failure a negative error code is returned. + * + * This function allocates both the audio and dsp device entries together + * and will always allocate them as a matching pair - eg dsp3/audio3 + */ + +int register_sound_dsp(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[3], fops, dev, 3, 131, + "dsp", S_IWUSR | S_IRUSR); +} + +EXPORT_SYMBOL(register_sound_dsp); + +/** + * register_sound_synth - register a synth device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a synth device. Unit is the number of the synth device requested. + * Pass -1 to request the next free synth unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + + +int register_sound_synth(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[9], fops, dev, 9, 137, + "synth", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_synth); + +/** + * unregister_sound_special - unregister a special sound device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with + * register_sound_special(). The unit passed is the return value from + * the register function. + */ + + +void unregister_sound_special(int unit) +{ + sound_remove_unit(&chains[unit % SOUND_STEP], unit); +} + +EXPORT_SYMBOL(unregister_sound_special); + +/** + * unregister_sound_mixer - unregister a mixer + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_mixer(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_mixer(int unit) +{ + sound_remove_unit(&chains[0], unit); +} + +EXPORT_SYMBOL(unregister_sound_mixer); + +/** + * unregister_sound_midi - unregister a midi device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_midi(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_midi(int unit) +{ + return sound_remove_unit(&chains[2], unit); +} + +EXPORT_SYMBOL(unregister_sound_midi); + +/** + * unregister_sound_dsp - unregister a DSP device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_dsp(). + * The unit passed is the return value from the register function. + * + * Both of the allocated units are released together automatically. + */ + +void unregister_sound_dsp(int unit) +{ + return sound_remove_unit(&chains[3], unit); +} + + +EXPORT_SYMBOL(unregister_sound_dsp); + +/** + * unregister_sound_synth - unregister a synth device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_synth(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_synth(int unit) +{ + return sound_remove_unit(&chains[9], unit); +} + +EXPORT_SYMBOL(unregister_sound_synth); + +/* + * Now our file operations + */ + +static int soundcore_open(struct inode *, struct file *); + +static struct file_operations soundcore_fops= +{ + /* We must have an owner or the module locking fails */ + .owner = THIS_MODULE, + .open = soundcore_open, +}; + +static struct sound_unit *__look_for_unit(int chain, int unit) +{ + struct sound_unit *s; + + s=chains[chain]; + while(s && s->unit_minor <= unit) + { + if(s->unit_minor==unit) + return s; + s=s->next; + } + return NULL; +} + +int soundcore_open(struct inode *inode, struct file *file) +{ + int chain; + int unit = minor(inode->i_rdev); + struct sound_unit *s; + struct file_operations *new_fops = NULL; + + chain=unit&0x0F; + if(chain==4 || chain==5) /* dsp/audio/dsp16 */ + { + unit&=0xF0; + unit|=3; + chain=3; + } + + spin_lock(&sound_loader_lock); + s = __look_for_unit(chain, unit); + if (s) + new_fops = fops_get(s->unit_fops); + if (!new_fops) { + char mod[32]; + + spin_unlock(&sound_loader_lock); + /* + * Please, don't change this order or code. + * For ALSA slot means soundcard and OSS emulation code + * comes as add-on modules which aren't depend on + * ALSA toplevel modules for soundcards, thus we need + * load them at first. [Jaroslav Kysela ] + */ + sprintf(mod, "sound-slot-%i", unit>>4); + request_module(mod); + sprintf(mod, "sound-service-%i-%i", unit>>4, chain); + request_module(mod); + spin_lock(&sound_loader_lock); + s = __look_for_unit(chain, unit); + if (s) + new_fops = fops_get(s->unit_fops); + } + if (new_fops) { + /* + * We rely upon the fact that we can't be unloaded while the + * subdriver is there, so if ->open() is successful we can + * safely drop the reference counter and if it is not we can + * revert to old ->f_op. Ugly, indeed, but that's the cost of + * switching ->f_op in the first place. + */ + int err = 0; + struct file_operations *old_fops = file->f_op; + file->f_op = new_fops; + spin_unlock(&sound_loader_lock); + if(file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; + } + spin_unlock(&sound_loader_lock); + return -ENODEV; +} + +extern int mod_firmware_load(const char *, char **); +EXPORT_SYMBOL(mod_firmware_load); + + +MODULE_DESCRIPTION("Core sound module"); +MODULE_AUTHOR("Alan Cox"); +MODULE_LICENSE("GPL"); + +static void __exit cleanup_soundcore(void) +{ + /* We have nothing to really do here - we know the lists must be + empty */ + unregister_chrdev(SOUND_MAJOR, "sound"); + devfs_remove("sound"); +} + +static int __init init_soundcore(void) +{ + if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) { + printk(KERN_ERR "soundcore: sound device already in use.\n"); + return -EBUSY; + } + devfs_mk_dir (NULL, "sound", NULL); + + return 0; +} + +module_init(init_soundcore); +module_exit(cleanup_soundcore); diff -urN linux-2.4.21-rc1.orig/sound/sound_firmware.c linux/sound/sound_firmware.c --- linux-2.4.21-rc1.orig/sound/sound_firmware.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/sound_firmware.c 2003-04-29 23:47:32.000000000 -0600 @@ -0,0 +1,78 @@ +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include + +static int errno; +static int do_mod_firmware_load(const char *fn, char **fp) +{ + int fd; + long l; + char *dp; + + fd = open(fn, 0, 0); + if (fd == -1) + { + printk(KERN_INFO "Unable to load '%s'.\n", fn); + return 0; + } + l = lseek(fd, 0L, 2); + if (l <= 0 || l > 131072) + { + printk(KERN_INFO "Invalid firmware '%s'\n", fn); + sys_close(fd); + return 0; + } + lseek(fd, 0L, 0); + dp = vmalloc(l); + if (dp == NULL) + { + printk(KERN_INFO "Out of memory loading '%s'.\n", fn); + sys_close(fd); + return 0; + } + if (read(fd, dp, l) != l) + { + printk(KERN_INFO "Failed to read '%s'.\n", fn); + vfree(dp); + sys_close(fd); + return 0; + } + close(fd); + *fp = dp; + return (int) l; +} + +/** + * mod_firmware_load - load sound driver firmware + * @fn: filename + * @fp: return for the buffer. + * + * Load the firmware for a sound module (up to 128K) into a buffer. + * The buffer is returned in *fp. It is allocated with vmalloc so is + * virtually linear and not DMAable. The caller should free it with + * vfree when finished. + * + * The length of the buffer is returned on a successful load, the + * value zero on a failure. + * + * Caution: This API is not recommended. Firmware should be loaded via + * an ioctl call and a setup application. This function may disappear + * in future. + */ + +int mod_firmware_load(const char *fn, char **fp) +{ + int r; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + r = do_mod_firmware_load(fn, fp); + set_fs(fs); + return r; +} + diff -urN linux-2.4.21-rc1.orig/sound/sparc/Config.in linux/sound/sparc/Config.in --- linux-2.4.21-rc1.orig/sound/sparc/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/sparc/Config.in 2003-04-29 05:02:23.000000000 -0600 @@ -0,0 +1,9 @@ +# ALSA PowerMac drivers + +mainmenu_option next_comment +comment "ALSA Sparc devices" + +dep_tristate 'Sun AMD7930' CONFIG_SND_SUN_AMD7930 $CONFIG_SBUS +tristate 'Sun CS4231' CONFIG_SND_SUN_CS4231 + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/sparc/Makefile linux/sound/sparc/Makefile --- linux-2.4.21-rc1.orig/sound/sparc/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/sparc/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,25 @@ +# +# Makefile for ALSA +# Copyright (c) 2002 by David S. Miller +# + +O_TARGET := sparc.o + +list-multi := amd7930.o cs4231.o + +snd-sun-amd7930-objs := amd7930.o +#snd-sun-dbri-objs := dbri.o +snd-sun-cs4231-objs := cs4231.o + +obj-$(CONFIG_SND_SUN_AMD7930) += snd-sun-amd7930.o +#obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o +obj-$(CONFIG_SND_SUN_CS4231) += snd-sun-cs4231.o + +include $(TOPDIR)/Rules.make + +snd-sun-amd7930.o: $(snd-sun-amd7930-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sun-amd7930-objs) + +snd-sun-cs4231.o: $(snd-sun-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sun-cs4231-objs) + diff -urN linux-2.4.21-rc1.orig/sound/sparc/amd7930.c linux/sound/sparc/amd7930.c --- linux-2.4.21-rc1.orig/sound/sparc/amd7930.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/sparc/amd7930.c 2002-11-23 03:41:53.000000000 -0700 @@ -0,0 +1,1171 @@ +/* + * Driver for AMD7930 sound chips found on Sparcs. + * Copyright (C) 2002 David S. Miller + * + * Based entirely upon drivers/sbus/audio/amd7930.c which is: + * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu) + * + * --- Notes from Thomas's original driver --- + * This is the lowlevel driver for the AMD7930 audio chip found on all + * sun4c machines and some sun4m machines. + * + * The amd7930 is actually an ISDN chip which has a very simple + * integrated audio encoder/decoder. When Sun decided on what chip to + * use for audio, they had the brilliant idea of using the amd7930 and + * only connecting the audio encoder/decoder pins. + * + * Thanks to the AMD engineer who was able to get us the AMD79C30 + * databook which has all the programming information and gain tables. + * + * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the + * SparcStation 1+. The chip provides microphone and speaker interfaces + * which provide mono-channel audio at 8K samples per second via either + * 8-bit A-law or 8-bit mu-law encoding. Also, the chip features an + * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface, + * which performs basic D channel LAPD processing and provides raw + * B channel data. The digital audio channel, the two ISDN B channels, + * and two 64 Kbps channels to the microprocessor are all interconnected + * via a multiplexer. + * --- End of notes from Thoamas's original driver --- + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include +#include +#include + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Sun AMD7930 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Sun AMD7930 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Sun AMD7930 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_AUTHOR("Thomas K. Dyas and David S. Miller"); +MODULE_DESCRIPTION("Sun AMD7930"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Sun,AMD7930}}"); + +/* Device register layout. */ + +/* Register interface presented to the CPU by the amd7930. */ +#define AMD7930_CR 0x00UL /* Command Register (W) */ +#define AMD7930_IR AMD7930_CR /* Interrupt Register (R) */ +#define AMD7930_DR 0x01UL /* Data Register (R/W) */ +#define AMD7930_DSR1 0x02UL /* D-channel Status Register 1 (R) */ +#define AMD7930_DER 0x03UL /* D-channel Error Register (R) */ +#define AMD7930_DCTB 0x04UL /* D-channel Transmit Buffer (W) */ +#define AMD7930_DCRB AMD7930_DCTB /* D-channel Receive Buffer (R) */ +#define AMD7930_BBTB 0x05UL /* Bb-channel Transmit Buffer (W) */ +#define AMD7930_BBRB AMD7930_BBTB /* Bb-channel Receive Buffer (R) */ +#define AMD7930_BCTB 0x06UL /* Bc-channel Transmit Buffer (W) */ +#define AMD7930_BCRB AMD7930_BCTB /* Bc-channel Receive Buffer (R) */ +#define AMD7930_DSR2 0x07UL /* D-channel Status Register 2 (R) */ + +/* Indirect registers in the Main Audio Processor. */ +struct amd7930_map { + __u16 x[8]; + __u16 r[8]; + __u16 gx; + __u16 gr; + __u16 ger; + __u16 stgr; + __u16 ftgr; + __u16 atgr; + __u8 mmr1; + __u8 mmr2; +}; + +/* After an amd7930 interrupt, reading the Interrupt Register (ir) + * clears the interrupt and returns a bitmask indicating which + * interrupt source(s) require service. + */ + +#define AMR_IR_DTTHRSH 0x01 /* D-channel xmit threshold */ +#define AMR_IR_DRTHRSH 0x02 /* D-channel recv threshold */ +#define AMR_IR_DSRI 0x04 /* D-channel packet status */ +#define AMR_IR_DERI 0x08 /* D-channel error */ +#define AMR_IR_BBUF 0x10 /* B-channel data xfer */ +#define AMR_IR_LSRI 0x20 /* LIU status */ +#define AMR_IR_DSR2I 0x40 /* D-channel buffer status */ +#define AMR_IR_MLTFRMI 0x80 /* multiframe or PP */ + +/* The amd7930 has "indirect registers" which are accessed by writing + * the register number into the Command Register and then reading or + * writing values from the Data Register as appropriate. We define the + * AMR_* macros to be the indirect register numbers and AM_* macros to + * be bits in whatever register is referred to. + */ + +/* Initialization */ +#define AMR_INIT 0x21 +#define AM_INIT_ACTIVE 0x01 +#define AM_INIT_DATAONLY 0x02 +#define AM_INIT_POWERDOWN 0x03 +#define AM_INIT_DISABLE_INTS 0x04 +#define AMR_INIT2 0x20 +#define AM_INIT2_ENABLE_POWERDOWN 0x20 +#define AM_INIT2_ENABLE_MULTIFRAME 0x10 + +/* Line Interface Unit */ +#define AMR_LIU_LSR 0xA1 +#define AM_LIU_LSR_STATE 0x07 +#define AM_LIU_LSR_F3 0x08 +#define AM_LIU_LSR_F7 0x10 +#define AM_LIU_LSR_F8 0x20 +#define AM_LIU_LSR_HSW 0x40 +#define AM_LIU_LSR_HSW_CHG 0x80 +#define AMR_LIU_LPR 0xA2 +#define AMR_LIU_LMR1 0xA3 +#define AM_LIU_LMR1_B1_ENABL 0x01 +#define AM_LIU_LMR1_B2_ENABL 0x02 +#define AM_LIU_LMR1_F_DISABL 0x04 +#define AM_LIU_LMR1_FA_DISABL 0x08 +#define AM_LIU_LMR1_REQ_ACTIV 0x10 +#define AM_LIU_LMR1_F8_F3 0x20 +#define AM_LIU_LMR1_LIU_ENABL 0x40 +#define AMR_LIU_LMR2 0xA4 +#define AM_LIU_LMR2_DECHO 0x01 +#define AM_LIU_LMR2_DLOOP 0x02 +#define AM_LIU_LMR2_DBACKOFF 0x04 +#define AM_LIU_LMR2_EN_F3_INT 0x08 +#define AM_LIU_LMR2_EN_F8_INT 0x10 +#define AM_LIU_LMR2_EN_HSW_INT 0x20 +#define AM_LIU_LMR2_EN_F7_INT 0x40 +#define AMR_LIU_2_4 0xA5 +#define AMR_LIU_MF 0xA6 +#define AMR_LIU_MFSB 0xA7 +#define AMR_LIU_MFQB 0xA8 + +/* Multiplexor */ +#define AMR_MUX_MCR1 0x41 +#define AMR_MUX_MCR2 0x42 +#define AMR_MUX_MCR3 0x43 +#define AM_MUX_CHANNEL_B1 0x01 +#define AM_MUX_CHANNEL_B2 0x02 +#define AM_MUX_CHANNEL_Ba 0x03 +#define AM_MUX_CHANNEL_Bb 0x04 +#define AM_MUX_CHANNEL_Bc 0x05 +#define AM_MUX_CHANNEL_Bd 0x06 +#define AM_MUX_CHANNEL_Be 0x07 +#define AM_MUX_CHANNEL_Bf 0x08 +#define AMR_MUX_MCR4 0x44 +#define AM_MUX_MCR4_ENABLE_INTS 0x08 +#define AM_MUX_MCR4_REVERSE_Bb 0x10 +#define AM_MUX_MCR4_REVERSE_Bc 0x20 +#define AMR_MUX_1_4 0x45 + +/* Main Audio Processor */ +#define AMR_MAP_X 0x61 +#define AMR_MAP_R 0x62 +#define AMR_MAP_GX 0x63 +#define AMR_MAP_GR 0x64 +#define AMR_MAP_GER 0x65 +#define AMR_MAP_STGR 0x66 +#define AMR_MAP_FTGR_1_2 0x67 +#define AMR_MAP_ATGR_1_2 0x68 +#define AMR_MAP_MMR1 0x69 +#define AM_MAP_MMR1_ALAW 0x01 +#define AM_MAP_MMR1_GX 0x02 +#define AM_MAP_MMR1_GR 0x04 +#define AM_MAP_MMR1_GER 0x08 +#define AM_MAP_MMR1_X 0x10 +#define AM_MAP_MMR1_R 0x20 +#define AM_MAP_MMR1_STG 0x40 +#define AM_MAP_MMR1_LOOPBACK 0x80 +#define AMR_MAP_MMR2 0x6A +#define AM_MAP_MMR2_AINB 0x01 +#define AM_MAP_MMR2_LS 0x02 +#define AM_MAP_MMR2_ENABLE_DTMF 0x04 +#define AM_MAP_MMR2_ENABLE_TONEGEN 0x08 +#define AM_MAP_MMR2_ENABLE_TONERING 0x10 +#define AM_MAP_MMR2_DISABLE_HIGHPASS 0x20 +#define AM_MAP_MMR2_DISABLE_AUTOZERO 0x40 +#define AMR_MAP_1_10 0x6B +#define AMR_MAP_MMR3 0x6C +#define AMR_MAP_STRA 0x6D +#define AMR_MAP_STRF 0x6E +#define AMR_MAP_PEAKX 0x70 +#define AMR_MAP_PEAKR 0x71 +#define AMR_MAP_15_16 0x72 + +/* Data Link Controller */ +#define AMR_DLC_FRAR_1_2_3 0x81 +#define AMR_DLC_SRAR_1_2_3 0x82 +#define AMR_DLC_TAR 0x83 +#define AMR_DLC_DRLR 0x84 +#define AMR_DLC_DTCR 0x85 +#define AMR_DLC_DMR1 0x86 +#define AMR_DLC_DMR1_DTTHRSH_INT 0x01 +#define AMR_DLC_DMR1_DRTHRSH_INT 0x02 +#define AMR_DLC_DMR1_TAR_ENABL 0x04 +#define AMR_DLC_DMR1_EORP_INT 0x08 +#define AMR_DLC_DMR1_EN_ADDR1 0x10 +#define AMR_DLC_DMR1_EN_ADDR2 0x20 +#define AMR_DLC_DMR1_EN_ADDR3 0x40 +#define AMR_DLC_DMR1_EN_ADDR4 0x80 +#define AMR_DLC_DMR1_EN_ADDRS 0xf0 +#define AMR_DLC_DMR2 0x87 +#define AMR_DLC_DMR2_RABRT_INT 0x01 +#define AMR_DLC_DMR2_RESID_INT 0x02 +#define AMR_DLC_DMR2_COLL_INT 0x04 +#define AMR_DLC_DMR2_FCS_INT 0x08 +#define AMR_DLC_DMR2_OVFL_INT 0x10 +#define AMR_DLC_DMR2_UNFL_INT 0x20 +#define AMR_DLC_DMR2_OVRN_INT 0x40 +#define AMR_DLC_DMR2_UNRN_INT 0x80 +#define AMR_DLC_1_7 0x88 +#define AMR_DLC_DRCR 0x89 +#define AMR_DLC_RNGR1 0x8A +#define AMR_DLC_RNGR2 0x8B +#define AMR_DLC_FRAR4 0x8C +#define AMR_DLC_SRAR4 0x8D +#define AMR_DLC_DMR3 0x8E +#define AMR_DLC_DMR3_VA_INT 0x01 +#define AMR_DLC_DMR3_EOTP_INT 0x02 +#define AMR_DLC_DMR3_LBRP_INT 0x04 +#define AMR_DLC_DMR3_RBA_INT 0x08 +#define AMR_DLC_DMR3_LBT_INT 0x10 +#define AMR_DLC_DMR3_TBE_INT 0x20 +#define AMR_DLC_DMR3_RPLOST_INT 0x40 +#define AMR_DLC_DMR3_KEEP_FCS 0x80 +#define AMR_DLC_DMR4 0x8F +#define AMR_DLC_DMR4_RCV_1 0x00 +#define AMR_DLC_DMR4_RCV_2 0x01 +#define AMR_DLC_DMR4_RCV_4 0x02 +#define AMR_DLC_DMR4_RCV_8 0x03 +#define AMR_DLC_DMR4_RCV_16 0x01 +#define AMR_DLC_DMR4_RCV_24 0x02 +#define AMR_DLC_DMR4_RCV_30 0x03 +#define AMR_DLC_DMR4_XMT_1 0x00 +#define AMR_DLC_DMR4_XMT_2 0x04 +#define AMR_DLC_DMR4_XMT_4 0x08 +#define AMR_DLC_DMR4_XMT_8 0x0c +#define AMR_DLC_DMR4_XMT_10 0x08 +#define AMR_DLC_DMR4_XMT_14 0x0c +#define AMR_DLC_DMR4_IDLE_MARK 0x00 +#define AMR_DLC_DMR4_IDLE_FLAG 0x10 +#define AMR_DLC_DMR4_ADDR_BOTH 0x00 +#define AMR_DLC_DMR4_ADDR_1ST 0x20 +#define AMR_DLC_DMR4_ADDR_2ND 0xa0 +#define AMR_DLC_DMR4_CR_ENABLE 0x40 +#define AMR_DLC_12_15 0x90 +#define AMR_DLC_ASR 0x91 +#define AMR_DLC_EFCR 0x92 +#define AMR_DLC_EFCR_EXTEND_FIFO 0x01 +#define AMR_DLC_EFCR_SEC_PKT_INT 0x02 + +#define AMR_DSR1_VADDR 0x01 +#define AMR_DSR1_EORP 0x02 +#define AMR_DSR1_PKT_IP 0x04 +#define AMR_DSR1_DECHO_ON 0x08 +#define AMR_DSR1_DLOOP_ON 0x10 +#define AMR_DSR1_DBACK_OFF 0x20 +#define AMR_DSR1_EOTP 0x40 +#define AMR_DSR1_CXMT_ABRT 0x80 + +#define AMR_DSR2_LBRP 0x01 +#define AMR_DSR2_RBA 0x02 +#define AMR_DSR2_RPLOST 0x04 +#define AMR_DSR2_LAST_BYTE 0x08 +#define AMR_DSR2_TBE 0x10 +#define AMR_DSR2_MARK_IDLE 0x20 +#define AMR_DSR2_FLAG_IDLE 0x40 +#define AMR_DSR2_SECOND_PKT 0x80 + +#define AMR_DER_RABRT 0x01 +#define AMR_DER_RFRAME 0x02 +#define AMR_DER_COLLISION 0x04 +#define AMR_DER_FCS 0x08 +#define AMR_DER_OVFL 0x10 +#define AMR_DER_UNFL 0x20 +#define AMR_DER_OVRN 0x40 +#define AMR_DER_UNRN 0x80 + +/* Peripheral Port */ +#define AMR_PP_PPCR1 0xC0 +#define AMR_PP_PPSR 0xC1 +#define AMR_PP_PPIER 0xC2 +#define AMR_PP_MTDR 0xC3 +#define AMR_PP_MRDR 0xC3 +#define AMR_PP_CITDR0 0xC4 +#define AMR_PP_CIRDR0 0xC4 +#define AMR_PP_CITDR1 0xC5 +#define AMR_PP_CIRDR1 0xC5 +#define AMR_PP_PPCR2 0xC8 +#define AMR_PP_PPCR3 0xC9 + +typedef struct snd_amd7930 { + spinlock_t lock; + unsigned long regs; + u32 flags; +#define AMD7930_FLAG_PLAYBACK 0x00000001 +#define AMD7930_FLAG_CAPTURE 0x00000002 + + struct amd7930_map map; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + /* Playback/Capture buffer state. */ + unsigned char *p_orig, *p_cur; + int p_left; + unsigned char *c_orig, *c_cur; + int c_left; + + int rgain; + int pgain; + int mgain; + + struct sbus_dev *sdev; + unsigned int irq; + unsigned int regs_size; + struct snd_amd7930 *next; +} amd7930_t; +#define chip_t amd7930_t + +static amd7930_t *amd7930_list; + +/* Idle the AMD7930 chip. The amd->lock is not held. */ +static __inline__ void amd7930_idle(amd7930_t *amd) +{ + unsigned long flags; + + spin_lock_irqsave(&amd->lock, flags); + sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR); + sbus_writeb(0, amd->regs + AMD7930_DR); + spin_unlock_irqrestore(&amd->lock, flags); +} + +/* Enable chip interrupts. The amd->lock is not held. */ +static __inline__ void amd7930_enable_ints(amd7930_t *amd) +{ + unsigned long flags; + + spin_lock_irqsave(&amd->lock, flags); + sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR); + sbus_writeb(AM_INIT_ACTIVE, amd->regs + AMD7930_DR); + spin_unlock_irqrestore(&amd->lock, flags); +} + +/* Disable chip interrupts. The amd->lock is not held. */ +static __inline__ void amd7930_disable_ints(amd7930_t *amd) +{ + unsigned long flags; + + spin_lock_irqsave(&amd->lock, flags); + sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR); + sbus_writeb(AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS, amd->regs + AMD7930_DR); + spin_unlock_irqrestore(&amd->lock, flags); +} + +/* Commit amd7930_map settings to the hardware. + * The amd->lock is held and local interrupts are disabled. + */ +static void __amd7930_write_map(amd7930_t *amd) +{ + struct amd7930_map *map = &amd->map; + + sbus_writeb(AMR_MAP_GX, amd->regs + AMD7930_CR); + sbus_writeb(((map->gx >> 0) & 0xff), amd->regs + AMD7930_DR); + sbus_writeb(((map->gx >> 8) & 0xff), amd->regs + AMD7930_DR); + + sbus_writeb(AMR_MAP_GR, amd->regs + AMD7930_CR); + sbus_writeb(((map->gr >> 0) & 0xff), amd->regs + AMD7930_DR); + sbus_writeb(((map->gr >> 8) & 0xff), amd->regs + AMD7930_DR); + + sbus_writeb(AMR_MAP_STGR, amd->regs + AMD7930_CR); + sbus_writeb(((map->stgr >> 0) & 0xff), amd->regs + AMD7930_DR); + sbus_writeb(((map->stgr >> 8) & 0xff), amd->regs + AMD7930_DR); + + sbus_writeb(AMR_MAP_GER, amd->regs + AMD7930_CR); + sbus_writeb(((map->ger >> 0) & 0xff), amd->regs + AMD7930_DR); + sbus_writeb(((map->ger >> 8) & 0xff), amd->regs + AMD7930_DR); + + sbus_writeb(AMR_MAP_MMR1, amd->regs + AMD7930_CR); + sbus_writeb(map->mmr1, amd->regs + AMD7930_DR); + + sbus_writeb(AMR_MAP_MMR2, amd->regs + AMD7930_CR); + sbus_writeb(map->mmr2, amd->regs + AMD7930_DR); +} + +/* gx, gr & stg gains. this table must contain 256 elements with + * the 0th being "infinity" (the magic value 9008). The remaining + * elements match sun's gain curve (but with higher resolution): + * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps. + */ +static __const__ __u16 gx_coeff[256] = { + 0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33, + 0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22, + 0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b, + 0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb, + 0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a, + 0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213, + 0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231, + 0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4, + 0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2, + 0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa, + 0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b, + 0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b, + 0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd, + 0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808, + 0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243, + 0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224, + 0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb, + 0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33, + 0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32, + 0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323, + 0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a, + 0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23, + 0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1, + 0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333, + 0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227, + 0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6, + 0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2, + 0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba, + 0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033, + 0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021, + 0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012, + 0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e, +}; + +static __const__ __u16 ger_coeff[] = { + 0x431f, /* 5. dB */ + 0x331f, /* 5.5 dB */ + 0x40dd, /* 6. dB */ + 0x11dd, /* 6.5 dB */ + 0x440f, /* 7. dB */ + 0x411f, /* 7.5 dB */ + 0x311f, /* 8. dB */ + 0x5520, /* 8.5 dB */ + 0x10dd, /* 9. dB */ + 0x4211, /* 9.5 dB */ + 0x410f, /* 10. dB */ + 0x111f, /* 10.5 dB */ + 0x600b, /* 11. dB */ + 0x00dd, /* 11.5 dB */ + 0x4210, /* 12. dB */ + 0x110f, /* 13. dB */ + 0x7200, /* 14. dB */ + 0x2110, /* 15. dB */ + 0x2200, /* 15.9 dB */ + 0x000b, /* 16.9 dB */ + 0x000f /* 18. dB */ +}; +#define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0])) + +/* Update amd7930_map settings and program them into the hardware. + * The amd->lock is held and local interrupts are disabled. + */ +static void __amd7930_update_map(amd7930_t *amd) +{ + struct amd7930_map *map = &amd->map; + int level; + + map->gx = gx_coeff[amd->rgain]; + map->stgr = gx_coeff[amd->mgain]; + level = (amd->pgain * (256 + NR_GER_COEFFS)) >> 8; + if (level >= 256) { + map->ger = ger_coeff[level - 256]; + map->gr = gx_coeff[255]; + } else { + map->ger = ger_coeff[0]; + map->gr = gx_coeff[level]; + } + __amd7930_write_map(amd); +} + +static void snd_amd7930_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + amd7930_t *amd = dev_id; + unsigned int elapsed; + u8 ir; + + spin_lock(&amd->lock); + + elapsed = 0; + + ir = sbus_readb(amd->regs + AMD7930_IR); + if (ir & AMR_IR_BBUF) { + u8 byte; + + if (amd->flags & AMD7930_FLAG_PLAYBACK) { + if (amd->p_left > 0) { + byte = *(amd->p_cur++); + amd->p_left--; + sbus_writeb(byte, amd->regs + AMD7930_BBTB); + if (amd->p_left == 0) + elapsed |= AMD7930_FLAG_PLAYBACK; + } else + sbus_writeb(0, amd->regs + AMD7930_BBTB); + } else if (amd->flags & AMD7930_FLAG_CAPTURE) { + byte = sbus_readb(amd->regs + AMD7930_BBRB); + if (amd->c_left > 0) { + *(amd->c_cur++) = byte; + amd->c_left--; + if (amd->c_left == 0) + elapsed |= AMD7930_FLAG_CAPTURE; + } + } + } + spin_unlock(&amd->lock); + + if (elapsed & AMD7930_FLAG_PLAYBACK) + snd_pcm_period_elapsed(amd->playback_substream); + else + snd_pcm_period_elapsed(amd->capture_substream); +} + +static int snd_amd7930_trigger(amd7930_t *amd, unsigned int flag, int cmd) +{ + unsigned long flags; + int result = 0; + + spin_lock_irqsave(&amd->lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(amd->flags & flag)) { + amd->flags |= flag; + + /* Enable B channel interrupts. */ + sbus_writeb(AMR_MUX_MCR4, amd->regs + AMD7930_CR); + sbus_writeb(AM_MUX_MCR4_ENABLE_INTS, amd->regs + AMD7930_DR); + } + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (amd->flags & flag) { + amd->flags &= ~flag; + + /* Disable B channel interrupts. */ + sbus_writeb(AMR_MUX_MCR4, amd->regs + AMD7930_CR); + sbus_writeb(0, amd->regs + AMD7930_DR); + } + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&amd->lock, flags); + + return result; +} + +static int snd_amd7930_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + return snd_amd7930_trigger(amd, AMD7930_FLAG_PLAYBACK, cmd); +} + +static int snd_amd7930_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + return snd_amd7930_trigger(amd, AMD7930_FLAG_CAPTURE, cmd); +} + +static int snd_amd7930_playback_prepare(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned long flags; + u8 new_mmr1; + + spin_lock_irqsave(&amd->lock, flags); + + amd->flags |= AMD7930_FLAG_PLAYBACK; + + /* Setup the pseudo-dma transfer pointers. */ + amd->p_orig = amd->p_cur = runtime->dma_area; + amd->p_left = size; + + /* Put the chip into the correct encoding format. */ + new_mmr1 = amd->map.mmr1; + if (runtime->format == SNDRV_PCM_FORMAT_A_LAW) + new_mmr1 |= AM_MAP_MMR1_ALAW; + else + new_mmr1 &= ~AM_MAP_MMR1_ALAW; + if (new_mmr1 != amd->map.mmr1) { + amd->map.mmr1 = new_mmr1; + __amd7930_update_map(amd); + } + + spin_unlock_irqrestore(&amd->lock, flags); + + return 0; +} + +static int snd_amd7930_capture_prepare(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned long flags; + u8 new_mmr1; + + spin_lock_irqsave(&amd->lock, flags); + + amd->flags |= AMD7930_FLAG_CAPTURE; + + /* Setup the pseudo-dma transfer pointers. */ + amd->c_orig = amd->c_cur = runtime->dma_area; + amd->c_left = size; + + /* Put the chip into the correct encoding format. */ + new_mmr1 = amd->map.mmr1; + if (runtime->format == SNDRV_PCM_FORMAT_A_LAW) + new_mmr1 |= AM_MAP_MMR1_ALAW; + else + new_mmr1 &= ~AM_MAP_MMR1_ALAW; + if (new_mmr1 != amd->map.mmr1) { + amd->map.mmr1 = new_mmr1; + __amd7930_update_map(amd); + } + + spin_unlock_irqrestore(&amd->lock, flags); + + return 0; +} + +static snd_pcm_uframes_t snd_amd7930_playback_pointer(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(amd->flags & AMD7930_FLAG_PLAYBACK)) + return 0; + ptr = amd->p_cur - amd->p_orig; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_amd7930_capture_pointer(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(amd->flags & AMD7930_FLAG_CAPTURE)) + return 0; + + ptr = amd->c_cur - amd->c_orig; + return bytes_to_frames(substream->runtime, ptr); +} + +/* Playback and capture have identical properties. */ +static snd_pcm_hardware_t snd_amd7930_pcm_hw = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_HALF_DUPLEX), + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + .rates = SNDRV_PCM_RATE_8000, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (64*1024), + .period_bytes_min = 1, + .period_bytes_max = (64*1024), + .periods_min = 1, + .periods_max = 1024, +}; + +static int snd_amd7930_playback_open(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + amd->playback_substream = substream; + runtime->hw = snd_amd7930_pcm_hw; + return 0; +} + +static int snd_amd7930_capture_open(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + amd->capture_substream = substream; + runtime->hw = snd_amd7930_pcm_hw; + return 0; +} + +static int snd_amd7930_playback_close(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + + amd->playback_substream = NULL; + return 0; +} + +static int snd_amd7930_capture_close(snd_pcm_substream_t * substream) +{ + amd7930_t *amd = snd_pcm_substream_chip(substream); + + amd->capture_substream = NULL; + return 0; +} + +static int snd_amd7930_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_amd7930_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static snd_pcm_ops_t snd_amd7930_playback_ops = { + .open = snd_amd7930_playback_open, + .close = snd_amd7930_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_amd7930_hw_params, + .hw_free = snd_amd7930_hw_free, + .prepare = snd_amd7930_playback_prepare, + .trigger = snd_amd7930_playback_trigger, + .pointer = snd_amd7930_playback_pointer, +}; + +static snd_pcm_ops_t snd_amd7930_capture_ops = { + .open = snd_amd7930_capture_open, + .close = snd_amd7930_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_amd7930_hw_params, + .hw_free = snd_amd7930_hw_free, + .prepare = snd_amd7930_capture_prepare, + .trigger = snd_amd7930_capture_trigger, + .pointer = snd_amd7930_capture_pointer, +}; + +static void snd_amd7930_pcm_free(snd_pcm_t *pcm) +{ + amd7930_t *amd = snd_magic_cast(amd7930_t, pcm->private_data, return); + + amd->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init snd_amd7930_pcm(amd7930_t *amd) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(amd->card, + /* ID */ "sun_amd7930", + /* device */ 0, + /* playback count */ 1, + /* capture count */ 1, &pcm)) < 0) + return err; + snd_assert(pcm != NULL, return -EINVAL); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops); + + pcm->private_data = amd; + pcm->private_free = snd_amd7930_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, amd->card->shortname); + amd->pcm = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 64*1024, GFP_KERNEL); + + return 0; +} + +#define VOLUME_MONITOR 0 +#define VOLUME_CAPTURE 1 +#define VOLUME_PLAYBACK 2 + +static int snd_amd7930_info_volume(snd_kcontrol_t *kctl, snd_ctl_elem_info_t *uinfo) +{ + int type = kctl->private_value; + + snd_assert(type == VOLUME_MONITOR || + type == VOLUME_CAPTURE || + type == VOLUME_PLAYBACK, return -EINVAL); + (void) type; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + + return 0; +} + +static int snd_amd7930_get_volume(snd_kcontrol_t *kctl, snd_ctl_elem_value_t *ucontrol) +{ + amd7930_t *amd = snd_kcontrol_chip(kctl); + int type = kctl->private_value; + int *swval; + + snd_assert(type == VOLUME_MONITOR || + type == VOLUME_CAPTURE || + type == VOLUME_PLAYBACK, return -EINVAL); + + switch (type) { + case VOLUME_MONITOR: + swval = &amd->mgain; + break; + case VOLUME_CAPTURE: + swval = &amd->rgain; + break; + case VOLUME_PLAYBACK: + default: + swval = &amd->pgain; + break; + }; + + ucontrol->value.integer.value[0] = *swval; + + return 0; +} + +static int snd_amd7930_put_volume(snd_kcontrol_t *kctl, snd_ctl_elem_value_t *ucontrol) +{ + amd7930_t *amd = snd_kcontrol_chip(kctl); + unsigned long flags; + int type = kctl->private_value; + int *swval, change; + + snd_assert(type == VOLUME_MONITOR || + type == VOLUME_CAPTURE || + type == VOLUME_PLAYBACK, return -EINVAL); + + switch (type) { + case VOLUME_MONITOR: + swval = &amd->mgain; + break; + case VOLUME_CAPTURE: + swval = &amd->rgain; + break; + case VOLUME_PLAYBACK: + default: + swval = &amd->pgain; + break; + }; + + spin_lock_irqsave(&amd->lock, flags); + + if (*swval != ucontrol->value.integer.value[0]) { + *swval = ucontrol->value.integer.value[0]; + __amd7930_update_map(amd); + change = 1; + } else + change = 0; + + spin_unlock_irqrestore(&amd->lock, flags); + + return change; +} + +static snd_kcontrol_new_t amd7930_controls[] __initdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Volume", + .index = 0, + .info = snd_amd7930_info_volume, + .get = snd_amd7930_get_volume, + .put = snd_amd7930_put_volume, + .private_value = VOLUME_MONITOR, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .index = 0, + .info = snd_amd7930_info_volume, + .get = snd_amd7930_get_volume, + .put = snd_amd7930_put_volume, + .private_value = VOLUME_CAPTURE, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Playback Volume", + .index = 0, + .info = snd_amd7930_info_volume, + .get = snd_amd7930_get_volume, + .put = snd_amd7930_put_volume, + .private_value = VOLUME_PLAYBACK, + }, +}; + +#define NUM_AMD7930_CONTROLS (sizeof(amd7930_controls)/sizeof(snd_kcontrol_new_t)) + +static int __init snd_amd7930_mixer(amd7930_t *amd) +{ + snd_card_t *card; + int idx, err; + + snd_assert(amd != NULL && amd->card != NULL, return -EINVAL); + + card = amd->card; + strcpy(card->mixername, card->shortname); + + for (idx = 0; idx < NUM_AMD7930_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, + snd_ctl_new1(&amd7930_controls[idx], amd))) < 0) + return err; + } + + return 0; +} + +static int snd_amd7930_free(amd7930_t *amd) +{ + amd7930_idle(amd); + + if (amd->irq) + free_irq(amd->irq, amd); + + if (amd->regs) + sbus_iounmap(amd->regs, amd->regs_size); + + snd_magic_kfree(amd); + + return 0; +} + +static int snd_amd7930_dev_free(snd_device_t *device) +{ + amd7930_t *amd = snd_magic_cast(amd7930_t, device->device_data, return -ENXIO); + + return snd_amd7930_free(amd); +} + +static snd_device_ops_t snd_amd7930_dev_ops = { + .dev_free = snd_amd7930_dev_free, +}; + +static int __init snd_amd7930_create(snd_card_t *card, + struct sbus_dev *sdev, + struct resource *rp, + unsigned int reg_size, + struct linux_prom_irqs *irq_prop, + int dev, + amd7930_t **ramd) +{ + unsigned long flags; + amd7930_t *amd; + int err; + + *ramd = NULL; + amd = snd_magic_kcalloc(amd7930_t, 0, GFP_KERNEL); + if (amd == NULL) + return -ENOMEM; + + spin_lock_init(&amd->lock); + amd->card = card; + amd->sdev = sdev; + amd->regs_size = reg_size; + + amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930"); + if (!amd->regs) { + snd_printk("amd7930-%d: Unable to map chip registers.\n", dev); + return -EIO; + } + + amd7930_idle(amd); + + if (request_irq(irq_prop->pri, snd_amd7930_interrupt, + SA_INTERRUPT | SA_SHIRQ, "amd7930", amd)) { + snd_amd7930_free(amd); + snd_printk("amd7930-%d: Unable to grab IRQ %s\n", + dev, + __irq_itoa(irq_prop->pri)); + return -EBUSY; + } + amd->irq = irq_prop->pri; + + amd7930_enable_ints(amd); + + spin_lock_irqsave(&amd->lock, flags); + + amd->rgain = 128; + amd->pgain = 200; + amd->mgain = 0; + + memset(&amd->map, 0, sizeof(amd->map)); + amd->map.mmr1 = (AM_MAP_MMR1_GX | AM_MAP_MMR1_GER | + AM_MAP_MMR1_GR | AM_MAP_MMR1_STG); + amd->map.mmr2 = (AM_MAP_MMR2_LS | AM_MAP_MMR2_AINB); + + __amd7930_update_map(amd); + + /* Always MUX audio (Ba) to channel Bb. */ + sbus_writeb(AMR_MUX_MCR1, amd->regs + AMD7930_CR); + sbus_writeb(AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4), + amd->regs + AMD7930_DR); + + spin_unlock_irqrestore(&amd->lock, flags); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + amd, &snd_amd7930_dev_ops)) < 0) { + snd_amd7930_free(amd); + return err; + } + + *ramd = amd; + return 0; +} + +static int __init amd7930_attach(int prom_node, struct sbus_dev *sdev) +{ + static int dev; + struct linux_prom_registers reg_prop; + struct linux_prom_irqs irq_prop; + struct resource res, *rp; + snd_card_t *card; + amd7930_t *amd; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + err = prom_getproperty(prom_node, "intr", + (char *) &irq_prop, sizeof(irq_prop)); + if (err < 0) { + snd_printk("amd7930-%d: Firmware node lacks IRQ property.\n", dev); + return -ENODEV; + } + + err = prom_getproperty(prom_node, "reg", + (char *) ®_prop, sizeof(reg_prop)); + if (err < 0) { + snd_printk("amd7930-%d: Firmware node lacks register property.\n", dev); + return -ENODEV; + } + + if (sdev) { + rp = &sdev->resource[0]; + } else { + rp = &res; + rp->start = reg_prop.phys_addr; + rp->end = rp->start + reg_prop.reg_size - 1; + rp->flags = IORESOURCE_IO | (reg_prop.which_io & 0xff); + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "AMD7930"); + strcpy(card->shortname, "Sun AMD7930"); + sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s", + card->shortname, + rp->flags & 0xffL, + rp->start, + __irq_itoa(irq_prop.pri)); + + if ((err = snd_amd7930_create(card, sdev, rp, reg_prop.reg_size, + &irq_prop, dev, &amd)) < 0) + goto out_err; + + if ((err = snd_amd7930_pcm(amd)) < 0) + goto out_err; + + if ((err = snd_amd7930_mixer(amd)) < 0) + goto out_err; + + if ((err = snd_card_register(card)) < 0) + goto out_err; + + amd->next = amd7930_list; + amd7930_list = amd; + + dev++; + return 0; + +out_err: + snd_card_free(card); + return err; +} + +static int __init amd7930_init(void) +{ + struct sbus_bus *sbus; + struct sbus_dev *sdev; + int node, found; + + found = 0; + + /* Try to find the sun4c "audio" node first. */ + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(node, "audio"); + if (node && amd7930_attach(node, NULL) == 0) + found++; + + /* Probe each SBUS for amd7930 chips. */ + for_all_sbusdev(sdev, sbus) { + if (!strcmp(sdev->prom_name, "audio")) { + if (amd7930_attach(sdev->prom_node, sdev) == 0) + found++; + } + } + + return (found > 0) ? 0 : -EIO; +} + +static void __exit amd7930_exit(void) +{ + amd7930_t *p = amd7930_list; + + while (p != NULL) { + amd7930_t *next = p->next; + + snd_card_free(p->card); + + p = next; + } + + amd7930_list = NULL; +} + +module_init(amd7930_init); +module_exit(amd7930_exit); + +#ifndef MODULE + +/* format is: snd-sun-amd7930=index,id,enable */ + +static int __init alsa_card_sun_amd7930_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&index[nr_dev]) == 2 && + get_option(&str,&id[nr_dev]) == 2 && + get_id(&str,&enable[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sun-amd7930=", alsa_card_sun_amd7930_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/sparc/cs4231.c linux/sound/sparc/cs4231.c --- linux-2.4.21-rc1.orig/sound/sparc/cs4231.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/sparc/cs4231.c 2002-12-04 13:54:00.000000000 -0700 @@ -0,0 +1,2269 @@ +/* + * Driver for CS4231 sound chips found on Sparcs. + * Copyright (C) 2002 David S. Miller + * + * Based entirely upon drivers/sbus/audio/cs4231.c which is: + * Copyright (C) 1996, 1997, 1998, 1998 Derrick J Brashear (shadow@andrew.cmu.edu) + * and also sound/isa/cs423x/cs4231_lib.c which is: + * Copyright (c) by Jaroslav Kysela + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include + +#include +#include + +#ifdef CONFIG_SBUS +#define SBUS_SUPPORT +#endif + +#ifdef SBUS_SUPPORT +#include +#endif + +#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64) +#define EBUS_SUPPORT +#endif + +#ifdef EBUS_SUPPORT +#include +#include +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_AUTHOR("Jaroslav Kysela, Derrick J. Brashear and David S. Miller"); +MODULE_DESCRIPTION("Sun CS4231"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Sun,CS4231}}"); + +typedef struct snd_cs4231 { + spinlock_t lock; + unsigned long port; +#ifdef EBUS_SUPPORT + struct ebus_dma_info eb2c; + struct ebus_dma_info eb2p; +#endif + + u32 flags; +#define CS4231_FLAG_EBUS 0x00000001 +#define CS4231_FLAG_PLAYBACK 0x00000002 +#define CS4231_FLAG_CAPTURE 0x00000004 + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + unsigned int p_periods_sent; + snd_pcm_substream_t *capture_substream; + unsigned int c_periods_sent; + snd_timer_t *timer; + + unsigned short mode; +#define CS4231_MODE_NONE 0x0000 +#define CS4231_MODE_PLAY 0x0001 +#define CS4231_MODE_RECORD 0x0002 +#define CS4231_MODE_TIMER 0x0004 +#define CS4231_MODE_OPEN (CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER) + + unsigned char image[32]; /* registers image */ + int mce_bit; + int calibrate_mute; + struct semaphore mce_mutex; + struct semaphore open_mutex; + + union { +#ifdef SBUS_SUPPORT + struct sbus_dev *sdev; +#endif +#ifdef EBUS_SUPPORT + struct pci_dev *pdev; +#endif + } dev_u; + unsigned int irq[2]; + unsigned int regs_size; + struct snd_cs4231 *next; +} cs4231_t; +#define chip_t cs4231_t + +static cs4231_t *cs4231_list; + +/* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for + * now.... -DaveM + */ + +/* IO ports */ + +#define CS4231P(chip, x) ((chip)->port + c_d_c_CS4231##x) + +/* XXX offsets are different than PC ISA chips... */ +#define c_d_c_CS4231REGSEL 0x0 +#define c_d_c_CS4231REG 0x4 +#define c_d_c_CS4231STATUS 0x8 +#define c_d_c_CS4231PIO 0xc + +/* codec registers */ + +#define CS4231_LEFT_INPUT 0x00 /* left input control */ +#define CS4231_RIGHT_INPUT 0x01 /* right input control */ +#define CS4231_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define CS4231_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define CS4231_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define CS4231_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define CS4231_LEFT_OUTPUT 0x06 /* left output control register */ +#define CS4231_RIGHT_OUTPUT 0x07 /* right output control register */ +#define CS4231_PLAYBK_FORMAT 0x08 /* clock and data format - playback - bits 7-0 MCE */ +#define CS4231_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define CS4231_PIN_CTRL 0x0a /* pin control */ +#define CS4231_TEST_INIT 0x0b /* test and initialization */ +#define CS4231_MISC_INFO 0x0c /* miscellaneaous information */ +#define CS4231_LOOPBACK 0x0d /* loopback control */ +#define CS4231_PLY_UPR_CNT 0x0e /* playback upper base count */ +#define CS4231_PLY_LWR_CNT 0x0f /* playback lower base count */ +#define CS4231_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ +#define CS4231_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ +#define CS4231_LEFT_LINE_IN 0x12 /* left line input control */ +#define CS4231_RIGHT_LINE_IN 0x13 /* right line input control */ +#define CS4231_TIMER_LOW 0x14 /* timer low byte */ +#define CS4231_TIMER_HIGH 0x15 /* timer high byte */ +#define CS4231_LEFT_MIC_INPUT 0x16 /* left MIC input control register (InterWave only) */ +#define CS4231_RIGHT_MIC_INPUT 0x17 /* right MIC input control register (InterWave only) */ +#define CS4236_EXT_REG 0x17 /* extended register access */ +#define CS4231_IRQ_STATUS 0x18 /* irq status register */ +#define CS4231_LINE_LEFT_OUTPUT 0x19 /* left line output control register (InterWave only) */ +#define CS4231_VERSION 0x19 /* CS4231(A) - version values */ +#define CS4231_MONO_CTRL 0x1a /* mono input/output control */ +#define CS4231_LINE_RIGHT_OUTPUT 0x1b /* right line output control register (InterWave only) */ +#define CS4235_LEFT_MASTER 0x1b /* left master output control */ +#define CS4231_REC_FORMAT 0x1c /* clock and data format - record - bits 7-0 MCE */ +#define CS4231_PLY_VAR_FREQ 0x1d /* playback variable frequency */ +#define CS4235_RIGHT_MASTER 0x1d /* right master output control */ +#define CS4231_REC_UPR_CNT 0x1e /* record upper count */ +#define CS4231_REC_LWR_CNT 0x1f /* record lower count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define CS4231_INIT 0x80 /* CODEC is initializing */ +#define CS4231_MCE 0x40 /* mode change enable */ +#define CS4231_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define CS4231_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for codec irq status */ + +#define CS4231_PLAYBACK_IRQ 0x10 +#define CS4231_RECORD_IRQ 0x20 +#define CS4231_TIMER_IRQ 0x40 +#define CS4231_ALL_IRQS 0x70 +#define CS4231_REC_UNDERRUN 0x08 +#define CS4231_REC_OVERRUN 0x04 +#define CS4231_PLY_OVERRUN 0x02 +#define CS4231_PLY_UNDERRUN 0x01 + +/* definitions for CS4231_LEFT_INPUT and CS4231_RIGHT_INPUT registers */ + +#define CS4231_ENABLE_MIC_GAIN 0x20 + +#define CS4231_MIXS_LINE 0x00 +#define CS4231_MIXS_AUX1 0x40 +#define CS4231_MIXS_MIC 0x80 +#define CS4231_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - CS4231_PLAYBK_FORMAT */ + +#define CS4231_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define CS4231_ALAW_8 0x60 /* 8-bit A-law companded */ +#define CS4231_ULAW_8 0x20 /* 8-bit U-law companded */ +#define CS4231_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define CS4231_LINEAR_16_BIG 0xc0 /* 16-bit twos complement data - big endian */ +#define CS4231_ADPCM_16 0xa0 /* 16-bit ADPCM */ +#define CS4231_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define CS4231_XTAL1 0x00 /* 24.576 crystal */ +#define CS4231_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - CS4231_IFACE_CTRL */ + +#define CS4231_RECORD_PIO 0x80 /* record PIO enable */ +#define CS4231_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define CS4231_CALIB_MODE 0x18 /* calibration mode bits */ +#define CS4231_AUTOCALIB 0x08 /* auto calibrate */ +#define CS4231_SINGLE_DMA 0x04 /* use single DMA channel */ +#define CS4231_RECORD_ENABLE 0x02 /* record enable */ +#define CS4231_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - CS4231_PIN_CTRL */ + +#define CS4231_IRQ_ENABLE 0x02 /* enable IRQ */ +#define CS4231_XCTL1 0x40 /* external control #1 */ +#define CS4231_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - CS4231_TEST_INIT */ + +#define CS4231_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define CS4231_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* definitions for misc control register - CS4231_MISC_INFO */ + +#define CS4231_MODE2 0x40 /* MODE 2 */ +#define CS4231_IW_MODE3 0x6c /* MODE 3 - InterWave enhanced mode */ +#define CS4231_4236_MODE3 0xe0 /* MODE 3 - CS4236+ enhanced mode */ + +/* definitions for alternate feature 1 register - CS4231_ALT_FEATURE_1 */ + +#define CS4231_DACZ 0x01 /* zero DAC when underrun */ +#define CS4231_TIMER_ENABLE 0x40 /* codec timer enable */ +#define CS4231_OLB 0x80 /* output level bit */ + +/* SBUS DMA register defines. */ + +#define APCCSR 0x10UL /* APC DMA CSR */ +#define APCCVA 0x20UL /* APC Capture DMA Address */ +#define APCCC 0x24UL /* APC Capture Count */ +#define APCCNVA 0x28UL /* APC Capture DMA Next Address */ +#define APCCNC 0x2cUL /* APC Capture Next Count */ +#define APCPVA 0x30UL /* APC Play DMA Address */ +#define APCPC 0x34UL /* APC Play Count */ +#define APCPNVA 0x38UL /* APC Play DMA Next Address */ +#define APCPNC 0x3cUL /* APC Play Next Count */ + +/* APCCSR bits */ + +#define APC_INT_PENDING 0x800000 /* Interrupt Pending */ +#define APC_PLAY_INT 0x400000 /* Playback interrupt */ +#define APC_CAPT_INT 0x200000 /* Capture interrupt */ +#define APC_GENL_INT 0x100000 /* General interrupt */ +#define APC_XINT_ENA 0x80000 /* General ext int. enable */ +#define APC_XINT_PLAY 0x40000 /* Playback ext intr */ +#define APC_XINT_CAPT 0x20000 /* Capture ext intr */ +#define APC_XINT_GENL 0x10000 /* Error ext intr */ +#define APC_XINT_EMPT 0x8000 /* Pipe empty interrupt (0 write to pva) */ +#define APC_XINT_PEMP 0x4000 /* Play pipe empty (pva and pnva not set) */ +#define APC_XINT_PNVA 0x2000 /* Playback NVA dirty */ +#define APC_XINT_PENA 0x1000 /* play pipe empty Int enable */ +#define APC_XINT_COVF 0x800 /* Cap data dropped on floor */ +#define APC_XINT_CNVA 0x400 /* Capture NVA dirty */ +#define APC_XINT_CEMP 0x200 /* Capture pipe empty (cva and cnva not set) */ +#define APC_XINT_CENA 0x100 /* Cap. pipe empty int enable */ +#define APC_PPAUSE 0x80 /* Pause the play DMA */ +#define APC_CPAUSE 0x40 /* Pause the capture DMA */ +#define APC_CDC_RESET 0x20 /* CODEC RESET */ +#define APC_PDMA_READY 0x08 /* Play DMA Go */ +#define APC_CDMA_READY 0x04 /* Capture DMA Go */ +#define APC_CHIP_RESET 0x01 /* Reset the chip */ + +/* EBUS DMA register offsets */ + +#define EBDMA_CSR 0x00UL /* Control/Status */ +#define EBDMA_ADDR 0x04UL /* DMA Address */ +#define EBDMA_COUNT 0x08UL /* DMA Count */ + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | CS4231_XTAL2, + /* 6620 */ 0x0E | CS4231_XTAL2, + /* 8000 */ 0x00 | CS4231_XTAL1, + /* 9600 */ 0x0E | CS4231_XTAL1, + /* 11025 */ 0x02 | CS4231_XTAL2, + /* 16000 */ 0x02 | CS4231_XTAL1, + /* 18900 */ 0x04 | CS4231_XTAL2, + /* 22050 */ 0x06 | CS4231_XTAL2, + /* 27042 */ 0x04 | CS4231_XTAL1, + /* 32000 */ 0x06 | CS4231_XTAL1, + /* 33075 */ 0x0C | CS4231_XTAL2, + /* 37800 */ 0x08 | CS4231_XTAL2, + /* 44100 */ 0x0A | CS4231_XTAL2, + /* 48000 */ 0x0C | CS4231_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = 14, + .list = rates, +}; + +static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_rates); +} + +static unsigned char snd_cs4231_original_image[32] = +{ + 0x00, /* 00/00 - lic */ + 0x00, /* 01/01 - ric */ + 0x9f, /* 02/02 - la1ic */ + 0x9f, /* 03/03 - ra1ic */ + 0x9f, /* 04/04 - la2ic */ + 0x9f, /* 05/05 - ra2ic */ + 0xbf, /* 06/06 - loc */ + 0xbf, /* 07/07 - roc */ + 0x20, /* 08/08 - pdfr */ + CS4231_AUTOCALIB, /* 09/09 - ic */ + 0x00, /* 0a/10 - pc */ + 0x00, /* 0b/11 - ti */ + CS4231_MODE2, /* 0c/12 - mi */ + 0x00, /* 0d/13 - lbc */ + 0x00, /* 0e/14 - pbru */ + 0x00, /* 0f/15 - pbrl */ + 0x80, /* 10/16 - afei */ + 0x01, /* 11/17 - afeii */ + 0x9f, /* 12/18 - llic */ + 0x9f, /* 13/19 - rlic */ + 0x00, /* 14/20 - tlb */ + 0x00, /* 15/21 - thb */ + 0x00, /* 16/22 - la3mic/reserved */ + 0x00, /* 17/23 - ra3mic/reserved */ + 0x00, /* 18/24 - afs */ + 0x00, /* 19/25 - lamoc/version */ + 0x00, /* 1a/26 - mioc */ + 0x00, /* 1b/27 - ramoc/reserved */ + 0x20, /* 1c/28 - cdfr */ + 0x00, /* 1d/29 - res4 */ + 0x00, /* 1e/30 - cbru */ + 0x00, /* 1f/31 - cbrl */ +}; + +static u8 __cs4231_readb(cs4231_t *cp, unsigned long reg_addr) +{ +#ifdef EBUS_SUPPORT + if (cp->flags & CS4231_FLAG_EBUS) { + return readb(reg_addr); + } else { +#endif +#ifdef SBUS_SUPPORT + return sbus_readb(reg_addr); +#endif +#ifdef EBUS_SUPPORT + } +#endif +} + +static void __cs4231_writeb(cs4231_t *cp, u8 val, unsigned long reg_addr) +{ +#ifdef EBUS_SUPPORT + if (cp->flags & CS4231_FLAG_EBUS) { + return writeb(val, reg_addr); + } else { +#endif +#ifdef SBUS_SUPPORT + return sbus_writeb(val, reg_addr); +#endif +#ifdef EBUS_SUPPORT + } +#endif +} + +/* + * Basic I/O functions + */ + +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + int timeout; + unsigned char tmp; + + for (timeout = 250; + timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + if (chip->calibrate_mute) { + chip->image[reg] &= mask; + chip->image[reg] |= value; + } else { + __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); + mb(); + tmp = (chip->image[reg] & mask) | value; + __cs4231_writeb(chip, tmp, CS4231P(chip, REG)); + chip->image[reg] = tmp; + mb(); + } +} + +static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); + __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); + __cs4231_writeb(chip, value, CS4231P(chip, REG)); + mb(); +} + +static void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); + __cs4231_writeb(chip, value, CS4231P(chip, REG)); + chip->image[reg] = value; + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +static unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) +{ + int timeout; + unsigned char ret; + + for (timeout = 250; + timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); +#endif + __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); + mb(); + ret = __cs4231_readb(chip, CS4231P(chip, REG)); +#if 0 + printk("codec in - reg 0x%x = 0x%x\n", chip->mce_bit | reg, ret); +#endif + return ret; +} + +#ifdef CONFIG_SND_DEBUG + +void snd_cs4231_debug(cs4231_t *chip) +{ + printk("CS4231 REGS: INDEX = 0x%02x ", + __cs4231_readb(chip, CS4231P(chip, REGSEL))); + printk(" STATUS = 0x%02x\n", + __cs4231_readb(chip, CS4231P(chip, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00)); + printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10)); + printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01)); + printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11)); + printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02)); + printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12)); + printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03)); + printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13)); + printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04)); + printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14)); + printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05)); + printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15)); + printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06)); + printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16)); + printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07)); + printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); + printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08)); + printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18)); + printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09)); + printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19)); + printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a)); + printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); + printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b)); + printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); + printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c)); + printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); + printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d)); + printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); + printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e)); + printk(" 0x1e: rec upr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); + printk(" 0x0f: ply lwr count = 0x%02x ", snd_cs4231_in(chip, 0x0f)); + printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); +} + +#endif + +/* + * CS4231 detection / MCE routines + */ + +static void snd_cs4231_busy_wait(cs4231_t *chip) +{ + int timeout; + + /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ + for (timeout = 5; timeout > 0; timeout--) + __cs4231_readb(chip, CS4231P(chip, REGSEL)); + /* end of cleanup sequence */ + for (timeout = 250; + timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +} + +static void snd_cs4231_mce_up(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + + spin_lock_irqsave(&chip->lock, flags); + for (timeout = 250; timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + chip->mce_bit |= CS4231_MCE; + timeout = __cs4231_readb(chip, CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & CS4231_MCE)) + __cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void snd_cs4231_mce_down(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_busy_wait(chip); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", CS4231P(chip, REGSEL)); +#endif + chip->mce_bit &= ~CS4231_MCE; + timeout = __cs4231_readb(chip, CS4231P(chip, REGSEL)); + __cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + snd_cs4231_busy_wait(chip); + + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--) + udelay(100); + if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { + snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); + spin_unlock_irqrestore(&chip->lock, flags); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + time = HZ / 4; + while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { + spin_unlock_irqrestore(&chip->lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->lock, flags); + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + time = HZ / 10; + while (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) { + spin_unlock_irqrestore(&chip->lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (3)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->lock, flags); + } + spin_unlock_irqrestore(&chip->lock, flags); +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", __cs4231_readb(chip, CS4231P(chip, REGSEL))); +#endif +} + +#if 0 /* Unused for now... */ +static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) +{ + switch (format & 0xe0) { + case CS4231_LINEAR_16: + case CS4231_LINEAR_16_BIG: + size >>= 1; + break; + case CS4231_ADPCM_16: + return size >> 2; + } + if (format & CS4231_STEREO) + size >>= 1; + return size; +} +#endif + +#ifdef EBUS_SUPPORT +static void snd_cs4231_ebus_advance_dma(struct ebus_dma_info *p, snd_pcm_substream_t *substream, unsigned int *periods_sent) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + while (1) { + unsigned int dma_size = snd_pcm_lib_period_bytes(substream); + unsigned int offset = dma_size * (*periods_sent); + + if (dma_size >= (1 << 24)) + BUG(); + + if (ebus_dma_request(p, runtime->dma_addr + offset, dma_size)) + return; +#if 0 + printk("ebus_advance: Sent period %u (size[%x] offset[%x])\n", + (*periods_sent), dma_size, offset); +#endif + (*periods_sent) = ((*periods_sent) + 1) % runtime->periods; + } +} +#endif + +static void cs4231_dma_trigger(cs4231_t *chip, unsigned int what, int on) +{ +#ifdef EBUS_SUPPORT + if (chip->flags & CS4231_FLAG_EBUS) { + if (what & CS4231_PLAYBACK_ENABLE) { + if (on) { + ebus_dma_prepare(&chip->eb2p, 0); + ebus_dma_enable(&chip->eb2p, 1); + snd_cs4231_ebus_advance_dma(&chip->eb2p, + chip->playback_substream, + &chip->p_periods_sent); + } else { + ebus_dma_enable(&chip->eb2p, 0); + } + } + if (what & CS4231_RECORD_ENABLE) { + if (on) { + ebus_dma_prepare(&chip->eb2c, 1); + ebus_dma_enable(&chip->eb2c, 1); + snd_cs4231_ebus_advance_dma(&chip->eb2c, + chip->capture_substream, + &chip->c_periods_sent); + } else { + ebus_dma_enable(&chip->eb2c, 0); + } + } + } else { +#endif +#ifdef SBUS_SUPPORT +#endif +#ifdef EBUS_SUPPORT + } +#endif +} + +static int snd_cs4231_trigger(snd_pcm_substream_t *substream, int cmd) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + unsigned long flags; + + do { + if (s == chip->playback_substream) { + what |= CS4231_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= CS4231_RECORD_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + +#if 0 + printk("TRIGGER: what[%x] on(%d)\n", + what, (cmd == SNDRV_PCM_TRIGGER_START)); +#endif + + spin_lock_irqsave(&chip->lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + cs4231_dma_trigger(chip, what, 1); + chip->image[CS4231_IFACE_CTRL] |= what; + if (what & CS4231_PLAYBACK_ENABLE) { + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, 0xff); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, 0xff); + } + if (what & CS4231_RECORD_ENABLE) { + snd_cs4231_out(chip, CS4231_REC_LWR_CNT, 0xff); + snd_cs4231_out(chip, CS4231_REC_UPR_CNT, 0xff); + } + } else { + cs4231_dma_trigger(chip, what, 0); + chip->image[CS4231_IFACE_CTRL] &= ~what; + } + snd_cs4231_out(chip, CS4231_IFACE_CTRL, + chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->lock, flags); + break; + } + default: + result = -EINVAL; + break; + } +#if 0 + snd_cs4231_debug(chip); +#endif + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_cs4231_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + // snd_BUG(); + return freq_bits[13]; +} + +static unsigned char snd_cs4231_get_format(cs4231_t *chip, int format, int channels) +{ + unsigned char rformat; + + rformat = CS4231_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break; + case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break; + case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break; + } + if (channels > 1) + rformat |= CS4231_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + if (!mute) { + snd_cs4231_dout(chip, CS4231_LEFT_INPUT, + chip->image[CS4231_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, + chip->image[CS4231_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LOOPBACK, + chip->image[CS4231_LOOPBACK]); + } + snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, + mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, + mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, + mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, + mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, + mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, + mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, + mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, + mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_MONO_CTRL, + mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void snd_cs4231_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, + unsigned char pdfr) +{ + unsigned long flags; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + + snd_cs4231_mce_up(chip); + + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? + (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : + pdfr); + spin_unlock_irqrestore(&chip->lock, flags); + + snd_cs4231_mce_down(chip); + + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +static void snd_cs4231_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, + unsigned char cdfr) +{ + unsigned long flags; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + + snd_cs4231_mce_up(chip); + + spin_lock_irqsave(&chip->lock, flags); + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + ((chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | + (cdfr & 0x0f)); + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_down(chip); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->lock, flags); + } + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); + spin_unlock_irqrestore(&chip->lock, flags); + + snd_cs4231_mce_down(chip); + + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +/* + * Timer interface + */ + +static unsigned long snd_cs4231_timer_resolution(snd_timer_t *timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + + return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; +} + +static int snd_cs4231_timer_start(snd_timer_t *timer) +{ + unsigned long flags; + unsigned int ticks; + cs4231_t *chip = snd_timer_chip(timer); + + spin_lock_irqsave(&chip->lock, flags); + ticks = timer->sticks; + if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || + (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || + (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { + snd_cs4231_out(chip, CS4231_TIMER_HIGH, + chip->image[CS4231_TIMER_HIGH] = + (unsigned char) (ticks >> 8)); + snd_cs4231_out(chip, CS4231_TIMER_LOW, + chip->image[CS4231_TIMER_LOW] = + (unsigned char) ticks); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, + chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); + } + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int snd_cs4231_timer_stop(snd_timer_t *timer) +{ + unsigned long flags; + cs4231_t *chip = snd_timer_chip(timer); + + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, + chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static void snd_cs4231_init(cs4231_t *chip) +{ + unsigned long flags; + + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (1)\n"); +#endif + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | + CS4231_CALIB_MODE); + chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (2)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); +#endif + + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); + spin_unlock_irqrestore(&chip->lock, flags); + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (4)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (5)\n"); +#endif +} + +static int snd_cs4231_open(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if ((chip->mode & mode)) { + up(&chip->open_mutex); + return -EAGAIN; + } + if (chip->mode & CS4231_MODE_OPEN) { + chip->mode |= mode; + up(&chip->open_mutex); + return 0; + } + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); /* clear IRQ */ + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); /* clear IRQ */ + + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + spin_unlock_irqrestore(&chip->lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + return 0; +} + +static void snd_cs4231_close(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + chip->mode &= ~mode; + if (chip->mode & CS4231_MODE_OPEN) { + up(&chip->open_mutex); + return; + } + snd_cs4231_calibrate_mute(chip, 1); + + /* disable IRQ */ + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); /* clear IRQ */ + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); /* clear IRQ */ + + /* now disable record & playback */ + + if (chip->image[CS4231_IFACE_CTRL] & + (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->lock, flags); + chip->image[CS4231_IFACE_CTRL] &= + ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->lock, flags); + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->lock, flags); + } + + /* clear IRQ again */ + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); /* clear IRQ */ + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); /* clear IRQ */ + spin_unlock_irqrestore(&chip->lock, flags); + + snd_cs4231_calibrate_mute(chip, 0); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * timer open/close + */ + +static int snd_cs4231_timer_open(snd_timer_t *timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_open(chip, CS4231_MODE_TIMER); + return 0; +} + +static int snd_cs4231_timer_close(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_close(chip, CS4231_MODE_TIMER); + return 0; +} + +static struct _snd_timer_hardware snd_cs4231_timer_table = +{ + .flags = SNDRV_TIMER_HW_AUTO, + .resolution = 9945, + .ticks = 65535, + .open = snd_cs4231_timer_open, + .close = snd_cs4231_timer_close, + .c_resolution = snd_cs4231_timer_resolution, + .start = snd_cs4231_timer_start, + .stop = snd_cs4231_timer_stop, +}; + +/* + * ok.. exported functions.. + */ + +static int snd_cs4231_playback_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_pdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params))) < 0) + return err; + new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), + params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + snd_cs4231_playback_format(chip, hw_params, new_pdfr); + + return 0; +} + +static int snd_cs4231_playback_hw_free(snd_pcm_substream_t *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4231_playback_prepare(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | + CS4231_PLAYBACK_PIO); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int snd_cs4231_capture_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_cdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params))) < 0) + return err; + new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), + params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + snd_cs4231_capture_format(chip, hw_params, new_cdfr); + + return 0; +} + +static int snd_cs4231_capture_hw_free(snd_pcm_substream_t *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4231_capture_prepare(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | + CS4231_RECORD_PIO); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static void snd_cs4231_overrange(cs4231_t *chip) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&chip->lock, flags); + res = snd_cs4231_in(chip, CS4231_TEST_INIT); + spin_unlock_irqrestore(&chip->lock, flags); + + if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */ + chip->capture_substream->runtime->overrange++; +} + +static void snd_cs4231_generic_interrupt(cs4231_t *chip) +{ + unsigned long flags; + unsigned char status; + + status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); + if (!status) + return; + + if (status & CS4231_TIMER_IRQ) { + if (chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + } + if (status & CS4231_PLAYBACK_IRQ) + snd_pcm_period_elapsed(chip->playback_substream); + if (status & CS4231_RECORD_IRQ) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + + /* ACK the CS4231 interrupt. */ + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +#ifdef SBUS_SUPPORT +static void snd_cs4231_sbus_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return); + u32 csr; + + csr = sbus_readl(chip->port + APCCSR); + if (!(csr & (APC_INT_PENDING | + APC_PLAY_INT | + APC_CAPT_INT | + APC_GENL_INT | + APC_XINT_PEMP | + APC_XINT_CEMP))) + return; + + /* ACK the APC interrupt. */ + sbus_writel(csr, chip->port + APCCSR); + + snd_cs4231_generic_interrupt(chip); +} +#endif + +#ifdef EBUS_SUPPORT +static void snd_cs4231_ebus_play_callback(struct ebus_dma_info *p, int event, void *cookie) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, cookie, return); + + if (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_cs4231_ebus_advance_dma(p, chip->playback_substream, + &chip->p_periods_sent); + } +} + +static void snd_cs4231_ebus_capture_callback(struct ebus_dma_info *p, int event, void *cookie) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, cookie, return); + + if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) { + snd_pcm_period_elapsed(chip->capture_substream); + snd_cs4231_ebus_advance_dma(p, chip->capture_substream, + &chip->c_periods_sent); + } +} +#endif + +static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr, residue, period_bytes; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) + return 0; + period_bytes = snd_pcm_lib_period_bytes(substream); + ptr = period_bytes * chip->p_periods_sent; +#ifdef EBUS_SUPPORT + if (chip->flags & CS4231_FLAG_EBUS) { + residue = ebus_dma_residue(&chip->eb2p); + } else { +#endif +#ifdef SBUS_SUPPORT + residue = sbus_readl(chip->port + APCPC); +#endif +#ifdef EBUS_SUPPORT + } +#endif + ptr += (period_bytes - residue); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr, residue, period_bytes; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) + return 0; + period_bytes = snd_pcm_lib_period_bytes(substream); + ptr = period_bytes * chip->c_periods_sent; +#ifdef EBUS_SUPPORT + if (chip->flags & CS4231_FLAG_EBUS) { + residue = ebus_dma_residue(&chip->eb2c); + } else { +#endif +#ifdef SBUS_SUPPORT + residue = sbus_readl(chip->port + APCCC); +#endif +#ifdef EBUS_SUPPORT + } +#endif + ptr += (period_bytes - residue); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_cs4231_probe(cs4231_t *chip) +{ + unsigned long flags; + int i, id, vers; + unsigned char *ptr; + +#if 0 + snd_cs4231_debug(chip); +#endif + id = vers = 0; + for (i = 0; i < 50; i++) { + mb(); + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + udelay(2000); + else { + spin_lock_irqsave(&chip->lock, flags); + snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); + id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; + vers = snd_cs4231_in(chip, CS4231_VERSION); + spin_unlock_irqrestore(&chip->lock, flags); + if (id == 0x0a) + break; /* this is valid value */ + } + } + snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); + if (id != 0x0a) + return -ENODEV; /* no valid device found */ + + spin_lock_irqsave(&chip->lock, flags); + + + /* Reset DMA engine. */ +#ifdef EBUS_SUPPORT + if (chip->flags & CS4231_FLAG_EBUS) { + /* Done by ebus_dma_register */ + } else { +#endif +#ifdef SBUS_SUPPORT + sbus_writel(APC_CHIP_RESET, chip->port + APCCSR); + sbus_writel(0x00, chip->port + APCCSR); + sbus_writel(sbus_readl(chip->port + APCCSR) | APC_CDC_RESET, + chip->port + APCCSR); + + udelay(20); + + sbus_writel(sbus_readl(chip->port + APCCSR) & ~APC_CDC_RESET, + chip->port + APCCSR); + sbus_writel(sbus_readl(chip->port + APCCSR) | (APC_XINT_ENA | + APC_XINT_PENA | + APC_XINT_CENA), + chip->port + APCCSR); +#endif +#ifdef EBUS_SUPPORT + } +#endif + + __cs4231_readb(chip, CS4231P(chip, STATUS)); /* clear any pendings IRQ */ + __cs4231_writeb(chip, 0, CS4231P(chip, STATUS)); + mb(); + + spin_unlock_irqrestore(&chip->lock, flags); + + chip->image[CS4231_MISC_INFO] = CS4231_MODE2; + chip->image[CS4231_IFACE_CTRL] = + chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA; + chip->image[CS4231_ALT_FEATURE_1] = 0x80; + chip->image[CS4231_ALT_FEATURE_2] = 0x01; + if (vers & 0x20) + chip->image[CS4231_ALT_FEATURE_2] |= 0x02; + + ptr = (unsigned char *) &chip->image; + + snd_cs4231_mce_down(chip); + + spin_lock_irqsave(&chip->lock, flags); + + for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */ + snd_cs4231_out(chip, i, *ptr++); + + spin_unlock_irqrestore(&chip->lock, flags); + + snd_cs4231_mce_up(chip); + + snd_cs4231_mce_down(chip); + + mdelay(2); + + return 0; /* all things are ok.. */ +} + +static snd_pcm_hardware_t snd_cs4231_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (32*1024), + .period_bytes_min = 4096, + .period_bytes_max = (32*1024), + .periods_min = 1, + .periods_max = 1024, +}; + +static snd_pcm_hardware_t snd_cs4231_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + .rate_min = 5510, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (32*1024), + .period_bytes_min = 4096, + .period_bytes_max = (32*1024), + .periods_min = 1, + .periods_max = 1024, +}; + +static int snd_cs4231_playback_open(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_playback; + + if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) { + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->playback_substream = substream; + chip->p_periods_sent = 0; + snd_pcm_set_sync(substream); + snd_cs4231_xrate(runtime); + + return 0; +} + +static int snd_cs4231_capture_open(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_capture; + + if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) { + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->capture_substream = substream; + chip->c_periods_sent = 0; + snd_pcm_set_sync(substream); + snd_cs4231_xrate(runtime); + + return 0; +} + +static int snd_cs4231_playback_close(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_PLAY); + + return 0; +} + +static int snd_cs4231_capture_close(snd_pcm_substream_t *substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_RECORD); + + return 0; +} + +/* XXX We can do some power-management, in particular on EBUS using + * XXX the audio AUXIO register... + */ + +static snd_pcm_ops_t snd_cs4231_playback_ops = { + .open = snd_cs4231_playback_open, + .close = snd_cs4231_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs4231_playback_hw_params, + .hw_free = snd_cs4231_playback_hw_free, + .prepare = snd_cs4231_playback_prepare, + .trigger = snd_cs4231_trigger, + .pointer = snd_cs4231_playback_pointer, +}; + +static snd_pcm_ops_t snd_cs4231_capture_ops = { + .open = snd_cs4231_capture_open, + .close = snd_cs4231_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cs4231_capture_hw_params, + .hw_free = snd_cs4231_capture_hw_free, + .prepare = snd_cs4231_capture_prepare, + .trigger = snd_cs4231_trigger, + .pointer = snd_cs4231_capture_pointer, +}; + +static void snd_cs4231_pcm_free(snd_pcm_t *pcm) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_cs4231_pcm(cs4231_t *chip) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "CS4231", 0, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_cs4231_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + strcpy(pcm->name, "CS4231"); + +#ifdef EBUS_SUPPORT + if (chip->flags & CS4231_FLAG_EBUS) { + snd_pcm_lib_preallocate_pci_pages_for_all(chip->dev_u.pdev, pcm, + 64*1024, 128*1024); + } else { +#endif +#ifdef SBUS_SUPPORT + snd_pcm_lib_preallocate_sbus_pages_for_all(chip->dev_u.sdev, pcm, + 64*1024, 128*1024); +#endif +#ifdef EBUS_SUPPORT + } +#endif + + chip->pcm = pcm; + + return 0; +} + +static void snd_cs4231_timer_free(snd_timer_t *timer) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_cs4231_timer(cs4231_t *chip) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int err; + + /* Timer initialization */ + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = 0; + tid.subdevice = 0; + if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) + return err; + strcpy(timer->name, "CS4231"); + timer->private_data = chip; + timer->private_free = snd_cs4231_timer_free; + timer->hw = snd_cs4231_timer_table; + chip->timer = timer; + + return 0; +} + +/* + * MIXER part + */ + +static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[4] = { + "Line", "CD", "Mic", "Mix" + }; + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + + snd_assert(chip->card != NULL, return -EINVAL); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_cs4231_get_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = + (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = + (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int snd_cs4231_put_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + + spin_lock_irqsave(&chip->lock, flags); + + left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; + right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; + change = left != chip->image[CS4231_LEFT_INPUT] || + right != chip->image[CS4231_RIGHT_INPUT]; + snd_cs4231_out(chip, CS4231_LEFT_INPUT, left); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right); + + spin_unlock_irqrestore(&chip->lock, flags); + + return change; +} + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = (mask == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + + return 0; +} + +int snd_cs4231_get_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + + spin_unlock_irqrestore(&chip->lock, flags); + + if (invert) + ucontrol->value.integer.value[0] = + (mask - ucontrol->value.integer.value[0]); + + return 0; +} + +int snd_cs4231_put_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + + spin_lock_irqsave(&chip->lock, flags); + + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_cs4231_out(chip, reg, val); + + spin_unlock_irqrestore(&chip->lock, flags); + + return change; +} + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + + return 0; +} + +int snd_cs4231_get_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->lock, flags); + + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + + spin_unlock_irqrestore(&chip->lock, flags); + + if (invert) { + ucontrol->value.integer.value[0] = + (mask - ucontrol->value.integer.value[0]); + ucontrol->value.integer.value[1] = + (mask - ucontrol->value.integer.value[1]); + } + + return 0; +} + +int snd_cs4231_put_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + + spin_lock_irqsave(&chip->lock, flags); + + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4231_out(chip, right_reg, val2); + + spin_unlock_irqrestore(&chip->lock, flags); + + return change; +} + +#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t)) + +#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4231_info_single, \ + .get = snd_cs4231_get_single, .put = snd_cs4231_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_cs4231_info_double, \ + .get = snd_cs4231_get_double, .put = snd_cs4231_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static snd_kcontrol_new_t snd_cs4231_controls[] = { +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1), +CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_cs4231_info_mux, + .get = snd_cs4231_get_mux, + .put = snd_cs4231_put_mux, +}, +CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1), +/* SPARC specific uses of XCTL{0,1} general purpose outputs. */ +CS4231_SINGLE("Line Out Switch", 0, CS4231_PIN_CTRL, 6, 1, 1), +CS4231_SINGLE("Headphone Out Switch", 0, CS4231_PIN_CTRL, 7, 1, 1) +}; + +int snd_cs4231_mixer(cs4231_t *chip) +{ + snd_card_t *card; + int err, idx; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < CS4231_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, + snd_ctl_new1(&snd_cs4231_controls[idx], + chip))) < 0) + return err; + } + return 0; +} + +static int dev; + +static int cs4231_attach_begin(snd_card_t **rcard) +{ + snd_card_t *card; + + *rcard = NULL; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "CS4231"); + strcpy(card->shortname, "Sun CS4231"); + + *rcard = card; + return 0; +} + +static int cs4231_attach_finish(snd_card_t *card, cs4231_t *chip) +{ + int err; + + if ((err = snd_cs4231_pcm(chip)) < 0) + goto out_err; + + if ((err = snd_cs4231_mixer(chip)) < 0) + goto out_err; + + if ((err = snd_cs4231_timer(chip)) < 0) + goto out_err; + + if ((err = snd_card_register(card)) < 0) + goto out_err; + + chip->next = cs4231_list; + cs4231_list = chip; + + dev++; + return 0; + +out_err: + snd_card_free(card); + return err; +} + +#ifdef SBUS_SUPPORT +static int snd_cs4231_sbus_free(cs4231_t *chip) +{ + if (chip->irq[0]) + free_irq(chip->irq[0], chip); + + if (chip->port) + sbus_iounmap(chip->port, chip->regs_size); + + if (chip->timer) + snd_device_free(chip->card, chip->timer); + + snd_magic_kfree(chip); + + return 0; +} + +static int snd_cs4231_sbus_dev_free(snd_device_t *device) +{ + cs4231_t *cp = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + + return snd_cs4231_sbus_free(cp); +} + +static snd_device_ops_t snd_cs4231_sbus_dev_ops = { + .dev_free = snd_cs4231_sbus_dev_free, +}; + +static int __init snd_cs4231_sbus_create(snd_card_t *card, + struct sbus_dev *sdev, + int dev, + cs4231_t **rchip) +{ + cs4231_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spin_lock_init(&chip->lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->dev_u.sdev = sdev; + chip->regs_size = sdev->reg_addrs[0].reg_size; + memcpy(&chip->image, &snd_cs4231_original_image, + sizeof(snd_cs4231_original_image)); + + chip->port = sbus_ioremap(&sdev->resource[0], 0, + chip->regs_size, "cs4231"); + if (!chip->port) { + snd_printk("cs4231-%d: Unable to map chip registers.\n", dev); + return -EIO; + } + + if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt, + SA_SHIRQ, "cs4231", chip)) { + snd_cs4231_sbus_free(chip); + snd_printk("cs4231-%d: Unable to grab SBUS IRQ %s\n", + dev, + __irq_itoa(sdev->irqs[0])); + return -EBUSY; + } + chip->irq[0] = sdev->irqs[0]; + + if (snd_cs4231_probe(chip) < 0) { + snd_cs4231_sbus_free(chip); + return -ENODEV; + } + snd_cs4231_init(chip); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &snd_cs4231_sbus_dev_ops)) < 0) { + snd_cs4231_sbus_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int cs4231_sbus_attach(struct sbus_dev *sdev) +{ + struct resource *rp = &sdev->resource[0]; + cs4231_t *cp; + snd_card_t *card; + int err; + + err = cs4231_attach_begin(&card); + if (err) + return err; + + sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s", + card->shortname, + rp->flags & 0xffL, + rp->start, + __irq_itoa(sdev->irqs[0])); + + if ((err = snd_cs4231_sbus_create(card, sdev, dev, &cp)) < 0) { + snd_card_free(card); + return err; + } + + return cs4231_attach_finish(card, cp); +} +#endif + +#ifdef EBUS_SUPPORT +static int snd_cs4231_ebus_free(cs4231_t *chip) +{ + if (chip->eb2c.regs) { + ebus_dma_unregister(&chip->eb2c); + iounmap(chip->eb2c.regs); + } + if (chip->eb2p.regs) { + ebus_dma_unregister(&chip->eb2p); + iounmap(chip->eb2p.regs); + } + + if (chip->port) + iounmap(chip->port); + if (chip->timer) + snd_device_free(chip->card, chip->timer); + + snd_magic_kfree(chip); + + return 0; +} + +static int snd_cs4231_ebus_dev_free(snd_device_t *device) +{ + cs4231_t *cp = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + + return snd_cs4231_ebus_free(cp); +} + +static snd_device_ops_t snd_cs4231_ebus_dev_ops = { + .dev_free = snd_cs4231_ebus_dev_free, +}; + +static int __init snd_cs4231_ebus_create(snd_card_t *card, + struct linux_ebus_device *edev, + int dev, + cs4231_t **rchip) +{ + cs4231_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spin_lock_init(&chip->lock); + spin_lock_init(&chip->eb2c.lock); + spin_lock_init(&chip->eb2p.lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + chip->flags |= CS4231_FLAG_EBUS; + chip->card = card; + chip->dev_u.pdev = edev->bus->self; + memcpy(&chip->image, &snd_cs4231_original_image, + sizeof(snd_cs4231_original_image)); + strcpy(chip->eb2c.name, "cs4231(capture)"); + chip->eb2c.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; + chip->eb2c.callback = snd_cs4231_ebus_capture_callback; + chip->eb2c.client_cookie = chip; + chip->eb2c.irq = edev->irqs[0]; + strcpy(chip->eb2p.name, "cs4231(play)"); + chip->eb2p.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; + chip->eb2p.callback = snd_cs4231_ebus_play_callback; + chip->eb2p.client_cookie = chip; + chip->eb2p.irq = edev->irqs[1]; + + chip->port = (unsigned long) ioremap(edev->resource[0].start, 0x10); + chip->eb2p.regs = (unsigned long) ioremap(edev->resource[1].start, 0x10); + chip->eb2c.regs = (unsigned long) ioremap(edev->resource[2].start, 0x10); + if (!chip->port || !chip->eb2p.regs || !chip->eb2c.regs) { + snd_cs4231_ebus_free(chip); + snd_printk("cs4231-%d: Unable to map chip registers.\n", dev); + return -EIO; + } + + if (ebus_dma_register(&chip->eb2c)) { + snd_cs4231_ebus_free(chip); + snd_printk("cs4231-%d: Unable to register EBUS capture DMA\n", dev); + return -EBUSY; + } + if (ebus_dma_irq_enable(&chip->eb2c, 1)) { + snd_cs4231_ebus_free(chip); + snd_printk("cs4231-%d: Unable to enable EBUS capture IRQ\n", dev); + return -EBUSY; + } + + if (ebus_dma_register(&chip->eb2p)) { + snd_cs4231_ebus_free(chip); + snd_printk("cs4231-%d: Unable to register EBUS play DMA\n", dev); + return -EBUSY; + } + if (ebus_dma_irq_enable(&chip->eb2p, 1)) { + snd_cs4231_ebus_free(chip); + snd_printk("cs4231-%d: Unable to enable EBUS play IRQ\n", dev); + return -EBUSY; + } + + if (snd_cs4231_probe(chip) < 0) { + snd_cs4231_ebus_free(chip); + return -ENODEV; + } + snd_cs4231_init(chip); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &snd_cs4231_ebus_dev_ops)) < 0) { + snd_cs4231_ebus_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int cs4231_ebus_attach(struct linux_ebus_device *edev) +{ + snd_card_t *card; + cs4231_t *chip; + int err; + + err = cs4231_attach_begin(&card); + if (err) + return err; + + sprintf(card->longname, "%s at 0x%lx, irq %s", + card->shortname, + edev->resource[0].start, + __irq_itoa(edev->irqs[0])); + + if ((err = snd_cs4231_ebus_create(card, edev, dev, &chip)) < 0) { + snd_card_free(card); + return err; + } + + return cs4231_attach_finish(card, chip); +} +#endif + +static int __init cs4231_init(void) +{ +#ifdef SBUS_SUPPORT + struct sbus_bus *sbus; + struct sbus_dev *sdev; +#endif +#ifdef EBUS_SUPPORT + struct linux_ebus *ebus; + struct linux_ebus_device *edev; +#endif + int found; + + found = 0; + +#ifdef SBUS_SUPPORT + for_all_sbusdev(sdev, sbus) { + if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { + if (cs4231_sbus_attach(sdev) == 0) + found++; + } + } +#endif +#ifdef EBUS_SUPPORT + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + int match = 0; + + if (!strcmp(edev->prom_name, "SUNW,CS4231")) { + match = 1; + } else if (!strcmp(edev->prom_name, "audio")) { + char compat[16]; + + prom_getstring(edev->prom_node, "compatible", + compat, sizeof(compat)); + compat[15] = '\0'; + if (!strcmp(compat, "SUNW,CS4231")) + match = 1; + } + + if (match && + cs4231_ebus_attach(edev) == 0) + found++; + } + } +#endif + + + return (found > 0) ? 0 : -EIO; +} + +static void __exit cs4231_exit(void) +{ + cs4231_t *p = cs4231_list; + + while (p != NULL) { + cs4231_t *next = p->next; + + snd_card_free(p->card); + + p = next; + } + + cs4231_list = NULL; +} + +module_init(cs4231_init); +module_exit(cs4231_exit); + +#ifndef MODULE + +/* format is: snd-sun-cs4231=index,id,enable */ + +static int __init alsa_card_sun_cs4231_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&index[nr_dev]) == 2 && + get_option(&str,&id[nr_dev]) == 2 && + get_id(&str,&enable[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sun-cs4231=", alsa_card_sun_cs4231_setup); + +#endif /* ifndef MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/synth/Makefile linux/sound/synth/Makefile --- linux-2.4.21-rc1.orig/sound/synth/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := synth.o + +subdir-y := emux +subdir-m := $(subdir-y) + +list-multi := snd-util-mem.o + +export-objs := util_mem.o + +snd-util-mem-objs := util_mem.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o +obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o +endif + +include $(TOPDIR)/Rules.make + +snd-util-mem.o: $(snd-util-mem-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-util-mem-objs) diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/Makefile linux/sound/synth/emux/Makefile --- linux-2.4.21-rc1.orig/sound/synth/emux/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _emux.o + +list-multi := snd-emux-synth.o + +export-objs := emux.o + +snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ + emux_effect.o emux_proc.o soundfont.o +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) + snd-emux-synth-objs += emux_oss.o +endif + +# Toplevel Module Dependency +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-emux-synth.o + obj-$(CONFIG_SND_EMU10K1) += snd-emux-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-emux-synth.o: $(snd-emux-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emux-synth-objs) diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux.c linux/sound/synth/emux/emux.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux.c 2002-11-23 03:41:54.000000000 -0700 @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Routines for control of EMU WaveTable chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "emux_voice.h" + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip"); +MODULE_LICENSE("GPL"); + +/* + * create a new hardware dependent device for Emu8000/Emu10k1 + */ +int snd_emux_new(snd_emux_t **remu) +{ + snd_emux_t *emu; + + *remu = NULL; + emu = snd_magic_kcalloc(snd_emux_t, 0, GFP_KERNEL); + if (emu == NULL) + return -ENOMEM; + + spin_lock_init(&emu->voice_lock); + init_MUTEX(&emu->register_mutex); + + emu->client = -1; +#ifdef CONFIG_SND_SEQUENCER_OSS + emu->oss_synth = NULL; +#endif + emu->max_voices = 0; + emu->use_time = 0; + + init_timer(&emu->tlist); + emu->tlist.function = snd_emux_timer_callback; + emu->tlist.data = (unsigned long)emu; + emu->timer_active = 0; + + *remu = emu; + return 0; +} + + +/* + */ +int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name) +{ + snd_sf_callback_t sf_cb; + + snd_assert(emu->hw != NULL, return -EINVAL); + snd_assert(emu->max_voices > 0, return -EINVAL); + snd_assert(card != NULL, return -EINVAL); + snd_assert(name != NULL, return -EINVAL); + + emu->card = card; + emu->name = snd_kmalloc_strdup(name, GFP_KERNEL); + emu->voices = snd_kcalloc(sizeof(snd_emux_voice_t) * emu->max_voices, GFP_KERNEL); + if (emu->voices == NULL) + return -ENOMEM; + + /* create soundfont list */ + memset(&sf_cb, 0, sizeof(sf_cb)); + sf_cb.private_data = emu; + sf_cb.sample_new = (snd_sf_sample_new_t)emu->ops.sample_new; + sf_cb.sample_free = (snd_sf_sample_free_t)emu->ops.sample_free; + sf_cb.sample_reset = (snd_sf_sample_reset_t)emu->ops.sample_reset; + emu->sflist = snd_sf_new(&sf_cb, emu->memhdr); + if (emu->sflist == NULL) + return -ENOMEM; + + snd_emux_init_voices(emu); + + snd_emux_init_seq(emu, card, index); +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_emux_init_seq_oss(emu); +#endif + snd_emux_init_virmidi(emu, card); + +#ifdef CONFIG_PROC_FS + snd_emux_proc_init(emu, card, index); +#endif + return 0; +} + + +/* + */ +int snd_emux_free(snd_emux_t *emu) +{ + unsigned long flags; + + if (! emu) + return -EINVAL; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->timer_active) + del_timer(&emu->tlist); + spin_unlock_irqrestore(&emu->voice_lock, flags); + +#ifdef CONFIG_PROC_FS + snd_emux_proc_free(emu); +#endif + snd_emux_delete_virmidi(emu); +#ifdef CONFIG_SND_SEQUENCER_OSS + snd_emux_detach_seq_oss(emu); +#endif + snd_emux_detach_seq(emu); + + if (emu->sflist) + snd_sf_free(emu->sflist); + + if (emu->voices) + kfree(emu->voices); + + if (emu->name) + kfree(emu->name); + + snd_magic_kfree(emu); + return 0; +} + + +EXPORT_SYMBOL(snd_emux_new); +EXPORT_SYMBOL(snd_emux_register); +EXPORT_SYMBOL(snd_emux_free); + +EXPORT_SYMBOL(snd_emux_terminate_all); +EXPORT_SYMBOL(snd_emux_lock_voice); +EXPORT_SYMBOL(snd_emux_unlock_voice); + +/* soundfont.c */ +EXPORT_SYMBOL(snd_sf_linear_to_log); + + +/* + * INIT part + */ + +static int __init alsa_emux_init(void) +{ + return 0; +} + +static void __exit alsa_emux_exit(void) +{ +} + +module_init(alsa_emux_init) +module_exit(alsa_emux_exit) diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_effect.c linux/sound/synth/emux/emux_effect.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_effect.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_effect.c 2002-08-12 02:43:49.000000000 -0600 @@ -0,0 +1,308 @@ +/* + * Midi synth routines for the Emu8k/Emu10k1 + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * Contains code based on awe_wave.c by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "emux_voice.h" +#include + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +/* + * effects table + */ + +#define xoffsetof(type,tag) ((long)(&((type)NULL)->tag) - (long)(NULL)) + +#define parm_offset(tag) xoffsetof(soundfont_voice_parm_t*, tag) + +#define PARM_IS_BYTE (1 << 0) +#define PARM_IS_WORD (1 << 1) +#define PARM_IS_ALIGNED (3 << 2) +#define PARM_IS_ALIGN_HI (1 << 2) +#define PARM_IS_ALIGN_LO (2 << 2) +#define PARM_IS_SIGNED (1 << 4) + +#define PARM_WORD (PARM_IS_WORD) +#define PARM_BYTE_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO) +#define PARM_BYTE_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI) +#define PARM_BYTE (PARM_IS_BYTE) +#define PARM_SIGN_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED) +#define PARM_SIGN_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED) + +static struct emux_parm_defs { + int type; /* byte or word */ + int low, high; /* value range */ + long offset; /* offset in parameter record (-1 = not written) */ + int update; /* flgas for real-time update */ +} parm_defs[EMUX_NUM_EFFECTS] = { + {PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0}, /* env1 delay */ + {PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0}, /* env1 attack */ + {PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0}, /* env1 hold */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0}, /* env1 decay */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0}, /* env1 release */ + {PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0}, /* env1 sustain */ + {PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0}, /* env1 pitch */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0}, /* env1 fc */ + + {PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0}, /* env2 delay */ + {PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0}, /* env2 attack */ + {PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0}, /* env2 hold */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0}, /* env2 decay */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0}, /* env2 release */ + {PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0}, /* lfo1 delay */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 freq */ + {PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 vol */ + {PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 pitch */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 cutoff */ + + {PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0}, /* lfo2 delay */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 freq */ + {PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 pitch */ + + {PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, parm_offset(chorus), 0}, /* chorus */ + {PARM_BYTE, 0, 0xff, parm_offset(reverb), 0}, /* reverb */ + {PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME}, /* cutoff */ + {PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q}, /* resonance */ + + {PARM_WORD, 0, 0xffff, -1, 0}, /* sample start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* loop start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* loop end */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME}, /* initial attenuation */ +}; + +/* set byte effect value */ +static void +effect_set_byte(unsigned char *valp, snd_midi_channel_t *chan, int type) +{ + short effect; + snd_emux_effect_table_t *fx = chan->private; + + effect = fx->val[type]; + if (fx->flag[type] == EMUX_FX_FLAG_ADD) { + if (parm_defs[type].type & PARM_IS_SIGNED) + effect += *(char*)valp; + else + effect += *valp; + } + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + *valp = (unsigned char)effect; +} + +/* set word effect value */ +static void +effect_set_word(unsigned short *valp, snd_midi_channel_t *chan, int type) +{ + int effect; + snd_emux_effect_table_t *fx = chan->private; + + effect = *(unsigned short*)&fx->val[type]; + if (fx->flag[type] == EMUX_FX_FLAG_ADD) + effect += *valp; + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + *valp = (unsigned short)effect; +} + +/* address offset */ +static int +effect_get_offset(snd_midi_channel_t *chan, int lo, int hi, int mode) +{ + int addr = 0; + snd_emux_effect_table_t *fx = chan->private; + + if (fx->flag[hi]) + addr = (short)fx->val[hi]; + addr = addr << 15; + if (fx->flag[lo]) + addr += (short)fx->val[lo]; + if (!(mode & SNDRV_SFNT_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + +#ifdef CONFIG_SND_SEQUENCER_OSS +/* change effects - for OSS sequencer compatibility */ +void +snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val) +{ + int mode; + + if (type & 0x40) + mode = EMUX_FX_FLAG_OFF; + else if (type & 0x80) + mode = EMUX_FX_FLAG_ADD; + else + mode = EMUX_FX_FLAG_SET; + type &= 0x3f; + + snd_emux_send_effect(port, chan, type, val, mode); +} +#endif + +/* Modify the effect value. + * if update is necessary, call emu8000_control + */ +void +snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode) +{ + int i; + int offset; + unsigned char *srcp, *origp; + snd_emux_t *emu; + snd_emux_effect_table_t *fx; + unsigned long flags; + + emu = port->emu; + fx = chan->private; + if (emu == NULL || fx == NULL) + return; + if (type < 0 || type >= EMUX_NUM_EFFECTS) + return; + + fx->val[type] = val; + fx->flag[type] = mode; + + /* do we need to modify the register in realtime ? */ + if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0) + return; + +#ifdef SNDRV_LITTLE_ENDIAN + if (parm_defs[type].type & PARM_IS_ALIGN_HI) + offset++; +#else + if (parm_defs[type].type & PARM_IS_ALIGN_LO) + offset++; +#endif + /* modify the register values */ + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + snd_emux_voice_t *vp = &emu->voices[i]; + if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan) + continue; + srcp = (unsigned char*)&vp->reg.parm + offset; + origp = (unsigned char*)&vp->zone->v.parm + offset; + if (parm_defs[i].type & PARM_IS_BYTE) { + *srcp = *origp; + effect_set_byte(srcp, chan, type); + } else { + *(unsigned short*)srcp = *(unsigned short*)origp; + effect_set_word((unsigned short*)srcp, chan, type); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + /* activate them */ + snd_emux_update_channel(port, chan, parm_defs[type].update); +} + + +/* copy wavetable registers to voice table */ +void +snd_emux_setup_effect(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + snd_emux_effect_table_t *fx; + unsigned char *srcp; + int i; + + if (! (fx = chan->private)) + return; + + /* modify the register values via effect table */ + for (i = 0; i < EMUX_FX_END; i++) { + int offset; + if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0) + continue; +#ifdef SNDRV_LITTLE_ENDIAN + if (parm_defs[i].type & PARM_IS_ALIGN_HI) + offset++; +#else + if (parm_defs[i].type & PARM_IS_ALIGN_LO) + offset++; +#endif + srcp = (unsigned char*)&vp->reg.parm + offset; + if (parm_defs[i].type & PARM_IS_BYTE) + effect_set_byte(srcp, chan, i); + else + effect_set_word((unsigned short*)srcp, chan, i); + } + + /* correct sample and loop points */ + vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START, + EMUX_FX_COARSE_SAMPLE_START, + vp->reg.sample_mode); + + vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START, + EMUX_FX_COARSE_LOOP_START, + vp->reg.sample_mode); + + vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END, + EMUX_FX_COARSE_LOOP_END, + vp->reg.sample_mode); +} + +/* + * effect table + */ +void +snd_emux_create_effect(snd_emux_port_t *p) +{ + int i; + p->effect = snd_kcalloc(sizeof(snd_emux_effect_table_t) * p->chset.max_channels, GFP_KERNEL); + if (p->effect) { + for (i = 0; i < p->chset.max_channels; i++) + p->chset.channels[i].private = p->effect + i; + } else { + for (i = 0; i < p->chset.max_channels; i++) + p->chset.channels[i].private = NULL; + } +} + +void +snd_emux_delete_effect(snd_emux_port_t *p) +{ + if (p->effect) { + kfree(p->effect); + p->effect = NULL; + } +} + +void +snd_emux_clear_effect(snd_emux_port_t *p) +{ + if (p->effect) { + memset(p->effect, 0, sizeof(snd_emux_effect_table_t) * p->chset.max_channels); + } +} + +#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_nrpn.c linux/sound/synth/emux/emux_nrpn.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_nrpn.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_nrpn.c 2002-08-12 02:43:49.000000000 -0600 @@ -0,0 +1,400 @@ +/* + * NRPN / SYSEX callbacks for Emu8k/Emu10k1 + * + * Copyright (c) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "emux_voice.h" +#include + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + +/* + * conversion from NRPN/control parameters to Emu8000 raw parameters + */ + +/* NRPN / CC -> Emu8000 parameter converter */ +typedef struct { + int control; + int effect; + int (*convert)(int val); +} nrpn_conv_table; + +/* effect sensitivity */ + +#define FX_CUTOFF 0 +#define FX_RESONANCE 1 +#define FX_ATTACK 2 +#define FX_RELEASE 3 +#define FX_VIBRATE 4 +#define FX_VIBDEPTH 5 +#define FX_VIBDELAY 6 +#define FX_NUMS 7 + +/* + * convert NRPN/control values + */ + +static int send_converted_effect(nrpn_conv_table *table, int num_tables, + snd_emux_port_t *port, snd_midi_channel_t *chan, + int type, int val, int mode) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + snd_emux_send_effect(port, chan, table[i].effect, + cval, mode); + return 1; + } + } + return 0; +} + +#define DEF_FX_CUTOFF 170 +#define DEF_FX_RESONANCE 6 +#define DEF_FX_ATTACK 50 +#define DEF_FX_RELEASE 50 +#define DEF_FX_VIBRATE 30 +#define DEF_FX_VIBDEPTH 4 +#define DEF_FX_VIBDELAY 1500 + +/* effect sensitivities for GS NRPN: + * adjusted for chaos 8MB soundfonts + */ +static int gs_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + +/* effect sensitivies for XG controls: + * adjusted for chaos 8MB soundfonts + */ +static int xg_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + + +/* + * AWE32 NRPN effects + */ + +static int fx_delay(int val); +static int fx_attack(int val); +static int fx_hold(int val); +static int fx_decay(int val); +static int fx_the_value(int val); +static int fx_twice_value(int val); +static int fx_conv_pitch(int val); +static int fx_conv_Q(int val); + +/* function for each NRPN */ /* [range] units */ +#define fx_env1_delay fx_delay /* [0,5900] 4msec */ +#define fx_env1_attack fx_attack /* [0,5940] 1msec */ +#define fx_env1_hold fx_hold /* [0,8191] 1msec */ +#define fx_env1_decay fx_decay /* [0,5940] 4msec */ +#define fx_env1_release fx_decay /* [0,5940] 4msec */ +#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ +#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ + +#define fx_env2_delay fx_delay /* [0,5900] 4msec */ +#define fx_env2_attack fx_attack /* [0,5940] 1msec */ +#define fx_env2_hold fx_hold /* [0,8191] 1msec */ +#define fx_env2_decay fx_decay /* [0,5940] 4msec */ +#define fx_env2_release fx_decay /* [0,5940] 4msec */ +#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ + +#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ +#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ + +#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ + +#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ +#define fx_chorus fx_the_value /* [0,255] -- */ +#define fx_reverb fx_the_value /* [0,255] -- */ +#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ +#define fx_filterQ fx_conv_Q /* [0,127] -- */ + +static int fx_delay(int val) +{ + return (unsigned short)snd_sf_calc_parm_delay(val); +} + +static int fx_attack(int val) +{ + return (unsigned short)snd_sf_calc_parm_attack(val); +} + +static int fx_hold(int val) +{ + return (unsigned short)snd_sf_calc_parm_hold(val); +} + +static int fx_decay(int val) +{ + return (unsigned short)snd_sf_calc_parm_decay(val); +} + +static int fx_the_value(int val) +{ + return (unsigned short)(val & 0xff); +} + +static int fx_twice_value(int val) +{ + return (unsigned short)((val * 2) & 0xff); +} + +static int fx_conv_pitch(int val) +{ + return (short)(val * 4096 / 1200); +} + +static int fx_conv_Q(int val) +{ + return (unsigned short)((val / 8) & 0xff); +} + + +static nrpn_conv_table awe_effects[] = +{ + { 0, EMUX_FX_LFO1_DELAY, fx_lfo1_delay}, + { 1, EMUX_FX_LFO1_FREQ, fx_lfo1_freq}, + { 2, EMUX_FX_LFO2_DELAY, fx_lfo2_delay}, + { 3, EMUX_FX_LFO2_FREQ, fx_lfo2_freq}, + + { 4, EMUX_FX_ENV1_DELAY, fx_env1_delay}, + { 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack}, + { 6, EMUX_FX_ENV1_HOLD, fx_env1_hold}, + { 7, EMUX_FX_ENV1_DECAY, fx_env1_decay}, + { 8, EMUX_FX_ENV1_SUSTAIN, fx_env1_sustain}, + { 9, EMUX_FX_ENV1_RELEASE, fx_env1_release}, + + {10, EMUX_FX_ENV2_DELAY, fx_env2_delay}, + {11, EMUX_FX_ENV2_ATTACK, fx_env2_attack}, + {12, EMUX_FX_ENV2_HOLD, fx_env2_hold}, + {13, EMUX_FX_ENV2_DECAY, fx_env2_decay}, + {14, EMUX_FX_ENV2_SUSTAIN, fx_env2_sustain}, + {15, EMUX_FX_ENV2_RELEASE, fx_env2_release}, + + {16, EMUX_FX_INIT_PITCH, fx_init_pitch}, + {17, EMUX_FX_LFO1_PITCH, fx_lfo1_pitch}, + {18, EMUX_FX_LFO2_PITCH, fx_lfo2_pitch}, + {19, EMUX_FX_ENV1_PITCH, fx_env1_pitch}, + {20, EMUX_FX_LFO1_VOLUME, fx_lfo1_volume}, + {21, EMUX_FX_CUTOFF, fx_cutoff}, + {22, EMUX_FX_FILTERQ, fx_filterQ}, + {23, EMUX_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, + {24, EMUX_FX_ENV1_CUTOFF, fx_env1_cutoff}, + {25, EMUX_FX_CHORUS, fx_chorus}, + {26, EMUX_FX_REVERB, fx_reverb}, +}; + +static int num_awe_effects = NELEM(awe_effects); + + +/* + * GS(SC88) NRPN effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static int gs_cutoff(int val) +{ + return (val - 64) * gs_sense[FX_CUTOFF] / 50; +} + +/* resonance: 0 to 15(max) */ +static int gs_filterQ(int val) +{ + return (val - 64) * gs_sense[FX_RESONANCE] / 50; +} + +/* attack: */ +static int gs_attack(int val) +{ + return -(val - 64) * gs_sense[FX_ATTACK] / 50; +} + +/* decay: */ +static int gs_decay(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* release: */ +static int gs_release(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* vibrato freq: 0.042Hz step, max=255 */ +static int gs_vib_rate(int val) +{ + return (val - 64) * gs_sense[FX_VIBRATE] / 50; +} + +/* vibrato depth: max=127, 1 octave */ +static int gs_vib_depth(int val) +{ + return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; +} + +/* vibrato delay: -0.725msec step */ +static int gs_vib_delay(int val) +{ + return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; +} + +static nrpn_conv_table gs_effects[] = +{ + {32, EMUX_FX_CUTOFF, gs_cutoff}, + {33, EMUX_FX_FILTERQ, gs_filterQ}, + {99, EMUX_FX_ENV2_ATTACK, gs_attack}, + {100, EMUX_FX_ENV2_DECAY, gs_decay}, + {102, EMUX_FX_ENV2_RELEASE, gs_release}, + {8, EMUX_FX_LFO1_FREQ, gs_vib_rate}, + {9, EMUX_FX_LFO1_VOLUME, gs_vib_depth}, + {10, EMUX_FX_LFO1_DELAY, gs_vib_delay}, +}; + +static int num_gs_effects = NELEM(gs_effects); + + +/* + * NRPN events + */ +void +snd_emux_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL, return); + snd_assert(chan != NULL, return); + + if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 && + chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) { + int val; + /* Win/DOS AWE32 specific NRPNs */ + /* both MSB/LSB necessary */ + val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | + chan->control[MIDI_CTL_LSB_DATA_ENTRY]; + val -= 8192; + send_converted_effect + (awe_effects, num_awe_effects, + port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], + val, EMUX_FX_FLAG_SET); + return; + } + + if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS && + chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) { + int val; + /* GS specific NRPNs */ + /* only MSB is valid */ + val = chan->control[MIDI_CTL_MSB_DATA_ENTRY]; + send_converted_effect + (gs_effects, num_gs_effects, + port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], + val, EMUX_FX_FLAG_ADD); + return; + } +} + + +/* + * XG control effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static int xg_cutoff(int val) +{ + return (val - 64) * xg_sense[FX_CUTOFF] / 64; +} + +/* resonance: 0(open) to 15(most nasal) */ +static int xg_filterQ(int val) +{ + return (val - 64) * xg_sense[FX_RESONANCE] / 64; +} + +/* attack: */ +static int xg_attack(int val) +{ + return -(val - 64) * xg_sense[FX_ATTACK] / 64; +} + +/* release: */ +static int xg_release(int val) +{ + return -(val - 64) * xg_sense[FX_RELEASE] / 64; +} + +static nrpn_conv_table xg_effects[] = +{ + {71, EMUX_FX_CUTOFF, xg_cutoff}, + {74, EMUX_FX_FILTERQ, xg_filterQ}, + {72, EMUX_FX_ENV2_RELEASE, xg_release}, + {73, EMUX_FX_ENV2_ATTACK, xg_attack}, +}; + +static int num_xg_effects = NELEM(xg_effects); + +int +snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param) +{ + return send_converted_effect(xg_effects, num_xg_effects, + port, chan, param, + chan->control[param], + EMUX_FX_FLAG_ADD); +} + +/* + * receive sysex + */ +void +snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset) +{ + snd_emux_port_t *port; + snd_emux_t *emu; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL, return); + snd_assert(chset != NULL, return); + emu = port->emu; + + switch (parsed) { + case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME: + snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); + break; + default: + if (emu->ops.sysex) + emu->ops.sysex(emu, buf, len, parsed, chset); + break; + } +} + diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_oss.c linux/sound/synth/emux/emux_oss.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_oss.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_oss.c 2003-01-31 08:21:26.000000000 -0700 @@ -0,0 +1,495 @@ +/* + * Interface for OSS sequencer emulation + * + * Copyright (C) 1999 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Changes + * 19990227 Steve Ratcliffe Made separate file and merged in latest + * midi emulation. + */ + +#include + +#ifdef CONFIG_SND_SEQUENCER_OSS + +#include +#include +#include "emux_voice.h" +#include + +static int snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); +static int snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); +static int snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count); +static int snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +static void reset_port_mode(snd_emux_port_t *port, int midi_mode); +static void emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); +static void gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); +static void fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop); + +/* operators */ +static snd_seq_oss_callback_t oss_callback = { + .owner = THIS_MODULE, + .open = snd_emux_open_seq_oss, + .close = snd_emux_close_seq_oss, + .ioctl = snd_emux_ioctl_seq_oss, + .load_patch = snd_emux_load_patch_seq_oss, + .reset = snd_emux_reset_seq_oss, +}; + + +/* + * register OSS synth + */ + +void +snd_emux_init_seq_oss(snd_emux_t *emu) +{ + snd_seq_oss_reg_t *arg; + snd_seq_device_t *dev; + + if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS, + sizeof(snd_seq_oss_reg_t), &dev) < 0) + return; + + emu->oss_synth = dev; + strcpy(dev->name, emu->name); + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + arg->type = SYNTH_TYPE_SAMPLE; + arg->subtype = SAMPLE_TYPE_AWE32; + arg->nvoices = emu->max_voices; + arg->oper = oss_callback; + arg->private_data = emu; + + /* register to OSS synth table */ + snd_device_register(emu->card, dev); +} + + +/* + * unregister + */ +void +snd_emux_detach_seq_oss(snd_emux_t *emu) +{ + if (emu->oss_synth) { + snd_device_free(emu->card, emu->oss_synth); + emu->oss_synth = NULL; + } +} + + +/* use port number as a unique soundfont client number */ +#define SF_CLIENT_NO(p) ((p) + 0x1000) + +/* + * open port for OSS sequencer + */ +static int +snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + snd_seq_port_callback_t callback; + char tmpname[64]; + + emu = snd_magic_cast(snd_emux_t, closure, return -EINVAL); + snd_assert(arg != NULL && emu != NULL, return -ENXIO); + + down(&emu->register_mutex); + + if (!snd_emux_inc_count(emu)) { + up(&emu->register_mutex); + return -EFAULT; + } + + memset(&callback, 0, sizeof(callback)); + callback.owner = THIS_MODULE; + callback.event_input = snd_emux_event_oss_input; + + sprintf(tmpname, "%s OSS Port", emu->name); + p = snd_emux_create_port(emu, tmpname, 32, + 1, &callback); + if (p == NULL) { + snd_printk("can't create port\n"); + snd_emux_dec_count(emu); + up(&emu->register_mutex); + return -ENOMEM; + } + + /* fill the argument data */ + arg->private_data = p; + arg->addr.client = p->chset.client; + arg->addr.port = p->chset.port; + p->oss_arg = arg; + + reset_port_mode(p, arg->seq_mode); + + snd_emux_reset_port(p); + + up(&emu->register_mutex); + return 0; +} + + +#define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25)) + +/* + * reset port mode + */ +static void +reset_port_mode(snd_emux_port_t *port, int midi_mode) +{ + if (midi_mode) { + port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI; + port->drum_flags = DEFAULT_DRUM_FLAGS; + port->volume_atten = 0; + port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS; + } else { + port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH; + port->drum_flags = 0; + port->volume_atten = 32; + port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; + } +} + + +/* + * close port + */ +static int +snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + down(&emu->register_mutex); + snd_emux_sounds_off_all(p); + snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); + snd_seq_event_port_detach(p->chset.client, p->chset.port); + snd_emux_dec_count(emu); + + up(&emu->register_mutex); + return 0; +} + + +/* + * load patch + */ +static int +snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, + const char *buf, int offs, int count) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + int rc; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + if (format == GUS_PATCH) + rc = snd_soundfont_load_guspatch(emu->sflist, buf, count, + SF_CLIENT_NO(p->chset.port)); + else if (format == SNDRV_OSS_SOUNDFONT_PATCH) { + soundfont_patch_info_t patch; + if (count < (int)sizeof(patch)) + rc = -EINVAL; + if (copy_from_user(&patch, buf, sizeof(patch))) + rc = -EFAULT; + if (patch.type >= SNDRV_SFNT_LOAD_INFO && + patch.type <= SNDRV_SFNT_PROBE_DATA) + rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port)); + else { + if (emu->ops.load_fx) + rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count); + else + rc = -EINVAL; + } + } else + rc = 0; + return rc; +} + + +/* + * ioctl + */ +static int +snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + switch (cmd) { + case SNDCTL_SEQ_RESETSAMPLES: + snd_soundfont_remove_samples(emu->sflist); + return 0; + + case SNDCTL_SYNTH_MEMAVL: + if (emu->memhdr) + return snd_util_mem_avail(emu->memhdr); + return 0; + } + + return 0; +} + + +/* + * reset device + */ +static int +snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg) +{ + snd_emux_port_t *p; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + snd_emux_reset_port(p); + return 0; +} + + +/* + * receive raw events: only SEQ_PRIVATE is accepted. + */ +static int +snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + unsigned char cmd, *data; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + if (ev->type != SNDRV_SEQ_EVENT_OSS) + return snd_emux_event_input(ev, direct, private_data, atomic, hop); + + data = ev->data.raw8.d; + /* only SEQ_PRIVATE is accepted */ + if (data[0] != 0xfe) + return 0; + cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK; + if (data[2] & _EMUX_OSS_MODE_FLAG) + emuspec_control(emu, p, cmd, data, atomic, hop); + else + gusspec_control(emu, p, cmd, data, atomic, hop); + return 0; +} + + +/* + * OSS/AWE driver specific h/w controls + */ +static void +emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, + unsigned char *event, int atomic, int hop) +{ + int voice; + unsigned short p1; + short p2; + int i; + snd_midi_channel_t *chan; + + voice = event[3]; + if (voice < 0 || voice >= port->chset.max_channels) + chan = NULL; + else + chan = &port->chset.channels[voice]; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + + switch (cmd) { + case _EMUX_OSS_REMOVE_LAST_SAMPLES: + snd_soundfont_remove_unlocked(emu->sflist); + break; + case _EMUX_OSS_SEND_EFFECT: + if (chan) + snd_emux_send_effect_oss(port, chan, p1, p2); + break; + + case _EMUX_OSS_TERMINATE_ALL: + snd_emux_terminate_all(emu); + break; + + case _EMUX_OSS_TERMINATE_CHANNEL: + /*snd_emux_mute_channel(emu, chan);*/ + break; + case _EMUX_OSS_RESET_CHANNEL: + /*snd_emux_channel_init(chset, chan);*/ + break; + + case _EMUX_OSS_RELEASE_ALL: + fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop); + break; + case _EMUX_OSS_NOTEOFF_ALL: + fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop); + break; + + case _EMUX_OSS_INITIAL_VOLUME: + if (p2) { + port->volume_atten = (short)p1; + snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); + } + break; + + case _EMUX_OSS_CHN_PRESSURE: + if (chan) { + chan->midi_pressure = p1; + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2); + } + break; + + case _EMUX_OSS_CHANNEL_MODE: + reset_port_mode(port, p1); + snd_emux_reset_port(port); + break; + + case _EMUX_OSS_DRUM_CHANNELS: + port->drum_flags = *(unsigned int*)&event[4]; + for (i = 0; i < port->chset.max_channels; i++) { + chan = &port->chset.channels[i]; + chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; + } + break; + + case _EMUX_OSS_MISC_MODE: + if (p1 < EMUX_MD_END) + port->ctrls[p1] = p2; + break; + case _EMUX_OSS_DEBUG_MODE: + break; + + default: + if (emu->ops.oss_ioctl) + emu->ops.oss_ioctl(emu, cmd, p1, p2); + break; + } +} + +/* + * GUS specific h/w controls + */ + +#include + +static void +gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, + unsigned char *event, int atomic, int hop) +{ + int voice; + unsigned short p1; + short p2; + int plong; + snd_midi_channel_t *chan; + + if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH) + return; + if (cmd == _GUS_NUMVOICES) + return; + voice = event[3]; + if (voice < 0 || voice >= port->chset.max_channels) + return; + + chan = &port->chset.channels[voice]; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + chan->midi_program = p1; + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> 0 to 127 */ + chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3; + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START, + (short)(plong & 0x7fff), + EMUX_FX_FLAG_SET); + snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff, + EMUX_FX_FLAG_SET); +#endif + return; + } +} + + +/* + * send an event to midi emulation + */ +static void +fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop) +{ + snd_seq_event_t ev; + memset(&ev, 0, sizeof(ev)); + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + ev.data.control.channel = ch; + ev.data.control.param = param; + ev.data.control.value = val; + snd_emux_event_input(&ev, 0, port, atomic, hop); +} + +#endif /* CONFIG_SND_SEQUENCER_OSS */ diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_proc.c linux/sound/synth/emux/emux_proc.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_proc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_proc.c 2002-08-12 02:43:50.000000000 -0600 @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Proc interface for Emu8k/Emu10k1 WaveTable synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "emux_voice.h" + +#ifdef CONFIG_PROC_FS + +static void +snd_emux_proc_info_read(snd_info_entry_t *entry, + snd_info_buffer_t *buf) +{ + snd_emux_t *emu; + int i; + + emu = snd_magic_cast(snd_emux_t, entry->private_data, return); + down(&emu->register_mutex); + if (emu->name) + snd_iprintf(buf, "Device: %s\n", emu->name); + snd_iprintf(buf, "Ports: %d\n", emu->num_ports); + snd_iprintf(buf, "Addresses:"); + for (i = 0; i < emu->num_ports; i++) + snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]); + snd_iprintf(buf, "\n"); + snd_iprintf(buf, "Use Counter: %d\n", emu->used); + snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices); + snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices); + if (emu->memhdr) { + snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size); + snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr)); + snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks); + } else { + snd_iprintf(buf, "Memory Size: 0\n"); + } + if (emu->sflist) { + down(&emu->sflist->presets_mutex); + snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size); + snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter); + snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter); + snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked); + snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked); + up(&emu->sflist->presets_mutex); + } +#if 0 /* debug */ + if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) { + snd_emux_voice_t *vp = &emu->voices[0]; + snd_iprintf(buf, "voice 0: on\n"); + snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", + vp->reg.parm.moddelay, + vp->reg.parm.modatkhld, + vp->reg.parm.moddcysus, + vp->reg.parm.modrelease); + snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", + vp->reg.parm.voldelay, + vp->reg.parm.volatkhld, + vp->reg.parm.voldcysus, + vp->reg.parm.volrelease); + snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n", + vp->reg.parm.lfo1delay, + vp->reg.parm.lfo2delay, + vp->reg.parm.pefe); + snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n", + vp->reg.parm.fmmod, + vp->reg.parm.tremfrq, + vp->reg.parm.fm2frq2); + snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n", + vp->reg.parm.cutoff, + vp->reg.parm.filterQ, + vp->reg.parm.chorus, + vp->reg.parm.reverb); + snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n", + vp->avol, vp->acutoff, vp->apitch); + snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n", + vp->apan, vp->aaux, + vp->ptarget, + vp->vtarget, + vp->ftarget); + snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n", + vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend); + snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset); + } +#endif + up(&emu->register_mutex); +} + + +void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device) +{ + snd_info_entry_t *entry; + char name[64]; + + sprintf(name, "wavetableD%d", device); + entry = snd_info_create_card_entry(card, name, card->proc_root); + if (entry == NULL) + return; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_emux_proc_info_read; + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); + else + emu->proc = entry; +} + +void snd_emux_proc_free(snd_emux_t *emu) +{ + if (emu->proc) { + snd_info_unregister(emu->proc); + emu->proc = NULL; + } +} + +#endif /* CONFIG_PROC_FS */ diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_seq.c linux/sound/synth/emux/emux_seq.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_seq.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_seq.c 2003-02-28 08:08:26.000000000 -0700 @@ -0,0 +1,428 @@ +/* + * Midi Sequencer interface routines. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emux_voice.h" +#include + + +/* Prototypes for static functions */ +static void free_port(void *private); +static void snd_emux_init_port(snd_emux_port_t *p); +static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info); +static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info); +static int get_client(snd_card_t *card, int index, char *name); + +/* + * MIDI emulation operators + */ +static snd_midi_op_t emux_ops = { + snd_emux_note_on, + snd_emux_note_off, + snd_emux_key_press, + snd_emux_terminate_note, + snd_emux_control, + snd_emux_nrpn, + snd_emux_sysex, +}; + + +/* + * number of MIDI channels + */ +#define MIDI_CHANNELS 16 + +/* + * type flags for MIDI sequencer port + */ +#define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ + SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ + SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ + SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) + +/* + * Initialise the EMUX Synth by creating a client and registering + * a series of ports. + * Each of the ports will contain the 16 midi channels. Applications + * can connect to these ports to play midi data. + */ +int +snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index) +{ + int i; + snd_seq_port_callback_t pinfo; + char tmpname[64]; + + sprintf(tmpname, "%s WaveTable", emu->name); + emu->client = get_client(card, index, tmpname); + if (emu->client < 0) { + snd_printk("can't create client\n"); + return -ENODEV; + } + + if (emu->num_ports < 0) { + snd_printk("seqports must be greater than zero\n"); + emu->num_ports = 1; + } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { + snd_printk("too many ports." + "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); + emu->num_ports = SNDRV_EMUX_MAX_PORTS; + } + + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.owner = THIS_MODULE; + pinfo.use = snd_emux_use; + pinfo.unuse = snd_emux_unuse; + pinfo.event_input = snd_emux_event_input; + + for (i = 0; i < emu->num_ports; i++) { + snd_emux_port_t *p; + + sprintf(tmpname, "%s Port %d", emu->name, i); + p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, + 0, &pinfo); + if (p == NULL) { + snd_printk("can't create port\n"); + return -ENOMEM; + } + + p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; + snd_emux_init_port(p); + emu->ports[i] = p->chset.port; + } + + return 0; +} + + +/* + * Detach from the ports that were set up for this synthesizer and + * destroy the kernel client. + */ +void +snd_emux_detach_seq(snd_emux_t *emu) +{ + if (emu->voices) + snd_emux_terminate_all(emu); + + down(&emu->register_mutex); + if (emu->client >= 0) { + snd_seq_delete_kernel_client(emu->client); + emu->client = -1; + } + up(&emu->register_mutex); +} + + +/* + * create a sequencer port and channel_set + */ + +snd_emux_port_t * +snd_emux_create_port(snd_emux_t *emu, char *name, + int max_channels, int oss_port, + snd_seq_port_callback_t *callback) +{ + snd_emux_port_t *p; + int i, type, cap; + + /* Allocate structures for this channel */ + if ((p = snd_magic_kcalloc(snd_emux_port_t, 0, GFP_KERNEL)) == NULL) { + snd_printk("no memory\n"); + return NULL; + } + p->chset.channels = snd_kcalloc(max_channels * sizeof(snd_midi_channel_t), GFP_KERNEL); + if (p->chset.channels == NULL) { + snd_printk("no memory\n"); + snd_magic_kfree(p); + return NULL; + } + for (i = 0; i < max_channels; i++) + p->chset.channels[i].number = i; + p->chset.private_data = p; + p->chset.max_channels = max_channels; + p->emu = emu; + p->chset.client = emu->client; +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_create_effect(p); +#endif + callback->private_free = free_port; + callback->private_data = p; + + cap = SNDRV_SEQ_PORT_CAP_WRITE; + if (oss_port) { + type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; + } else { + type = DEFAULT_MIDI_TYPE; + cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + } + + p->chset.port = snd_seq_event_port_attach(emu->client, callback, + cap, type, max_channels, + emu->max_voices, name); + + return p; +} + + +/* + * release memory block for port + */ +static void +free_port(void *private_data) +{ + snd_emux_port_t *p; + + p = snd_magic_cast(snd_emux_port_t, private_data, return); + if (p) { +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_delete_effect(p); +#endif + if (p->chset.channels) + kfree(p->chset.channels); + snd_magic_kfree(p); + } +} + + +#define DEFAULT_DRUM_FLAGS (1<<9) + +/* + * initialize the port specific parameters + */ +static void +snd_emux_init_port(snd_emux_port_t *p) +{ + p->drum_flags = DEFAULT_DRUM_FLAGS; + p->volume_atten = 0; + + snd_emux_reset_port(p); +} + + +/* + * reset port + */ +void +snd_emux_reset_port(snd_emux_port_t *port) +{ + int i; + + /* stop all sounds */ + snd_emux_sounds_off_all(port); + + snd_midi_channel_set_clear(&port->chset); + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_clear_effect(port); +#endif + + /* set port specific control parameters */ + port->ctrls[EMUX_MD_DEF_BANK] = 0; + port->ctrls[EMUX_MD_DEF_DRUM] = 0; + port->ctrls[EMUX_MD_REALTIME_PAN] = 1; + + for (i = 0; i < port->chset.max_channels; i++) { + snd_midi_channel_t *chan = port->chset.channels + i; + chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; + } +} + + +/* + * input sequencer event + */ +int +snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(port != NULL && ev != NULL, return -EINVAL); + + snd_midi_process_event(&emux_ops, ev, &port->chset); + + return 0; +} + + +/* + * increment usage count + */ +int +snd_emux_inc_count(snd_emux_t *emu) +{ + emu->used++; + if (!try_module_get(emu->ops.owner)) + goto __error; + if (!try_module_get(emu->card->module)) { + module_put(emu->ops.owner); + __error: + emu->used--; + return 0; + } + return 1; +} + + +/* + * decrease usage count + */ +void +snd_emux_dec_count(snd_emux_t *emu) +{ + module_put(emu->card->module); + emu->used--; + if (emu->used <= 0) + snd_emux_terminate_all(emu); + module_put(emu->ops.owner); +} + + +/* + * Routine that is called upon a first use of a particular port + */ +static int +snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + + down(&emu->register_mutex); + snd_emux_init_port(p); + snd_emux_inc_count(emu); + up(&emu->register_mutex); + return 0; +} + +/* + * Routine that is called upon the last unuse() of a particular port. + */ +static int +snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + + down(&emu->register_mutex); + snd_emux_sounds_off_all(p); + snd_emux_dec_count(emu); + up(&emu->register_mutex); + return 0; +} + + +/* + * Create a sequencer client + */ +static int +get_client(snd_card_t *card, int index, char *name) +{ + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + int client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = NULL; + callbacks.allow_input = 1; + callbacks.allow_output = 1; + + /* Find a free client, start from 1 as the MPU expects to use 0 */ + client = snd_seq_create_kernel_client(card, index, &callbacks); + if (client < 0) + return client; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + strcpy(cinfo.name, name); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + return client; +} + + +/* + * attach virtual rawmidi devices + */ +int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card) +{ + int i; + + emu->vmidi = NULL; + if (emu->midi_ports <= 0) + return 0; + + emu->vmidi = snd_kcalloc(sizeof(snd_rawmidi_t*) * emu->midi_ports, GFP_KERNEL); + if (emu->vmidi == NULL) + return -ENOMEM; + + for (i = 0; i < emu->midi_ports; i++) { + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) + goto __error; + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + sprintf(rmidi->name, "%s Synth MIDI", emu->name); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; + rdev->client = emu->client; + rdev->port = emu->ports[i]; + if (snd_device_register(card, rmidi) < 0) { + snd_device_free(card, rmidi); + goto __error; + } + emu->vmidi[i] = rmidi; + //snd_printk("virmidi %d ok\n", i); + } + return 0; + +__error: + //snd_printk("error init..\n"); + snd_emux_delete_virmidi(emu); + return -ENOMEM; +} + +int snd_emux_delete_virmidi(snd_emux_t *emu) +{ + int i; + + if (emu->vmidi == NULL) + return 0; + + for (i = 0; i < emu->midi_ports; i++) { + if (emu->vmidi[i]) + snd_device_free(emu->card, emu->vmidi[i]); + } + kfree(emu->vmidi); + emu->vmidi = NULL; + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_synth.c linux/sound/synth/emux/emux_synth.c --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_synth.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_synth.c 2002-08-12 02:43:50.000000000 -0600 @@ -0,0 +1,973 @@ +/* + * Midi synth routines for the Emu8k/Emu10k1 + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * Contains code based on awe_wave.c by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "emux_voice.h" +#include + +/* + * Prototypes + */ + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + +static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table); +static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan); +static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free); +static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass); +static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free); +static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update); +static void setup_voice(snd_emux_voice_t *vp); +static int calc_pan(snd_emux_voice_t *vp); +static int calc_volume(snd_emux_voice_t *vp); +static int calc_pitch(snd_emux_voice_t *vp); + + +/* + * Start a note. + */ +void +snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + snd_emux_t *emu; + int i, key, nvoices; + snd_emux_voice_t *vp; + snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES]; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.get_voice != NULL, return); + snd_assert(emu->ops.trigger != NULL, return); + + key = note; /* remember the original note */ + nvoices = get_zone(emu, port, ¬e, vel, chan, table); + if (! nvoices) + return; + + /* exclusive note off */ + for (i = 0; i < nvoices; i++) { + snd_sf_zone_t *zp = table[i]; + if (zp && zp->v.exclusiveClass) + exclusive_note_off(emu, port, zp->v.exclusiveClass); + } + +#if 0 // seems not necessary + /* Turn off the same note on the same channel. */ + terminate_note1(emu, key, chan, 0); +#endif + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < nvoices; i++) { + + /* set up each voice parameter */ + /* at this stage, we don't trigger the voice yet. */ + + if (table[i] == NULL) + continue; + + vp = emu->ops.get_voice(emu, port); + if (vp == NULL || vp->ch < 0) + continue; + snd_assert(vp->emu != NULL && vp->hw != NULL, return); + if (STATE_IS_PLAYING(vp->state)) + emu->ops.terminate(vp); + + vp->time = emu->use_time++; + vp->chan = chan; + vp->port = port; + vp->key = key; + vp->note = note; + vp->velocity = vel; + vp->zone = table[i]; + if (vp->zone->sample) + vp->block = vp->zone->sample->block; + else + vp->block = NULL; + + setup_voice(vp); + + vp->state = SNDRV_EMUX_ST_STANDBY; + if (emu->ops.prepare) { + vp->state = SNDRV_EMUX_ST_OFF; + if (emu->ops.prepare(vp) >= 0) + vp->state = SNDRV_EMUX_ST_STANDBY; + } + } + + /* start envelope now */ + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->state == SNDRV_EMUX_ST_STANDBY && + vp->chan == chan) { + emu->ops.trigger(vp); + vp->state = SNDRV_EMUX_ST_ON; + vp->ontime = jiffies; /* remember the trigger timing */ + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { + /* clear voice position for the next note on this channel */ + snd_emux_effect_table_t *fx = chan->private; + if (fx) { + fx->flag[EMUX_FX_SAMPLE_START] = 0; + fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0; + } + } +#endif +} + +/* + * Release a note in response to a midi note off. + */ +void +snd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + int ch; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.release != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (STATE_IS_PLAYING(vp->state) && + vp->chan == chan && vp->key == note) { + vp->time = emu->use_time++; + vp->state = SNDRV_EMUX_ST_RELEASED; + if (vp->ontime == jiffies) { + /* if note-off is sent too shortly after + * note-on, emuX engine cannot produce the sound + * correctly. so we'll release this note + * a bit later via timer callback. + */ + vp->state = SNDRV_EMUX_ST_PENDING; + if (! emu->timer_active) { + emu->tlist.expires = jiffies + 1; + add_timer(&emu->tlist); + emu->timer_active = 1; + } + } else + /* ok now release the note */ + emu->ops.release(vp); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * timer callback + * + * release the pending note-offs + */ +void snd_emux_timer_callback(unsigned long data) +{ + snd_emux_t *emu = snd_magic_cast(snd_emux_t, (void*)data, return); + snd_emux_voice_t *vp; + int ch, do_again = 0; + + spin_lock(&emu->voice_lock); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (vp->state == SNDRV_EMUX_ST_PENDING) { + if (vp->ontime == jiffies) + do_again++; /* release this at the next interrupt */ + else { + emu->ops.release(vp); + vp->state = SNDRV_EMUX_ST_RELEASED; + } + } + } + if (do_again) { + emu->tlist.expires = jiffies + 1; + add_timer(&emu->tlist); + emu->timer_active = 1; + } else + emu->timer_active = 0; + spin_unlock(&emu->voice_lock); +} + +/* + * key pressure change + */ +void +snd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + int ch; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (vp->state == SNDRV_EMUX_ST_ON && + vp->chan == chan && vp->key == note) { + vp->velocity = vel; + update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Modulate the voices which belong to the channel + */ +void +snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + if (! update) + return; + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->chan == chan) + update_voice(emu, vp, update); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * Modulate all the voices which belong to the port. + */ +void +snd_emux_update_port(snd_emux_port_t *port, int update) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + if (! update) + return; + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->port == port) + update_voice(emu, vp, update); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Deal with a controler type event. This includes all types of + * control events, not just the midi controllers + */ +void +snd_emux_control(void *p, int type, snd_midi_channel_t *chan) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + switch (type) { + case MIDI_CTL_MSB_MAIN_VOLUME: + case MIDI_CTL_MSB_EXPRESSION: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME); + break; + + case MIDI_CTL_MSB_PAN: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); + break; + + case MIDI_CTL_SOFT_PEDAL: +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + /* FIXME: this is an emulation */ + snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160, + EMUX_FX_FLAG_ADD); +#endif + break; + + case MIDI_CTL_PITCHBEND: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH); + break; + + case MIDI_CTL_MSB_MODWHEEL: + case MIDI_CTL_CHAN_PRESSURE: + snd_emux_update_channel(port, chan, + SNDRV_EMUX_UPDATE_FMMOD | + SNDRV_EMUX_UPDATE_FM2FRQ2); + break; + + } + + if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) { + snd_emux_xg_control(port, chan, type); + } +} + + +/* + * for Emu10k1 - release at least 1 voice currently using + */ +int +snd_emux_release_voice(snd_emux_t *emu) +{ + return 0; +} + + +/* + * terminate note - if free flag is true, free the terminated voice + */ +static void +terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free) +{ + int i; + snd_emux_voice_t *vp; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && vp->chan == chan && + vp->key == note) + terminate_voice(emu, vp, free); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * terminate note - exported for midi emulation + */ +void +snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan) +{ + snd_emux_t *emu; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.terminate != NULL, return); + + terminate_note1(emu, note, chan, 1); +} + + +/* + * Terminate all the notes + */ +void +snd_emux_terminate_all(snd_emux_t *emu) +{ + int i; + snd_emux_voice_t *vp; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state)) + terminate_voice(emu, vp, 0); + if (vp->state == SNDRV_EMUX_ST_OFF) { + if (emu->ops.free_voice) + emu->ops.free_voice(vp); + if (emu->ops.reset) + emu->ops.reset(emu, i); + } + vp->time = 0; + } + /* initialize allocation time */ + emu->use_time = 0; + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Terminate all voices associated with the given port + */ +void +snd_emux_sounds_off_all(snd_emux_port_t *port) +{ + int i; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + + snd_assert(port != NULL, return); + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.terminate != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && + vp->port == port) + terminate_voice(emu, vp, 0); + if (vp->state == SNDRV_EMUX_ST_OFF) { + if (emu->ops.free_voice) + emu->ops.free_voice(vp); + if (emu->ops.reset) + emu->ops.reset(emu, i); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Terminate all voices that have the same exclusive class. This + * is mainly for drums. + */ +static void +exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass) +{ + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && vp->port == port && + vp->reg.exclusiveClass == exclass) { + terminate_voice(emu, vp, 0); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * terminate a voice + * if free flag is true, call free_voice after termination + */ +static void +terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free) +{ + emu->ops.terminate(vp); + vp->time = emu->use_time++; + vp->chan = NULL; + vp->port = NULL; + vp->zone = NULL; + vp->block = NULL; + vp->state = SNDRV_EMUX_ST_OFF; + if (free && emu->ops.free_voice) + emu->ops.free_voice(vp); +} + + +/* + * Modulate the voice + */ +static void +update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update) +{ + if (!STATE_IS_PLAYING(vp->state)) + return; + + if (vp->chan == NULL || vp->port == NULL) + return; + if (update & SNDRV_EMUX_UPDATE_VOLUME) + calc_volume(vp); + if (update & SNDRV_EMUX_UPDATE_PITCH) + calc_pitch(vp); + if (update & SNDRV_EMUX_UPDATE_PAN) { + if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN)) + return; + } + emu->ops.update(vp, update); +} + + +#if 0 // not used +/* table for volume target calculation */ +static unsigned short voltarget[16] = { + 0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58, + 0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90 +}; +#endif + +#define LO_BYTE(v) ((v) & 0xff) +#define HI_BYTE(v) (((v) >> 8) & 0xff) + +/* + * Sets up the voice structure by calculating some values that + * will be needed later. + */ +static void +setup_voice(snd_emux_voice_t *vp) +{ + soundfont_voice_parm_t *parm; + int pitch; + + /* copy the original register values */ + vp->reg = vp->zone->v; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_setup_effect(vp); +#endif + + /* reset status */ + vp->apan = -1; + vp->avol = -1; + vp->apitch = -1; + + calc_volume(vp); + calc_pitch(vp); + calc_pan(vp); + + parm = &vp->reg.parm; + + /* compute filter target and correct modulation parameters */ + if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) { + parm->moddelay = 0xbfff; + pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch; + if (pitch > 0xffff) + pitch = 0xffff; + /* calculate filter target */ + vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe); + LIMITVALUE(vp->ftarget, 0, 255); + vp->ftarget <<= 8; + } else { + vp->ftarget = parm->cutoff; + vp->ftarget <<= 8; + pitch = vp->apitch; + } + + /* compute pitch target */ + if (pitch != 0xffff) { + vp->ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710; + if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710; + if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710; + vp->ptarget += (vp->ptarget >> 1); + if (vp->ptarget > 0xffff) vp->ptarget = 0xffff; + } else + vp->ptarget = 0xffff; + + if (LO_BYTE(parm->modatkhld) >= 0x80) { + parm->modatkhld &= ~0xff; + parm->modatkhld |= 0x7f; + } + + /* compute volume target and correct volume parameters */ + vp->vtarget = 0; +#if 0 /* FIXME: this leads to some clicks.. */ + if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) { + parm->voldelay = 0xbfff; + vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4); + } +#endif + + if (LO_BYTE(parm->volatkhld) >= 0x80) { + parm->volatkhld &= ~0xff; + parm->volatkhld |= 0x7f; + } +} + +/* + * calculate pitch parameter + */ +static unsigned char pan_volumes[256] = { +0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a, +0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52, +0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75, +0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92, +0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab, +0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0, +0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1, +0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf, +0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9, +0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1, +0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7, +0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb, +0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, +0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +}; + +static int +calc_pan(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + int pan; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->reg.fixpan > 0) /* 0-127 */ + pan = 255 - (int)vp->reg.fixpan * 2; + else { + pan = chan->control[MIDI_CTL_MSB_PAN] - 64; + if (vp->reg.pan >= 0) /* 0-127 */ + pan += vp->reg.pan - 64; + pan = 127 - (int)pan * 2; + } + LIMITVALUE(pan, 0, 255); + + if (vp->emu->linear_panning) { + /* assuming linear volume */ + if (pan != vp->apan) { + vp->apan = pan; + if (pan == 0) + vp->aaux = 0xff; + else + vp->aaux = (-pan) & 0xff; + return 1; + } else + return 0; + } else { + /* using volume table */ + if (vp->apan != (int)pan_volumes[pan]) { + vp->apan = pan_volumes[pan]; + vp->aaux = pan_volumes[255 - pan]; + return 1; + } + return 0; + } +} + + +/* + * calculate volume attenuation + * + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + */ + +/* tables for volume->attenuation calculation */ +static unsigned char voltab1[128] = { + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char voltab2[128] = { + 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, + 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, + 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char expressiontab[128] = { + 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, + 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, + 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, + 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * Magic to calculate the volume (actually attenuation) from all the + * voice and channels parameters. + */ +static int +calc_volume(snd_emux_voice_t *vp) +{ + int vol; + int main_vol, expression_vol, master_vol; + snd_midi_channel_t *chan = vp->chan; + snd_emux_port_t *port = vp->port; + + expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION]; + LIMITMAX(vp->velocity, 127); + LIMITVALUE(expression_vol, 0, 127); + if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { + /* 0 - 127 */ + main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME]; + vol = (vp->velocity * main_vol * expression_vol) / (127*127); + vol = vol * vp->reg.amplitude / 127; + + LIMITVALUE(vol, 0, 127); + + /* calc to attenuation */ + vol = snd_sf_vol_table[vol]; + + } else { + main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127; + LIMITVALUE(main_vol, 0, 127); + + vol = voltab1[main_vol] + voltab2[vp->velocity]; + vol = (vol * 8) / 3; + vol += vp->reg.attenuation; + vol += ((0x100 - vol) * expressiontab[expression_vol])/128; + } + + master_vol = port->chset.gs_master_volume; + LIMITVALUE(master_vol, 0, 127); + vol += snd_sf_vol_table[master_vol]; + vol += port->volume_atten; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + if (chan->private) { + snd_emux_effect_table_t *fx = chan->private; + vol += fx->val[EMUX_FX_ATTEN]; + } +#endif + + LIMITVALUE(vol, 0, 255); + if (vp->avol == vol) + return 0; /* value unchanged */ + + vp->avol = vol; + if (!SF_IS_DRUM_BANK(get_bank(port, chan)) + && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) { + int atten; + if (vp->velocity < 70) + atten = 70; + else + atten = vp->velocity; + vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7; + } else { + vp->acutoff = vp->reg.parm.cutoff; + } + + return 1; /* value changed */ +} + +/* + * calculate pitch offset + * + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + */ + +static int +calc_pitch(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + int offset; + + /* calculate offset */ + if (vp->reg.fixkey >= 0) { + offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12; + } else { + offset = (vp->note - vp->reg.root) * 4096 / 12; + } + offset = (offset * vp->reg.scaleTuning) / 100; + offset += vp->reg.tune * 4096 / 1200; + if (chan->midi_pitchbend != 0) { + /* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */ + offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072; + } + + /* tuning via RPN: + * coarse = -8192 to 8192 (100 cent per 128) + * fine = -8192 to 8192 (max=100cent) + */ + /* 4096 = 1200 cents in emu8000 parameter */ + offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128); + offset += chan->gm_rpn_fine_tuning / 24; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + /* add initial pitch correction */ + if (chan->private) { + snd_emux_effect_table_t *fx = chan->private; + if (fx->flag[EMUX_FX_INIT_PITCH]) + offset += fx->val[EMUX_FX_INIT_PITCH]; + } +#endif + + /* 0xe000: root pitch */ + offset += 0xe000 + vp->reg.rate_offset; + offset += vp->emu->pitch_shift; + LIMITVALUE(offset, 0, 0xffff); + if (offset == vp->apitch) + return 0; /* unchanged */ + vp->apitch = offset; + return 1; /* value changed */ +} + +/* + * Get the bank number assigned to the channel + */ +static int +get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan) +{ + int val; + + switch (port->chset.midi_mode) { + case SNDRV_MIDI_MODE_XG: + val = chan->control[MIDI_CTL_MSB_BANK]; + if (val == 127) + return 128; /* return drum bank */ + return chan->control[MIDI_CTL_LSB_BANK]; + + case SNDRV_MIDI_MODE_GS: + if (chan->drum_channel) + return 128; + /* ignore LSB (bank map) */ + return chan->control[MIDI_CTL_MSB_BANK]; + + default: + if (chan->drum_channel) + return 128; + return chan->control[MIDI_CTL_MSB_BANK]; + } +} + + +/* Look for the zones matching with the given note and velocity. + * The resultant zones are stored on table. + */ +static int +get_zone(snd_emux_t *emu, snd_emux_port_t *port, + int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table) +{ + int preset, bank, def_preset, def_bank; + + bank = get_bank(port, chan); + preset = chan->midi_program; + + if (SF_IS_DRUM_BANK(bank)) { + def_preset = port->ctrls[EMUX_MD_DEF_DRUM]; + def_bank = bank; + } else { + def_preset = preset; + def_bank = port->ctrls[EMUX_MD_DEF_BANK]; + } + + return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank, + def_preset, def_bank, + table, SNDRV_EMUX_MAX_MULTI_VOICES); +} + +/* + */ +void +snd_emux_init_voices(snd_emux_t *emu) +{ + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + vp->ch = -1; /* not used */ + vp->state = SNDRV_EMUX_ST_OFF; + vp->chan = NULL; + vp->port = NULL; + vp->time = 0; + vp->emu = emu; + vp->hw = emu->hw; + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + */ +void snd_emux_lock_voice(snd_emux_t *emu, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF) + emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED; + else + snd_printk("invalid voice for lock %d (state = %x)\n", + voice, emu->voices[voice].state); + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + */ +void snd_emux_unlock_voice(snd_emux_t *emu, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED) + emu->voices[voice].state = SNDRV_EMUX_ST_OFF; + else + snd_printk("invalid voice for unlock %d (state = %x)\n", + voice, emu->voices[voice].state); + spin_unlock_irqrestore(&emu->voice_lock, flags); +} diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/emux_voice.h linux/sound/synth/emux/emux_voice.h --- linux-2.4.21-rc1.orig/sound/synth/emux/emux_voice.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/emux_voice.h 2002-02-14 10:40:34.000000000 -0700 @@ -0,0 +1,84 @@ +#ifndef __EMUX_VOICE_H +#define __EMUX_VOICE_H + +/* + * A structure to keep track of each hardware voice + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +/* Prototypes for emux_seq.c */ +int snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index); +void snd_emux_detach_seq(snd_emux_t *emu); +snd_emux_port_t *snd_emux_create_port(snd_emux_t *emu, char *name, int max_channels, int type, snd_seq_port_callback_t *callback); +void snd_emux_reset_port(snd_emux_port_t *port); +int snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +int snd_emux_inc_count(snd_emux_t *emu); +void snd_emux_dec_count(snd_emux_t *emu); +int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card); +int snd_emux_delete_virmidi(snd_emux_t *emu); + +/* Prototypes for emux_synth.c */ +void snd_emux_init_voices(snd_emux_t *emu); + +void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan); +void snd_emux_control(void *p, int type, struct snd_midi_channel *chan); + +void snd_emux_sounds_off_all(snd_emux_port_t *port); +void snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update); +void snd_emux_update_port(snd_emux_port_t *port, int update); + +void snd_emux_timer_callback(unsigned long data); + +/* emux_effect.c */ +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +void snd_emux_create_effect(snd_emux_port_t *p); +void snd_emux_delete_effect(snd_emux_port_t *p); +void snd_emux_clear_effect(snd_emux_port_t *p); +void snd_emux_setup_effect(snd_emux_voice_t *vp); +void snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val); +void snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode); +#endif + +/* emux_nrpn.c */ +void snd_emux_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param); +void snd_emux_nrpn(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); + +/* emux_oss.c */ +void snd_emux_init_seq_oss(snd_emux_t *emu); +void snd_emux_detach_seq_oss(snd_emux_t *emu); + +/* emux_proc.c */ +#ifdef CONFIG_PROC_FS +void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device); +void snd_emux_proc_free(snd_emux_t *emu); +#endif + +#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON) + +#endif diff -urN linux-2.4.21-rc1.orig/sound/synth/emux/soundfont.c linux/sound/synth/emux/soundfont.c --- linux-2.4.21-rc1.orig/sound/synth/emux/soundfont.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/emux/soundfont.c 2003-01-31 08:21:29.000000000 -0700 @@ -0,0 +1,1461 @@ +/* + * Soundfont generic routines. + * It is intended that these should be used by any driver that is willing + * to accept soundfont patches. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Deal with reading in of a soundfont. Code follows the OSS way + * of doing things so that the old sfxload utility can be used. + * Everything may change when there is an alsa way of doing things. + */ +#include +#include +#include +#include +#include + +/* Prototypes for static functions */ + +static int open_patch(snd_sf_list_t *sflist, const char *data, int count, int client); +static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name); +static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name); +static int close_patch(snd_sf_list_t *sflist); +static int probe_data(snd_sf_list_t *sflist, int sample_id); +static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp); +static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); +static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); +static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); +static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); +static int load_map(snd_sf_list_t *sflist, const void *data, int count); +static int load_info(snd_sf_list_t *sflist, const void *data, long count); +static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr); +static void init_voice_info(soundfont_voice_info_t *avp); +static void init_voice_parm(soundfont_voice_parm_t *pp); +static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp); +static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id); +static int load_data(snd_sf_list_t *sflist, const void *data, long count); +static void rebuild_presets(snd_sf_list_t *sflist); +static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur); +static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp); +static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key); +static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level); +static int get_index(int bank, int instr, int key); +static void snd_sf_init(snd_sf_list_t *sflist); +static void snd_sf_clear(snd_sf_list_t *sflist); + +/* + * lock access to sflist + */ +static int +lock_preset(snd_sf_list_t *sflist, int nonblock) +{ + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->sf_locked && nonblock) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + down(&sflist->presets_mutex); + sflist->sf_locked = 1; + return 0; +} + + +/* + * remove lock + */ +static void +unlock_preset(snd_sf_list_t *sflist) +{ + up(&sflist->presets_mutex); + sflist->sf_locked = 0; +} + + +/* + * close the patch if the patch was opened by this client. + */ +int +snd_soundfont_close_check(snd_sf_list_t *sflist, int client) +{ + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client == client) { + spin_unlock_irqrestore(&sflist->lock, flags); + return close_patch(sflist); + } + spin_unlock_irqrestore(&sflist->lock, flags); + return 0; +} + + +/* + * Deal with a soundfont patch. Any driver could use these routines + * although it was designed for the AWE64. + * + * The sample_write and callargs pararameters allow a callback into + * the actual driver to write sample data to the board or whatever + * it wants to do with it. + */ +int +snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client) +{ + soundfont_patch_info_t patch; + unsigned long flags; + int rc; + + if (count < (long)sizeof(patch)) { + snd_printk("patch record too small %ld\n", count); + return -EINVAL; + } + if (copy_from_user(&patch, data, sizeof(patch))) + return -EFAULT; + + count -= sizeof(patch); + data += sizeof(patch); + + if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { + snd_printk("'The wrong kind of patch' %x\n", patch.key); + return -EINVAL; + } + if (count < patch.len) { + snd_printk("Patch too short %ld, need %d\n", count, patch.len); + return -EINVAL; + } + if (patch.len < 0) { + snd_printk("poor length %d\n", patch.len); + return -EINVAL; + } + + if (patch.type == SNDRV_SFNT_OPEN_PATCH) { + /* grab sflist to open */ + lock_preset(sflist, 0); + rc = open_patch(sflist, data, count, client); + unlock_preset(sflist); + return rc; + } + + /* check if other client already opened patch */ + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client != client) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + + lock_preset(sflist, 0); + rc = -EINVAL; + switch (patch.type) { + case SNDRV_SFNT_LOAD_INFO: + rc = load_info(sflist, data, count); + break; + case SNDRV_SFNT_LOAD_DATA: + rc = load_data(sflist, data, count); + break; + case SNDRV_SFNT_CLOSE_PATCH: + rc = close_patch(sflist); + break; + case SNDRV_SFNT_REPLACE_DATA: + /*rc = replace_data(&patch, data, count);*/ + break; + case SNDRV_SFNT_MAP_PRESET: + rc = load_map(sflist, data, count); + break; + case SNDRV_SFNT_PROBE_DATA: + rc = probe_data(sflist, patch.optarg); + break; + case SNDRV_SFNT_REMOVE_INFO: + /* patch must be opened */ + if (sflist->currsf) { + snd_printk("soundfont: remove_info: patch not opened\n"); + rc = -EINVAL; + } else { + int bank, instr; + bank = ((unsigned short)patch.optarg >> 8) & 0xff; + instr = (unsigned short)patch.optarg & 0xff; + if (! remove_info(sflist, sflist->currsf, bank, instr)) + rc = -EINVAL; + else + rc = 0; + } + break; + } + unlock_preset(sflist); + + return rc; +} + + +/* check if specified type is special font (GUS or preset-alias) */ +static inline int +is_special_type(int type) +{ + type &= 0x0f; + return (type == SNDRV_SFNT_PAT_TYPE_GUS || + type == SNDRV_SFNT_PAT_TYPE_MAP); +} + + +/* open patch; create sf list */ +static int +open_patch(snd_sf_list_t *sflist, const char *data, int count, int client) +{ + soundfont_open_parm_t parm; + snd_soundfont_t *sf; + unsigned long flags; + + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client >= 0 || sflist->currsf) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + + if (copy_from_user(&parm, data, sizeof(parm))) + return -EFAULT; + + if (is_special_type(parm.type)) { + parm.type |= SNDRV_SFNT_PAT_SHARED; + sf = newsf(sflist, parm.type, NULL); + } else + sf = newsf(sflist, parm.type, parm.name); + if (sf == NULL) { + return -ENOMEM; + } + + sflist->open_client = client; + sflist->currsf = sf; + + return 0; +} + +/* + * Allocate a new soundfont structure. + */ +static snd_soundfont_t * +newsf(snd_sf_list_t *sflist, int type, char *name) +{ + snd_soundfont_t *sf; + + /* check the shared fonts */ + if (type & SNDRV_SFNT_PAT_SHARED) { + for (sf = sflist->fonts; sf; sf = sf->next) { + if (is_identical_font(sf, type, name)) { + return sf; + } + } + } + + /* not found -- create a new one */ + sf = (snd_soundfont_t*)snd_kcalloc(sizeof(*sf), GFP_KERNEL); + if (sf == NULL) + return NULL; + sf->id = sflist->fonts_size; + sflist->fonts_size++; + + /* prepend this record */ + sf->next = sflist->fonts; + sflist->fonts = sf; + + sf->type = type; + sf->zones = NULL; + sf->samples = NULL; + if (name) + memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); + + return sf; +} + +/* check if the given name matches to the existing list */ +static int +is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name) +{ + return ((sf->type & SNDRV_SFNT_PAT_SHARED) && + (sf->type & 0x0f) == (type & 0x0f) && + (name == NULL || + memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0)); +} + +/* + * Close the current patch. + */ +static int +close_patch(snd_sf_list_t *sflist) +{ + sflist->currsf = NULL; + sflist->open_client = -1; + + rebuild_presets(sflist); + + return 0; + +} + +/* probe sample in the current list -- nothing to be loaded */ +static int +probe_data(snd_sf_list_t *sflist, int sample_id) +{ + /* patch must be opened */ + if (sflist->currsf) { + /* search the specified sample by optarg */ + if (find_sample(sflist->currsf, sample_id)) + return 0; + } + return -EINVAL; +} + +/* + * increment zone counter + */ +static void +set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp) +{ + zp->counter = sflist->zone_counter++; + if (sf->type & SNDRV_SFNT_PAT_LOCKED) + sflist->zone_locked = sflist->zone_counter; +} + +/* + * allocate a new zone record + */ +static snd_sf_zone_t * +sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) +{ + snd_sf_zone_t *zp; + + if ((zp = snd_kcalloc(sizeof(*zp), GFP_KERNEL)) == NULL) + return NULL; + zp->next = sf->zones; + sf->zones = zp; + + init_voice_info(&zp->v); + + set_zone_counter(sflist, sf, zp); + return zp; +} + + +/* + * increment sample couter + */ +static void +set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) +{ + sp->counter = sflist->sample_counter++; + if (sf->type & SNDRV_SFNT_PAT_LOCKED) + sflist->sample_locked = sflist->sample_counter; +} + +/* + * allocate a new sample list record + */ +static snd_sf_sample_t * +sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) +{ + snd_sf_sample_t *sp; + + if ((sp = snd_kcalloc(sizeof(*sp), GFP_KERNEL)) == NULL) + return NULL; + + sp->next = sf->samples; + sf->samples = sp; + + set_sample_counter(sflist, sf, sp); + return sp; +} + +/* + * delete sample list -- this is an exceptional job. + * only the last allocated sample can be deleted. + */ +static void +sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) +{ + /* only last sample is accepted */ + if (sp == sf->samples) { + sf->samples = sp->next; + kfree(sp); + } +} + + +/* load voice map */ +static int +load_map(snd_sf_list_t *sflist, const void *data, int count) +{ + snd_sf_zone_t *zp, *prevp; + snd_soundfont_t *sf; + soundfont_voice_map_t map; + + /* get the link info */ + if (count < (int)sizeof(map)) + return -EINVAL; + if (copy_from_user(&map, data, sizeof(map))) + return -EFAULT; + + if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) + return -EINVAL; + + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); + if (sf == NULL) + return -ENOMEM; + + prevp = NULL; + for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { + if (zp->mapped && + zp->instr == map.map_instr && + zp->bank == map.map_bank && + zp->v.low == map.map_key && + zp->v.start == map.src_instr && + zp->v.end == map.src_bank && + zp->v.fixkey == map.src_key) { + /* the same mapping is already present */ + /* relink this record to the link head */ + if (prevp) { + prevp->next = zp->next; + zp->next = sf->zones; + sf->zones = zp; + } + /* update the counter */ + set_zone_counter(sflist, sf, zp); + return 0; + } + } + + /* create a new zone */ + if ((zp = sf_zone_new(sflist, sf)) == NULL) + return -ENOMEM; + + zp->bank = map.map_bank; + zp->instr = map.map_instr; + zp->mapped = 1; + if (map.map_key >= 0) { + zp->v.low = map.map_key; + zp->v.high = map.map_key; + } + zp->v.start = map.src_instr; + zp->v.end = map.src_bank; + zp->v.fixkey = map.src_key; + zp->v.sf_id = sf->id; + + add_preset(sflist, zp); + + return 0; +} + + +/* remove the present instrument layers */ +static int +remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr) +{ + snd_sf_zone_t *prev, *next, *p; + int removed = 0; + + prev = NULL; + for (p = sf->zones; p; p = next) { + next = p->next; + if (! p->mapped && + p->bank == bank && p->instr == instr) { + /* remove this layer */ + if (prev) + prev->next = next; + else + sf->zones = next; + removed++; + kfree(p); + } else + prev = p; + } + if (removed) + rebuild_presets(sflist); + return removed; +} + + +/* + * Read an info record from the user buffer and save it on the current + * open soundfont. + */ +static int +load_info(snd_sf_list_t *sflist, const void *data, long count) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *zone; + soundfont_voice_rec_hdr_t hdr; + int i; + + /* patch must be opened */ + if ((sf = sflist->currsf) == NULL) + return -EINVAL; + + if (is_special_type(sf->type)) + return -EINVAL; + + if (count < (long)sizeof(hdr)) { + printk("Soundfont error: invalid patch zone length\n"); + return -EINVAL; + } + if (copy_from_user((char*)&hdr, data, sizeof(hdr))) + return -EFAULT; + + data += sizeof(hdr); + count -= sizeof(hdr); + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices); + return -EINVAL; + } + + if (count < (long)sizeof(soundfont_voice_info_t)*hdr.nvoices) { + printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return -EINVAL; + } + + switch (hdr.write_mode) { + case SNDRV_SFNT_WR_EXCLUSIVE: + /* exclusive mode - if the instrument already exists, + return error */ + for (zone = sf->zones; zone; zone = zone->next) { + if (!zone->mapped && + zone->bank == hdr.bank && + zone->instr == hdr.instr) + return -EINVAL; + } + break; + case SNDRV_SFNT_WR_REPLACE: + /* replace mode - remove the instrument if it already exists */ + remove_info(sflist, sf, hdr.bank, hdr.instr); + break; + } + + for (i = 0; i < hdr.nvoices; i++) { + snd_sf_zone_t tmpzone; + + /* copy awe_voice_info parameters */ + if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) { + return -EFAULT; + } + + data += sizeof(tmpzone.v); + count -= sizeof(tmpzone.v); + + tmpzone.bank = hdr.bank; + tmpzone.instr = hdr.instr; + tmpzone.mapped = 0; + tmpzone.v.sf_id = sf->id; + if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM) + init_voice_parm(&tmpzone.v.parm); + + /* create a new zone */ + if ((zone = sf_zone_new(sflist, sf)) == NULL) { + return -ENOMEM; + } + + /* copy the temporary data */ + zone->bank = tmpzone.bank; + zone->instr = tmpzone.instr; + zone->v = tmpzone.v; + + /* look up the sample */ + zone->sample = set_sample(sf, &zone->v); + } + + return 0; +} + + +/* initialize voice_info record */ +static void +init_voice_info(soundfont_voice_info_t *avp) +{ + memset(avp, 0, sizeof(*avp)); + + avp->root = 60; + avp->high = 127; + avp->velhigh = 127; + avp->fixkey = -1; + avp->fixvel = -1; + avp->fixpan = -1; + avp->pan = -1; + avp->amplitude = 127; + avp->scaleTuning = 100; + + init_voice_parm(&avp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +init_voice_parm(soundfont_voice_parm_t *pp) +{ + memset(pp, 0, sizeof(*pp)); + + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + + pp->cutoff = 0xff; +} + +/* search the specified sample */ +static snd_sf_sample_t * +set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp) +{ + snd_sf_sample_t *sample; + + sample = find_sample(sf, avp->sample); + if (sample == NULL) + return NULL; + + /* add in the actual sample offsets: + * The voice_info addresses define only the relative offset + * from sample pointers. Here we calculate the actual DRAM + * offset from sample pointers. + */ + avp->start += sample->v.start; + avp->end += sample->v.end; + avp->loopstart += sample->v.loopstart; + avp->loopend += sample->v.loopend; + + /* copy mode flags */ + avp->sample_mode = sample->v.mode_flags; + + return sample; +} + +/* find the sample pointer with the given id in the soundfont */ +static snd_sf_sample_t * +find_sample(snd_soundfont_t *sf, int sample_id) +{ + snd_sf_sample_t *p; + + if (sf == NULL) + return NULL; + + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample_id) + return p; + } + return NULL; +} + + +/* + * Load sample information, this can include data to be loaded onto + * the soundcard. It can also just be a pointer into soundcard ROM. + * If there is data it will be written to the soundcard via the callback + * routine. + */ +static int +load_data(snd_sf_list_t *sflist, const void *data, long count) +{ + snd_soundfont_t *sf; + soundfont_sample_info_t sample_info; + snd_sf_sample_t *sp; + long off; + + /* patch must be opened */ + if ((sf = sflist->currsf) == NULL) + return -EINVAL; + + if (is_special_type(sf->type)) + return -EINVAL; + + if (copy_from_user(&sample_info, data, sizeof(sample_info))) + return -EFAULT; + + off = sizeof(sample_info); + + if (sample_info.size != (count-off)/2) + return -EINVAL; + + /* Check for dup */ + if (find_sample(sf, sample_info.sample)) { + /* if shared sample, skip this data */ + if (sf->type & SNDRV_SFNT_PAT_SHARED) + return 0; + return -EINVAL; + } + + /* Allocate a new sample structure */ + if ((sp = sf_sample_new(sflist, sf)) == NULL) + return -ENOMEM; + + sp->v = sample_info; + sp->v.sf_id = sf->id; + sp->v.dummy = 0; + sp->v.truesize = sp->v.size; + + /* + * If there is wave data then load it. + */ + if (sp->v.size > 0) { + int rc; + rc = sflist->callback.sample_new + (sflist->callback.private_data, sp, sflist->memhdr, + data + off, count - off); + if (rc < 0) { + sf_sample_delete(sflist, sf, sp); + return rc; + } + sflist->mem_used += sp->v.truesize; + } + + return count; +} + + +/* log2_tbl[i] = log2(i+128) * 0x10000 */ +static int log_tbl[129] = { + 0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa, + 0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed, + 0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08, + 0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019, + 0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a, + 0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382, + 0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404, + 0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2, + 0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9, + 0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188, + 0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89, + 0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07, + 0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c, + 0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f, + 0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8, + 0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d, + 0x80000, +}; + +/* convert from linear to log value + * + * conversion: value = log2(amount / base) * ratio + * + * argument: + * amount = linear value (unsigned, 32bit max) + * offset = base offset (:= log2(base) * 0x10000) + * ratio = division ratio + * + */ +int +snd_sf_linear_to_log(unsigned int amount, int offset, int ratio) +{ + int v; + int s, low, bit; + + if (amount < 2) + return 0; + for (bit = 0; ! (amount & 0x80000000L); bit++) + amount <<= 1; + s = (amount >> 24) & 0x7f; + low = (amount >> 16) & 0xff; + /* linear approxmimation by lower 8 bit */ + v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8; + v -= offset; + v = (v * ratio) >> 16; + v += (24 - bit) * ratio; + return v; +} + +#define OFFSET_MSEC 653117 /* base = 1000 */ +#define OFFSET_ABSCENT 851781 /* base = 8176 */ +#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ + +#define ABSCENT_RATIO 1200 +#define TIMECENT_RATIO 1200 +#define SAMPLERATE_RATIO 4096 + +/* + * mHz to abscent + * conversion: abscent = log2(MHz / 8176) * 1200 + */ +static int +freq_to_note(int mhz) +{ + return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO); +} + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + * + * conversion: offset = log2(Hz / 44100) * 4096 + */ +static int +calc_rate_offset(int hz) +{ + return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); +} + + +/* calculate GUS envelope time */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +/* convert envelope time parameter to soundfont parameters */ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, +707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, +361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, +180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, +90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, +45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, +22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, +11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, +2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, +1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, +691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, +345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, +172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, +86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, +43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, +}; + +/* delay time = 0x8000 - msec/92 */ +int +snd_sf_calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val >= 126) val = 126; + return val; +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} + +/* attack time: search from time table */ +int +snd_sf_calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +int +snd_sf_calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +int snd_sf_vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + + +#define calc_gus_sustain(val) (0x7f - snd_sf_vol_table[(val)/2]) +#define calc_gus_attenuation(val) snd_sf_vol_table[(val)/2] + +/* load GUS patch */ +static int +load_guspatch(snd_sf_list_t *sflist, const char *data, long count, int client) +{ + struct patch_info patch; + snd_soundfont_t *sf; + snd_sf_zone_t *zone; + snd_sf_sample_t *smp; + int note, sample_id; + int rc; + + if (count < (long)sizeof(patch)) { + snd_printk("patch record too small %ld\n", count); + return -EINVAL; + } + if (copy_from_user(&patch, data, sizeof(patch))) + return -EFAULT; + + count -= sizeof(patch); + data += sizeof(patch); + + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); + if (sf == NULL) + return -ENOMEM; + if ((smp = sf_sample_new(sflist, sf)) == NULL) + return -ENOMEM; + sample_id = sflist->sample_counter; + smp->v.sample = sample_id; + smp->v.start = 0; + smp->v.end = patch.len; + smp->v.loopstart = patch.loop_start; + smp->v.loopend = patch.loop_end; + smp->v.size = patch.len; + + /* set up mode flags */ + smp->v.mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED; + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP; + + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->v.size /= 2; + smp->v.end /= 2; + smp->v.loopstart /= 2; + smp->v.loopend /= 2; + } + /*smp->v.loopend++;*/ + + smp->v.dummy = 0; + smp->v.truesize = 0; + smp->v.sf_id = sf->id; + + /* set up voice info */ + if ((zone = sf_zone_new(sflist, sf)) == NULL) { + sf_sample_delete(sflist, sf, smp); + return -ENOMEM; + } + + /* + * load wave data + */ + if (sflist->callback.sample_new) { + rc = sflist->callback.sample_new + (sflist->callback.private_data, smp, sflist->memhdr, data, count); + if (rc < 0) { + sf_sample_delete(sflist, sf, smp); + return rc; + } + /* memory offset is updated after */ + } + + /* update the memory offset here */ + sflist->mem_used += smp->v.truesize; + + zone->v.sample = sample_id; /* the last sample */ + zone->v.rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + zone->v.root = note / 100; + zone->v.tune = -(note % 100); + zone->v.low = (freq_to_note(patch.low_note) + 99) / 100; + zone->v.high = freq_to_note(patch.high_note) / 100; + /* panning position; -128 - 127 => 0-127 */ + zone->v.pan = (patch.panning + 128) / 2; +#if 0 + snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n", + (int)patch.base_freq, zone->v.rate_offset, + zone->v.root, zone->v.tune, zone->v.low, zone->v.high); +#endif + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + zone->v.parm.volatkhld = + (snd_sf_calc_parm_hold(hold) << 8) | + snd_sf_calc_parm_attack(attack); + zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + snd_sf_calc_parm_decay(decay); + zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release); + zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); +#if 0 + snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n", + zone->v.parm.volatkhld, + zone->v.parm.voldcysus, + zone->v.parm.volrelease, + zone->v.attenuation); +#endif + } + + /* fast release */ + if (patch.mode & WAVE_FAST_RELEASE) { + zone->v.parm.volrelease = 0x807f; + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT)) + zone->v.mode = SNDRV_SFNT_MODE_LOOPING; + else + zone->v.mode = 0; + + /* append to the tail of the list */ + /*zone->bank = ctrls[AWE_MD_GUS_BANK];*/ + zone->bank = 0; + zone->instr = patch.instr_no; + zone->mapped = 0; + zone->v.sf_id = sf->id; + + zone->sample = set_sample(sf, &zone->v); + + /* rebuild preset now */ + add_preset(sflist, zone); + + return 0; +} + +/* load GUS patch */ +int +snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data, + long count, int client) +{ + int rc; + lock_preset(sflist, 0); + rc = load_guspatch(sflist, data, count, client); + unlock_preset(sflist); + return rc; +} + + +/* + * Rebuild the preset table. This is like a hash table in that it allows + * quick access to the zone information. For each preset there are zone + * structures linked by next_instr and by next_zone. Former is the whole + * link for this preset, and latter is the link for zone (i.e. instrument/ + * bank/key combination). + */ +static void +rebuild_presets(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *cur; + + /* clear preset table */ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + /* search all fonts and insert each font */ + for (sf = sflist->fonts; sf; sf = sf->next) { + for (cur = sf->zones; cur; cur = cur->next) { + if (! cur->mapped && cur->sample == NULL) { + /* try again to search the corresponding sample */ + cur->sample = set_sample(sf, &cur->v); + if (cur->sample == NULL) + continue; + } + + add_preset(sflist, cur); + } + } +} + + +/* + * add the given zone to preset table + */ +static void +add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur) +{ + snd_sf_zone_t *zone; + int index; + + zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low); + if (zone && zone->v.sf_id != cur->v.sf_id) { + /* different instrument was already defined */ + snd_sf_zone_t *p; + /* compare the allocated time */ + for (p = zone; p; p = p->next_zone) { + if (p->counter > cur->counter) + /* the current is older.. skipped */ + return; + } + /* remove old zones */ + delete_preset(sflist, zone); + zone = NULL; /* do not forget to clear this! */ + } + + /* prepend this zone */ + if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0) + return; + cur->next_zone = zone; /* zone link */ + cur->next_instr = sflist->presets[index]; /* preset table link */ + sflist->presets[index] = cur; +} + +/* + * delete the given zones from preset_table + */ +static void +delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp) +{ + int index; + snd_sf_zone_t *p; + + if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0) + return; + for (p = sflist->presets[index]; p; p = p->next_instr) { + while (p->next_instr == zp) { + p->next_instr = zp->next_instr; + zp = zp->next_zone; + if (zp == NULL) + return; + } + } +} + + +/* + * Search matching zones from preset table. + * The note can be rewritten by preset mapping (alias). + * The found zones are stored on 'table' array. max_layers defines + * the maximum number of elements in this array. + * This function returns the number of found zones. 0 if not found. + */ +int +snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, + int preset, int bank, + int def_preset, int def_bank, + snd_sf_zone_t **table, int max_layers) +{ + int nvoices; + + if (lock_preset(sflist, 1)) + return 0; + + nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0); + if (! nvoices) { + if (preset != def_preset || bank != def_bank) + nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0); + } + unlock_preset(sflist); + + return nvoices; +} + + +/* + * search the first matching zone + */ +static snd_sf_zone_t * +search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key) +{ + int index; + snd_sf_zone_t *zp; + + if ((index = get_index(bank, preset, key)) < 0) + return NULL; + for (zp = sflist->presets[index]; zp; zp = zp->next_instr) { + if (zp->instr == preset && zp->bank == bank) + return zp; + } + return NULL; +} + + +/* + * search matching zones from sflist. can be called recursively. + */ +static int +search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level) +{ + snd_sf_zone_t *zp; + int nvoices; + + zp = search_first_zone(sflist, bank, preset, *notep); + nvoices = 0; + for (; zp; zp = zp->next_zone) { + if (*notep >= zp->v.low && *notep <= zp->v.high && + vel >= zp->v.vellow && vel <= zp->v.velhigh) { + if (zp->mapped) { + /* search preset mapping (aliasing) */ + int key = zp->v.fixkey; + preset = zp->v.start; + bank = zp->v.end; + + if (level > 5) /* too deep alias level */ + return 0; + if (key < 0) + key = *notep; + nvoices = search_zones(sflist, &key, vel, + preset, bank, table, + max_layers, level + 1); + if (nvoices > 0) + *notep = key; + break; + } + table[nvoices++] = zp; + if (nvoices >= max_layers) + break; + } + } + + return nvoices; +} + + +/* calculate the index of preset table: + * drums are mapped from 128 to 255 according to its note key. + * other instruments are mapped from 0 to 127. + * if the index is out of range, return -1. + */ +static int +get_index(int bank, int instr, int key) +{ + int index; + if (SF_IS_DRUM_BANK(bank)) + index = key + SF_MAX_INSTRUMENTS; + else + index = instr; + index = index % SF_MAX_PRESETS; + if (index < 0) + return -1; + return index; +} + +/* + * Initialise the sflist structure. + */ +static void +snd_sf_init(snd_sf_list_t *sflist) +{ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + sflist->mem_used = 0; + sflist->currsf = NULL; + sflist->open_client = -1; + sflist->fonts = NULL; + sflist->fonts_size = 0; + sflist->zone_counter = 0; + sflist->sample_counter = 0; + sflist->zone_locked = 0; + sflist->sample_locked = 0; +} + +/* + * Release all list records + */ +static void +snd_sf_clear(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf, *nextsf; + snd_sf_zone_t *zp, *nextzp; + snd_sf_sample_t *sp, *nextsp; + + for (sf = sflist->fonts; sf; sf = nextsf) { + nextsf = sf->next; + for (zp = sf->zones; zp; zp = nextzp) { + nextzp = zp->next; + kfree(zp); + } + for (sp = sf->samples; sp; sp = nextsp) { + nextsp = sp->next; + if (sflist->callback.sample_free) + sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); + kfree(sp); + } + kfree(sf); + } + + snd_sf_init(sflist); +} + + +/* + * Create a new sflist structure + */ +snd_sf_list_t * +snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr) +{ + snd_sf_list_t *sflist; + + if ((sflist = snd_kcalloc(sizeof(snd_sf_list_t), GFP_KERNEL)) == NULL) + return NULL; + + init_MUTEX(&sflist->presets_mutex); + spin_lock_init(&sflist->lock); + sflist->sf_locked = 0; + sflist->memhdr = hdr; + + if (callback) + sflist->callback = *callback; + + snd_sf_init(sflist); + return sflist; +} + + +/* + * Free everything allocated off the sflist structure. + */ +void +snd_sf_free(snd_sf_list_t *sflist) +{ + if (sflist == NULL) + return; + + lock_preset(sflist, 0); + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + snd_sf_clear(sflist); + unlock_preset(sflist); + + kfree(sflist); +} + +/* + * Remove all samples + * The soundcard should be silet before calling this function. + */ +int +snd_soundfont_remove_samples(snd_sf_list_t *sflist) +{ + lock_preset(sflist, 0); + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + snd_sf_clear(sflist); + unlock_preset(sflist); + + return 0; +} + +/* + * Remove unlocked samples. + * The soundcard should be silet before calling this function. + */ +int +snd_soundfont_remove_unlocked(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *zp, *nextzp; + snd_sf_sample_t *sp, *nextsp; + + if (lock_preset(sflist, 1)) + return -EBUSY; + + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + + /* to be sure */ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + for (sf = sflist->fonts; sf; sf = sf->next) { + for (zp = sf->zones; zp; zp = nextzp) { + if (zp->counter < sflist->zone_locked) + break; + nextzp = zp->next; + sf->zones = nextzp; + kfree(zp); + } + + for (sp = sf->samples; sp; sp = nextsp) { + if (sp->counter < sflist->sample_locked) + break; + nextsp = sp->next; + sf->samples = nextsp; + sflist->mem_used -= sp->v.truesize; + if (sflist->callback.sample_free) + sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); + kfree(sp); + } + } + + sflist->zone_counter = sflist->zone_locked; + sflist->sample_counter = sflist->sample_locked; + + rebuild_presets(sflist); + + unlock_preset(sflist); + return 0; +} + +/* + * Return the used memory size (in words) + */ +int +snd_soundfont_mem_used(snd_sf_list_t *sflist) +{ + return sflist->mem_used; +} diff -urN linux-2.4.21-rc1.orig/sound/synth/util_mem.c linux/sound/synth/util_mem.c --- linux-2.4.21-rc1.orig/sound/synth/util_mem.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/synth/util_mem.c 2002-02-14 10:40:34.000000000 -0700 @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Generic memory management routines for soundcard memory allocation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); +MODULE_LICENSE("GPL"); + +#define get_memblk(p) list_entry(p, snd_util_memblk_t, list) + +/* + * create a new memory manager + */ +snd_util_memhdr_t * +snd_util_memhdr_new(int memsize) +{ + snd_util_memhdr_t *hdr; + + hdr = snd_kcalloc(sizeof(*hdr), GFP_KERNEL); + if (hdr == NULL) + return NULL; + hdr->size = memsize; + init_MUTEX(&hdr->block_mutex); + INIT_LIST_HEAD(&hdr->block); + + return hdr; +} + +/* + * free a memory manager + */ +void snd_util_memhdr_free(snd_util_memhdr_t *hdr) +{ + struct list_head *p; + + snd_assert(hdr != NULL, return); + /* release all blocks */ + while ((p = hdr->block.next) != &hdr->block) { + list_del(p); + kfree(get_memblk(p)); + } + kfree(hdr); +} + +/* + * allocate a memory block (without mutex) + */ +snd_util_memblk_t * +__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk; + snd_util_unit_t units, prev_offset; + struct list_head *p; + + snd_assert(hdr != NULL, return NULL); + snd_assert(size > 0, return NULL); + + /* word alignment */ + units = size; + if (units & 1) + units++; + if (units > hdr->size) + return NULL; + + /* look for empty block */ + prev_offset = 0; + list_for_each(p, &hdr->block) { + blk = get_memblk(p); + if (blk->offset - prev_offset >= units) + goto __found; + prev_offset = blk->offset + blk->size; + } + if (hdr->size - prev_offset < units) + return NULL; + +__found: + return __snd_util_memblk_new(hdr, units, p->prev); +} + + +/* + * create a new memory block with the given size + * the block is linked next to prev + */ +snd_util_memblk_t * +__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, + struct list_head *prev) +{ + snd_util_memblk_t *blk; + + blk = kmalloc(sizeof(snd_util_memblk_t) + hdr->block_extra_size, GFP_KERNEL); + if (blk == NULL) + return NULL; + + if (! prev || prev == &hdr->block) + blk->offset = 0; + else { + snd_util_memblk_t *p = get_memblk(prev); + blk->offset = p->offset + p->size; + } + blk->size = units; + list_add(&blk->list, prev); + hdr->nblocks++; + hdr->used += units; + return blk; +} + + +/* + * allocate a memory block (with mutex) + */ +snd_util_memblk_t * +snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk; + down(&hdr->block_mutex); + blk = __snd_util_mem_alloc(hdr, size); + up(&hdr->block_mutex); + return blk; +} + + +/* + * remove the block from linked-list and free resource + * (without mutex) + */ +void +__snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) +{ + list_del(&blk->list); + hdr->nblocks--; + hdr->used -= blk->size; + kfree(blk); +} + +/* + * free a memory block (with mutex) + */ +int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) +{ + snd_assert(hdr && blk, return -EINVAL); + + down(&hdr->block_mutex); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + +/* + * return available memory size + */ +int snd_util_mem_avail(snd_util_memhdr_t *hdr) +{ + unsigned int size; + down(&hdr->block_mutex); + size = hdr->size - hdr->used; + up(&hdr->block_mutex); + return size; +} + + +EXPORT_SYMBOL(snd_util_memhdr_new); +EXPORT_SYMBOL(snd_util_memhdr_free); +EXPORT_SYMBOL(snd_util_mem_alloc); +EXPORT_SYMBOL(snd_util_mem_free); +EXPORT_SYMBOL(snd_util_mem_avail); +EXPORT_SYMBOL(__snd_util_mem_alloc); +EXPORT_SYMBOL(__snd_util_mem_free); +EXPORT_SYMBOL(__snd_util_memblk_new); + +/* + * INIT part + */ + +static int __init alsa_util_mem_init(void) +{ + return 0; +} + +static void __exit alsa_util_mem_exit(void) +{ +} + +module_init(alsa_util_mem_init) +module_exit(alsa_util_mem_exit) diff -urN linux-2.4.21-rc1.orig/sound/usb/Config.in linux/sound/usb/Config.in --- linux-2.4.21-rc1.orig/sound/usb/Config.in 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/Config.in 2003-04-29 05:02:23.000000000 -0600 @@ -0,0 +1,9 @@ +# ALSA USB drivers + +mainmenu_option next_comment +comment 'ALSA USB devices' + +dep_tristate 'USB Audio driver' CONFIG_SND_USB_AUDIO $CONFIG_SND +dep_tristate 'USB MIDI driver' CONFIG_SND_USB_MIDI $CONFIG_SND $CONFIG_SND_SEQUENCER + +endmenu diff -urN linux-2.4.21-rc1.orig/sound/usb/Makefile linux/sound/usb/Makefile --- linux-2.4.21-rc1.orig/sound/usb/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/Makefile 2003-04-29 05:09:35.000000000 -0600 @@ -0,0 +1,17 @@ +# +# Makefile for ALSA +# + +O_TARGET := _usbaudio.o + +list_multi := snd-usb-audio.o + +snd-usb-audio-objs := usbaudio.o usbmixer.o usbmidi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o + +include $(TOPDIR)/Rules.make + +snd-usb-audio.o: $(snd-usb-audio-objs) + $(LD) -r -o $@ $(snd-usb-audio-objs) diff -urN linux-2.4.21-rc1.orig/sound/usb/usbaudio.c linux/sound/usb/usbaudio.c --- linux-2.4.21-rc1.orig/sound/usb/usbaudio.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/usbaudio.c 2003-03-12 05:55:24.000000000 -0700 @@ -0,0 +1,2664 @@ +/* + * (Tentative) USB Audio Driver for ALSA + * + * Main and PCM part + * + * Copyright (c) 2002 by Takashi Iwai + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include "usbaudio.h" + + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("USB Audio"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Generic,USB Audio}}"); + + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */ +static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */ + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for the USB audio adapter."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable USB audio adapter."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(vid, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); +MODULE_PARM_SYNTAX(vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); +MODULE_PARM(pid, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); +MODULE_PARM_SYNTAX(pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); + + +/* + * for using ASYNC unlink mode, define the following. + * this will make the driver quicker response for request to STOP-trigger, + * but it may cause oops by some unknown reason (bug of usb driver?), + * so turning off might be sure. + */ +/* #define SND_USE_ASYNC_UNLINK */ + +#ifdef SND_USB_ASYNC_UNLINK +#define UNLINK_FLAGS URB_ASYNC_UNLINK +#else +#define UNLINK_FLAGS 0 +#endif + + +/* + * debug the h/w constraints + */ +/* #define HW_CONST_DEBUG */ + + +/* + * + */ + +#define NRPACKS 4 /* 4ms per urb */ +#define MAX_URBS 5 /* max. 20ms long packets */ +#define SYNC_URBS 2 /* always two urbs for sync */ +#define MIN_PACKS_URB 1 /* minimum 1 packet per urb */ + +typedef struct snd_usb_substream snd_usb_substream_t; +typedef struct snd_usb_stream snd_usb_stream_t; +typedef struct snd_urb_ctx snd_urb_ctx_t; + +struct audioformat { + struct list_head list; + snd_pcm_format_t format; /* format type */ + unsigned int channels; /* # channels */ + int iface; /* interface number */ + unsigned char altsetting; /* corresponding alternate setting */ + unsigned char altset_idx; /* array index of altenate setting */ + unsigned char attributes; /* corresponding attributes of cs endpoint */ + unsigned char endpoint; /* endpoint */ + unsigned char ep_attr; /* endpoint attributes */ + unsigned int rates; /* rate bitmasks */ + unsigned int rate_min, rate_max; /* min/max rates */ + unsigned int nr_rates; /* number of rate table entries */ + unsigned int *rate_table; /* rate table */ +}; + +struct snd_urb_ctx { + struct urb *urb; + snd_usb_substream_t *subs; + int index; /* index for urb array */ + int packets; /* number of packets per urb */ + int transfer; /* transferred size */ + char *buf; /* buffer for capture */ +}; + +struct snd_urb_ops { + int (*prepare)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); + int (*retire)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); + int (*prepare_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); + int (*retire_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); +}; + +struct snd_usb_substream { + snd_usb_stream_t *stream; + struct usb_device *dev; + snd_pcm_substream_t *pcm_substream; + int direction; /* playback or capture */ + int interface; /* current interface */ + int endpoint; /* assigned endpoint */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data i/o pipe */ + unsigned int syncpipe; /* 1 - async out or adaptive in */ + unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ + unsigned int phase; /* phase accumulator */ + unsigned int maxpacksize; /* max packet size in bytes */ + unsigned int maxframesize; /* max packet size in frames */ + unsigned int curpacksize; /* current packet size in bytes (for capture) */ + unsigned int curframesize; /* current packet size in frames (for capture) */ + unsigned int fill_max: 1; /* fill max packet size always */ + + unsigned int running: 1; /* running status */ + + unsigned int hwptr; /* free frame position in the buffer (only for playback) */ + unsigned int hwptr_done; /* processed frame position in the buffer */ + unsigned int transfer_sched; /* scheduled frames since last period (for playback) */ + unsigned int transfer_done; /* processed frames since last period update */ + unsigned long active_mask; /* bitmask of active urbs */ + unsigned long unlink_mask; /* bitmask of unlinked urbs */ + + unsigned int nurbs; /* # urbs */ + snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */ + snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */ + char syncbuf[SYNC_URBS * NRPACKS * 3]; /* sync buffer; it's so small - let's get static */ + char *tmpbuf; /* temporary buffer for playback */ + + u64 formats; /* format bitmasks (all or'ed) */ + unsigned int num_formats; /* number of supported audio formats (list) */ + struct list_head fmt_list; /* format list */ + spinlock_t lock; + + struct snd_urb_ops ops; /* callbacks (must be filled at init) */ +}; + + +struct snd_usb_stream { + snd_usb_audio_t *chip; + snd_pcm_t *pcm; + int pcm_index; + snd_usb_substream_t substream[2]; + struct list_head list; +}; + +#define chip_t snd_usb_stream_t + + +/* + * we keep the snd_usb_audio_t instances by ourselves for merging + * the all interfaces on the same card as one sound device. + */ + +static DECLARE_MUTEX(register_mutex); +static snd_usb_audio_t *usb_chip[SNDRV_CARDS]; + + +/* + * convert a sampling rate into USB format (fs/1000 in Q10.14) + * this will overflow at approx 2MSPS + */ +inline static unsigned get_usb_rate(unsigned int rate) +{ + return ((rate << 11) + 62) / 125; +} + + +/* + * prepare urb for capture sync pipe + * + * fill the length and offset of each urb descriptor. + * the fixed 10.14 frequency is passed through the pipe. + */ +static int prepare_capture_sync_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + unsigned char *cp = urb->transfer_buffer; + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + int i, offs; + + urb->number_of_packets = ctx->packets; + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + for (i = offs = 0; i < urb->number_of_packets; i++, offs += 3, cp += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + cp[0] = subs->freqn; + cp[1] = subs->freqn >> 8; + cp[2] = subs->freqn >> 16; + } + urb->interval = 1; + return 0; +} + +/* + * process after capture sync complete + * - nothing to do + */ +static int retire_capture_sync_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + return 0; +} + +/* + * prepare urb for capture data pipe + * + * fill the offset and length of each descriptor. + * + * we use a temporary buffer to write the captured data. + * since the length of written data is determined by host, we cannot + * write onto the pcm buffer directly... the data is thus copied + * later at complete callback to the global buffer. + */ +static int prepare_capture_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + int i, offs; + unsigned long flags; + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + + offs = 0; + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->number_of_packets = 0; + spin_lock_irqsave(&subs->lock, flags); + for (i = 0; i < ctx->packets; i++) { + urb->iso_frame_desc[i].offset = offs; + urb->iso_frame_desc[i].length = subs->curpacksize; + offs += subs->curpacksize; + urb->number_of_packets++; + subs->transfer_sched += subs->curframesize; + if (subs->transfer_sched >= runtime->period_size) { + subs->transfer_sched -= runtime->period_size; + break; + } + } + spin_unlock_irqrestore(&subs->lock, flags); + urb->transfer_buffer = ctx->buf; + urb->transfer_buffer_length = offs; + urb->interval = 1; +#if 0 // for check + if (! urb->bandwidth) { + int bustime; + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) + return bustime; + printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets); + usb_claim_bandwidth(urb->dev, urb, bustime, 1); + } +#endif // for check + return 0; +} + +/* + * process after capture complete + * + * copy the data from each desctiptor to the pcm buffer, and + * update the current position. + */ +static int retire_capture_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + unsigned long flags; + unsigned char *cp; + int i; + unsigned int stride, len, oldptr; + + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status) { + snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status); + // continue; + } + len = urb->iso_frame_desc[i].actual_length / stride; + if (! len) + continue; + /* update the current pointer */ + spin_lock_irqsave(&subs->lock, flags); + oldptr = subs->hwptr_done; + subs->hwptr_done += len; + if (subs->hwptr_done >= runtime->buffer_size) + subs->hwptr_done -= runtime->buffer_size; + subs->transfer_done += len; + spin_unlock_irqrestore(&subs->lock, flags); + /* copy a data chunk */ + if (oldptr + len > runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - oldptr; + unsigned int blen = cnt * stride; + memcpy(runtime->dma_area + oldptr * stride, cp, blen); + memcpy(runtime->dma_area, cp + blen, len * stride - blen); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); + } + /* update the pointer, call callback if necessary */ + spin_lock_irqsave(&subs->lock, flags); + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + spin_unlock_irqrestore(&subs->lock, flags); + snd_pcm_period_elapsed(subs->pcm_substream); + } else + spin_unlock_irqrestore(&subs->lock, flags); + } + return 0; +} + + +/* + * prepare urb for playback sync pipe + * + * set up the offset and length to receive the current frequency. + */ + +static int prepare_playback_sync_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + int i, offs; + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + + urb->number_of_packets = ctx->packets; + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + for (i = offs = 0; i < urb->number_of_packets; i++, offs += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + } + return 0; +} + +/* + * process after playback sync complete + * + * retrieve the current 10.14 frequency from pipe, and set it. + * the value is referred in prepare_playback_urb(). + */ +static int retire_playback_sync_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + int i; + unsigned int f, found; + unsigned char *cp = urb->transfer_buffer; + unsigned long flags; + + found = 0; + for (i = 0; i < urb->number_of_packets; i++, cp += 3) { + if (urb->iso_frame_desc[i].status || + urb->iso_frame_desc[i].actual_length < 3) + continue; + f = combine_triple(cp); + if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) { + snd_printd(KERN_WARNING "requested frequency %u (nominal %u) out of range!\n", f, subs->freqn); + continue; + } + found = f; + } + if (found) { + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = found; + spin_unlock_irqrestore(&subs->lock, flags); + } + + return 0; +} + +/* + * prepare urb for playback data pipe + * + * we copy the data directly from the pcm buffer. + * the current position to be copied is held in hwptr field. + * since a urb can handle only a single linear buffer, if the total + * transferred area overflows the buffer boundary, we cannot send + * it directly from the buffer. thus the data is once copied to + * a temporary buffer and urb points to that. + */ +static int prepare_playback_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + int i, stride, offs; + unsigned int counts; + unsigned long flags; + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + + stride = runtime->frame_bits >> 3; + + offs = 0; + urb->dev = ctx->subs->dev; /* we need to set this at each time */ + urb->number_of_packets = 0; + spin_lock_irqsave(&subs->lock, flags); + for (i = 0; i < ctx->packets; i++) { + /* calculate the size of a packet */ + if (subs->fill_max) + counts = subs->maxframesize; /* fixed */ + else { + subs->phase = (subs->phase & 0x3fff) + subs->freqm; + counts = subs->phase >> 14; + if (counts > subs->maxframesize) + counts = subs->maxframesize; + } + /* set up descriptor */ + urb->iso_frame_desc[i].offset = offs * stride; + urb->iso_frame_desc[i].length = counts * stride; + offs += counts; + urb->number_of_packets++; + subs->transfer_sched += counts; + if (subs->transfer_sched >= runtime->period_size) { + subs->transfer_sched -= runtime->period_size; + break; + } + } + if (subs->hwptr + offs > runtime->buffer_size) { + /* err, the transferred area goes over buffer boundary. + * copy the data to the temp buffer. + */ + int len; + len = runtime->buffer_size - subs->hwptr; + urb->transfer_buffer = subs->tmpbuf; + memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride); + memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride); + subs->hwptr += offs; + subs->hwptr -= runtime->buffer_size; + } else { + /* set the buffer pointer */ + urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride; + subs->hwptr += offs; + } + spin_unlock_irqrestore(&subs->lock, flags); + urb->transfer_buffer_length = offs * stride; + ctx->transfer = offs; + + return 0; +} + +/* + * process after playback data complete + * + * update the current position and call callback if a period is processed. + */ +static int retire_playback_urb(snd_usb_substream_t *subs, + snd_pcm_runtime_t *runtime, + struct urb *urb) +{ + unsigned long flags; + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + + spin_lock_irqsave(&subs->lock, flags); + subs->transfer_done += ctx->transfer; + subs->hwptr_done += ctx->transfer; + ctx->transfer = 0; + if (subs->hwptr_done >= runtime->buffer_size) + subs->hwptr_done -= runtime->buffer_size; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + spin_unlock_irqrestore(&subs->lock, flags); + snd_pcm_period_elapsed(subs->pcm_substream); + } else + spin_unlock_irqrestore(&subs->lock, flags); + return 0; +} + + +/* + */ +static struct snd_urb_ops audio_urb_ops[2] = { + { + .prepare = prepare_playback_urb, + .retire = retire_playback_urb, + .prepare_sync = prepare_playback_sync_urb, + .retire_sync = retire_playback_sync_urb, + }, + { + .prepare = prepare_capture_urb, + .retire = retire_capture_urb, + .prepare_sync = prepare_capture_sync_urb, + .retire_sync = retire_capture_sync_urb, + }, +}; + +/* + * complete callback from data urb + */ +static void snd_complete_urb(struct urb *urb, struct pt_regs *regs) +{ + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + snd_usb_substream_t *subs = ctx->subs; + snd_pcm_substream_t *substream = ctx->subs->pcm_substream; + int err; + + clear_bit(ctx->index, &subs->active_mask); + if (subs->running && subs->ops.retire(subs, substream->runtime, urb)) + return; + if (! subs->running) /* can be stopped during retire callback */ + return; + if ((err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || + (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + return; + } + set_bit(ctx->index, &subs->active_mask); +} + + +/* + * complete callback from sync urb + */ +static void snd_complete_sync_urb(struct urb *urb, struct pt_regs *regs) +{ + snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; + snd_usb_substream_t *subs = ctx->subs; + snd_pcm_substream_t *substream = ctx->subs->pcm_substream; + int err; + + clear_bit(ctx->index + 16, &subs->active_mask); + if (subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) + return; + if (! subs->running) /* can be stopped during retire callback */ + return; + if ((err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 || + (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + return; + } + set_bit(ctx->index + 16, &subs->active_mask); +} + + +/* + * unlink active urbs. + * return the number of active urbs. + */ +static int deactivate_urbs(snd_usb_substream_t *subs, int force) +{ + unsigned int i; + int alive; + + subs->running = 0; + + if (!force && subs->stream->chip->shutdown) /* to be sure... */ + return 0; + +#ifndef SND_USB_ASYNC_UNLINK + if (in_interrupt()) + return 0; +#endif + alive = 0; + for (i = 0; i < subs->nurbs; i++) { + if (test_bit(i, &subs->active_mask)) { + alive++; + if (! test_and_set_bit(i, &subs->unlink_mask)) + usb_unlink_urb(subs->dataurb[i].urb); + } + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (test_bit(i+16, &subs->active_mask)) { + alive++; + if (! test_and_set_bit(i+16, &subs->unlink_mask)) + usb_unlink_urb(subs->syncurb[i].urb); + } + } + } +#ifdef SND_USB_ASYNC_UNLINK + return alive; +#else + return 0; +#endif +} + + +/* + * set up and start data/sync urbs + */ +static int start_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) +{ + unsigned int i; + int err; + + for (i = 0; i < subs->nurbs; i++) { + snd_assert(subs->dataurb[i].urb, return -EINVAL); + if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) { + snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i); + goto __error; + } + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + snd_assert(subs->syncurb[i].urb, return -EINVAL); + if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) { + snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i); + goto __error; + } + } + } + + subs->running = 1; + for (i = 0; i < subs->nurbs; i++) { + if ((err = usb_submit_urb(subs->dataurb[i].urb, GFP_KERNEL)) < 0) { + snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err); + goto __error; + } + set_bit(i, &subs->active_mask); + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if ((err = usb_submit_urb(subs->syncurb[i].urb, GFP_KERNEL)) < 0) { + snd_printk(KERN_ERR "cannot submit syncpipe for urb %d, err = %d\n", i, err); + goto __error; + } + set_bit(i + 16, &subs->active_mask); + } + } + return 0; + + __error: + // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); + deactivate_urbs(subs, 0); + return -EPIPE; +} + + +/* + * wait until all urbs are processed. + */ +static int wait_clear_urbs(snd_usb_substream_t *subs) +{ + int timeout = HZ; + unsigned int i; + int alive; + + do { + alive = 0; + for (i = 0; i < subs->nurbs; i++) { + if (test_bit(i, &subs->active_mask)) + alive++; + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (test_bit(i + 16, &subs->active_mask)) + alive++; + } + } + if (! alive) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + set_current_state(TASK_RUNNING); + } while (--timeout > 0); + if (alive) + snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); + return 0; +} + + +/* + * return the current pcm pointer. just return the hwptr_done value. + */ +static snd_pcm_uframes_t snd_usb_pcm_pointer(snd_pcm_substream_t *substream) +{ + snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data; + return subs->hwptr_done; +} + + +/* + * start/stop substream + */ +static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data; + int err; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = start_urbs(subs, substream->runtime); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = deactivate_urbs(subs, 0); + break; + default: + err = -EINVAL; + break; + } + return err < 0 ? err : 0; +} + + +/* + * release a urb data + */ +static void release_urb_ctx(snd_urb_ctx_t *u) +{ + if (u->urb) { + usb_free_urb(u->urb); + u->urb = 0; + } + if (u->buf) { + kfree(u->buf); + u->buf = 0; + } +} + +/* + * release a substream + */ +static void release_substream_urbs(snd_usb_substream_t *subs, int force) +{ + int i; + + /* stop urbs (to be sure) */ + if (deactivate_urbs(subs, force) > 0) + wait_clear_urbs(subs); + + for (i = 0; i < MAX_URBS; i++) + release_urb_ctx(&subs->dataurb[i]); + for (i = 0; i < SYNC_URBS; i++) + release_urb_ctx(&subs->syncurb[i]); + if (subs->tmpbuf) { + kfree(subs->tmpbuf); + subs->tmpbuf = 0; + } + subs->nurbs = 0; +} + +/* + * initialize a substream for plaback/capture + */ +static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) +{ + unsigned int maxsize, n, i; + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int npacks[MAX_URBS], total_packs; + + /* calculate the frequency in 10.14 format */ + subs->freqn = subs->freqm = get_usb_rate(runtime->rate); + subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */ + subs->phase = 0; + + /* reset the pointer */ + subs->hwptr = 0; + subs->hwptr_done = 0; + subs->transfer_sched = 0; + subs->transfer_done = 0; + subs->active_mask = 0; + subs->unlink_mask = 0; + + /* calculate the max. size of packet */ + maxsize = ((subs->freqmax + 0x3fff) * (runtime->frame_bits >> 3)) >> 14; + if (subs->maxpacksize && maxsize > subs->maxpacksize) { + //snd_printd(KERN_DEBUG "maxsize %d is greater than defined size %d\n", + // maxsize, subs->maxpacksize); + maxsize = subs->maxpacksize; + } + + if (subs->fill_max) + subs->curpacksize = subs->maxpacksize; + else + subs->curpacksize = maxsize; + subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); + + /* allocate a temporary buffer for playback */ + if (is_playback) { + subs->tmpbuf = kmalloc(maxsize * NRPACKS, GFP_KERNEL); + if (! subs->tmpbuf) { + snd_printk(KERN_ERR "cannot malloc tmpbuf\n"); + return -ENOMEM; + } + } + + /* decide how many packets to be used */ + total_packs = (frames_to_bytes(runtime, runtime->period_size) + maxsize - 1) / maxsize; + if (total_packs < 2 * MIN_PACKS_URB) + total_packs = 2 * MIN_PACKS_URB; + subs->nurbs = (total_packs + NRPACKS - 1) / NRPACKS; + if (subs->nurbs > MAX_URBS) { + /* too much... */ + subs->nurbs = MAX_URBS; + total_packs = MAX_URBS * NRPACKS; + } + n = total_packs; + for (i = 0; i < subs->nurbs; i++) { + npacks[i] = n > NRPACKS ? NRPACKS : n; + n -= NRPACKS; + } + if (subs->nurbs <= 1) { + /* too little - we need at least two packets + * to ensure contiguous playback/capture + */ + subs->nurbs = 2; + npacks[0] = (total_packs + 1) / 2; + npacks[1] = total_packs - npacks[0]; + } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB) { + /* the last packet is too small.. */ + if (subs->nurbs > 2) { + /* merge to the first one */ + npacks[0] += npacks[subs->nurbs - 1]; + subs->nurbs--; + } else { + /* divide to two */ + subs->nurbs = 2; + npacks[0] = (total_packs + 1) / 2; + npacks[1] = total_packs - npacks[0]; + } + } + + /* allocate and initialize data urbs */ + for (i = 0; i < subs->nurbs; i++) { + snd_urb_ctx_t *u = &subs->dataurb[i]; + u->index = i; + u->subs = subs; + u->transfer = 0; + u->packets = npacks[i]; + if (! is_playback) { + /* allocate a capture buffer per urb */ + u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL); + if (! u->buf) { + release_substream_urbs(subs, 0); + return -ENOMEM; + } + } + u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); + if (! u->urb) { + release_substream_urbs(subs, 0); + return -ENOMEM; + } + u->urb->dev = subs->dev; + u->urb->pipe = subs->datapipe; + u->urb->transfer_flags = URB_ISO_ASAP | UNLINK_FLAGS; + u->urb->number_of_packets = u->packets; + u->urb->context = u; + u->urb->complete = snd_usb_complete_callback(snd_complete_urb); + } + + if (subs->syncpipe) { + /* allocate and initialize sync urbs */ + for (i = 0; i < SYNC_URBS; i++) { + snd_urb_ctx_t *u = &subs->syncurb[i]; + u->index = i; + u->subs = subs; + u->packets = NRPACKS; + u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); + if (! u->urb) { + release_substream_urbs(subs, 0); + return -ENOMEM; + } + u->urb->transfer_buffer = subs->syncbuf + i * NRPACKS * 3; + u->urb->transfer_buffer_length = NRPACKS * 3; + u->urb->dev = subs->dev; + u->urb->pipe = subs->syncpipe; + u->urb->transfer_flags = URB_ISO_ASAP | UNLINK_FLAGS; + u->urb->number_of_packets = u->packets; + u->urb->context = u; + u->urb->complete = snd_usb_complete_callback(snd_complete_sync_urb); + } + } + return 0; +} + + +/* + * find a matching audio format + */ +static struct audioformat *find_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) +{ + struct list_head *p; + + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (fp->format != runtime->format || + fp->channels != runtime->channels) + continue; + if (runtime->rate < fp->rate_min || runtime->rate > fp->rate_max) + continue; + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) + return fp; + else { + unsigned int i; + for (i = 0; i < fp->nr_rates; i++) + if (fp->rate_table[i] == runtime->rate) + return fp; + } + } + return NULL; +} + + +/* + * initialize the picth control and sample rate + */ +static int init_usb_pitch(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt) +{ + unsigned int ep; + unsigned char data[1]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { + data[0] = 1; + if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", + dev->devnum, iface, ep); + return err; + } + } + return 0; +} + +static int init_usb_sample_rate(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) +{ + unsigned int ep; + unsigned char data[3]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { + int crate; + data[0] = rate; + data[1] = rate >> 8; + data[2] = rate >> 16; + if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", + dev->devnum, iface, fmt->altsetting, rate, ep); + return err; + } + if ((err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n", + dev->devnum, iface, fmt->altsetting, ep); + return err; + } + crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (crate != rate) { + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); + // runtime->rate = crate; + } + } + return 0; +} + +/* + * find a matching format and set up the interface + */ +static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) +{ + struct usb_device *dev = subs->dev; + struct usb_host_config *config = dev->actconfig; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_interface *iface; + struct audioformat *fmt; + unsigned int ep, attr; + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + int err; + + fmt = find_format(subs, runtime); + if (! fmt) { + snd_printd(KERN_DEBUG "cannot set format: format = %s, rate = %d, channels = %d\n", + snd_pcm_format_name(runtime->format), runtime->rate, runtime->channels); + return -EINVAL; + } + + iface = &config->interface[fmt->iface]; + alts = &iface->altsetting[fmt->altset_idx]; + altsd = get_iface_desc(alts); + snd_assert(altsd->bAlternateSetting == fmt->altsetting, return -EINVAL); + + /* close the old interface */ + if (subs->interface >= 0 && subs->interface != fmt->iface) { + usb_set_interface(subs->dev, subs->interface, 0); + subs->interface = -1; + subs->format = 0; + } + + /* set interface */ + if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) { + if (usb_set_interface(dev, fmt->iface, fmt->altset_idx) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EIO; + } + snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altset_idx); + subs->interface = fmt->iface; + subs->format = fmt->altset_idx; + } + + /* create a data pipe */ + ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK; + if (is_playback) + subs->datapipe = usb_sndisocpipe(dev, ep); + else + subs->datapipe = usb_rcvisocpipe(dev, ep); + subs->syncpipe = subs->syncinterval = 0; + subs->maxpacksize = get_endpoint(alts, 0)->wMaxPacketSize; + subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); + subs->fill_max = 0; + + /* we need a sync pipe in async OUT or adaptive IN mode */ + attr = fmt->ep_attr & EP_ATTR_MASK; + if ((is_playback && attr == EP_ATTR_ASYNC) || + (! is_playback && attr == EP_ATTR_ADAPTIVE)) { + /* + * QUIRK: plantronics headset has adaptive-in + * although it's really not... + */ + if (dev->descriptor.idVendor == 0x047f && + dev->descriptor.idProduct == 0x0ca1) + goto _ok; + /* check endpoint */ + if (altsd->bNumEndpoints < 2 || + get_endpoint(alts, 1)->bmAttributes != 0x01 || + get_endpoint(alts, 1)->bSynchAddress != 0) { + snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EINVAL; + } + ep = get_endpoint(alts, 1)->bEndpointAddress; + if ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || + (! is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN))) { + snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EINVAL; + } + ep &= USB_ENDPOINT_NUMBER_MASK; + if (is_playback) + subs->syncpipe = usb_rcvisocpipe(dev, ep); + else + subs->syncpipe = usb_sndisocpipe(dev, ep); + subs->syncinterval = get_endpoint(alts, 1)->bRefresh; + } + + _ok: + if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0 || + (err = init_usb_sample_rate(dev, subs->interface, alts, fmt, + runtime->rate)) < 0) + return err; + + /* always fill max packet size */ + if (fmt->attributes & EP_CS_ATTR_FILL_MAX) + subs->fill_max = 1; + +#if 0 + printk("setting done: format = %d, rate = %d, channels = %d\n", + runtime->format, runtime->rate, runtime->channels); + printk(" datapipe = 0x%0x, syncpipe = 0x%0x\n", + subs->datapipe, subs->syncpipe); +#endif + + return 0; +} + +/* + * allocate a buffer. + * + * so far we use a physically linear buffer although packetize transfer + * doesn't need a continuous area. + * if sg buffer is supported on the later version of alsa, we'll follow + * that. + */ +static int snd_usb_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +/* + * free the buffer + */ +static int snd_usb_hw_free(snd_pcm_substream_t *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* + * prepare callback + * + * set format and initialize urbs + */ +static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_usb_substream_t *subs = (snd_usb_substream_t *)runtime->private_data; + int err; + + release_substream_urbs(subs, 0); + if ((err = set_format(subs, runtime)) < 0) + return err; + + return init_substream_urbs(subs, runtime); +} + +static snd_pcm_hardware_t snd_usb_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, +}; + +static snd_pcm_hardware_t snd_usb_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, +}; + +/* + * h/w constraints + */ + +#ifdef HW_CONST_DEBUG +#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) +#else +#define hwc_debug(fmt, args...) /**/ +#endif + +static int hw_check_valid_format(snd_pcm_hw_params_t *params, struct audioformat *fp) +{ + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + snd_interval_t *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_mask_t *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* check the format */ + if (! snd_mask_test(fmts, fp->format)) { + hwc_debug(" > check: no supported format %d\n", fp->format); + return 0; + } + /* check the channels */ + if (fp->channels < ct->min || fp->channels > ct->max) { + hwc_debug(" > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max); + return 0; + } + /* check the rate is within the range */ + if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) { + hwc_debug(" > check: rate_min %d > max %d\n", fp->rate_min, it->max); + return 0; + } + if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) { + hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min); + return 0; + } + return 1; +} + +static int hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + unsigned int rmin, rmax; + int changed; + + hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + if (changed++) { + if (rmin > fp->rate_min) + rmin = fp->rate_min; + if (rmax < fp->rate_max) + rmax = fp->rate_max; + } else { + rmin = fp->rate_min; + rmax = fp->rate_max; + } + } + + if (! changed) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + + +static int hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + unsigned int rmin, rmax; + int changed; + + hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + if (changed++) { + if (rmin > fp->channels) + rmin = fp->channels; + if (rmax < fp->channels) + rmax = fp->channels; + } else { + rmin = fp->channels; + rmax = fp->channels; + } + } + + if (! changed) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + +static int hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_mask_t *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + u64 fbits; + u32 oldbits[2]; + int changed; + + hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); + fbits = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + fbits |= (1UL << fp->format); + } + + oldbits[0] = fmt->bits[0]; + oldbits[1] = fmt->bits[1]; + fmt->bits[0] &= (u32)fbits; + fmt->bits[1] &= (u32)(fbits >> 32); + if (! fmt->bits[0] && ! fmt->bits[1]) { + hwc_debug(" --> get empty\n"); + return -EINVAL; + } + changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]); + hwc_debug(" --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed); + return changed; +} + +/* + * check whether the registered audio formats need special hw-constraints + */ +static int check_hw_params_convention(snd_usb_substream_t *subs) +{ + int i; + u32 channels[64]; + u32 rates[64]; + u32 cmaster, rmaster; + struct list_head *p; + + memset(channels, 0, sizeof(channels)); + memset(rates, 0, sizeof(rates)); + + list_for_each(p, &subs->fmt_list) { + struct audioformat *f; + f = list_entry(p, struct audioformat, list); + /* unconventional channels? */ + if (f->channels > 32) + return 1; + /* combination of continuous rates and fixed rates? */ + if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) { + if (f->rates != rates[f->format]) + return 1; + } + if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { + if (rates[f->format] && rates[f->format] != f->rates) + return 1; + } + channels[f->format] |= (1 << f->channels); + rates[f->format] |= f->rates; + } + /* check whether channels and rates match for all formats */ + cmaster = rmaster = 0; + for (i = 0; i < 64; i++) { + if (cmaster != channels[i] && cmaster && channels[i]) + return 1; + if (rmaster != rates[i] && rmaster && rates[i]) + return 1; + if (channels[i]) + cmaster = channels[i]; + if (rates[i]) + rmaster = rates[i]; + } + return 0; +} + + +/* + * set up the runtime hardware information. + */ + +static int setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs) +{ + struct list_head *p; + int err; + + runtime->hw.formats = subs->formats; + + runtime->hw.rate_min = 0x7fffffff; + runtime->hw.rate_max = 0; + runtime->hw.channels_min = 256; + runtime->hw.channels_max = 0; + runtime->hw.rates = 0; + /* check min/max rates and channels */ + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + runtime->hw.rates |= fp->rates; + if (runtime->hw.rate_min > fp->rate_min) + runtime->hw.rate_min = fp->rate_min; + if (runtime->hw.rate_max < fp->rate_max) + runtime->hw.rate_max = fp->rate_max; + if (runtime->hw.channels_min > fp->channels) + runtime->hw.channels_min = fp->channels; + if (runtime->hw.channels_max < fp->channels) + runtime->hw.channels_max = fp->channels; + } + + /* set the period time minimum 1ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 1000 * MIN_PACKS_URB, + /*(NRPACKS * MAX_URBS) * 1000*/ UINT_MAX); + + if (check_hw_params_convention(subs)) { + hwc_debug("setting extra hw constraints...\n"); + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + } + return 0; +} + +static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction, + snd_pcm_hardware_t *hw) +{ + snd_usb_stream_t *as = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_usb_substream_t *subs = &as->substream[direction]; + + subs->interface = -1; + subs->format = 0; + runtime->hw = *hw; + runtime->private_data = subs; + subs->pcm_substream = substream; + return setup_hw_info(runtime, subs); +} + +static int snd_usb_pcm_close(snd_pcm_substream_t *substream, int direction) +{ + snd_usb_stream_t *as = snd_pcm_substream_chip(substream); + snd_usb_substream_t *subs = &as->substream[direction]; + + release_substream_urbs(subs, 0); + if (subs->interface >= 0) + usb_set_interface(subs->dev, subs->interface, 0); + subs->pcm_substream = NULL; + return 0; +} + +static int snd_usb_playback_open(snd_pcm_substream_t *substream) +{ + return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback); +} + +static int snd_usb_playback_close(snd_pcm_substream_t *substream) +{ + return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK); +} + +static int snd_usb_capture_open(snd_pcm_substream_t *substream) +{ + return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture); +} + +static int snd_usb_capture_close(snd_pcm_substream_t *substream) +{ + return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE); +} + +static snd_pcm_ops_t snd_usb_playback_ops = { + .open = snd_usb_playback_open, + .close = snd_usb_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_hw_params, + .hw_free = snd_usb_hw_free, + .prepare = snd_usb_pcm_prepare, + .trigger = snd_usb_pcm_trigger, + .pointer = snd_usb_pcm_pointer, +}; + +static snd_pcm_ops_t snd_usb_capture_ops = { + .open = snd_usb_capture_open, + .close = snd_usb_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_hw_params, + .hw_free = snd_usb_hw_free, + .prepare = snd_usb_pcm_prepare, + .trigger = snd_usb_pcm_trigger, + .pointer = snd_usb_pcm_pointer, +}; + + + +/* + * helper functions + */ + +/* + * combine bytes and get an integer value + */ +unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size) +{ + switch (size) { + case 1: return *bytes; + case 2: return combine_word(bytes); + case 3: return combine_triple(bytes); + case 4: return combine_quad(bytes); + default: return 0; + } +} + +/* + * parse descriptor buffer and return the pointer starting the given + * descriptor type. + */ +void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype) +{ + u8 *p, *end, *next; + + p = descstart; + end = p + desclen; + for (; p < end;) { + if (p[0] < 2) + return NULL; + next = p + p[0]; + if (next > end) + return NULL; + if (p[1] == dtype && (!after || (void *)p > after)) { + return p; + } + p = next; + } + return NULL; +} + +/* + * find a class-specified interface descriptor with the given subtype. + */ +void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype) +{ + unsigned char *p = after; + + while ((p = snd_usb_find_desc(buffer, buflen, p, + USB_DT_CS_INTERFACE)) != NULL) { + if (p[0] >= 3 && p[2] == dsubtype) + return p; + } + return NULL; +} + + +/* + * entry point for linux usb interface + */ + +#ifndef OLD_USB +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void usb_audio_disconnect(struct usb_interface *intf); +#endif + +static struct usb_device_id usb_audio_ids [] = { +#include "usbquirks.h" + { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_audio_ids); + +static struct usb_driver usb_audio_driver = { + .name = "snd-usb-audio", + .probe = usb_audio_probe, + .disconnect = usb_audio_disconnect, +#ifdef OLD_USB + .driver_list = LIST_HEAD_INIT(usb_audio_driver.driver_list), +#endif + .id_table = usb_audio_ids, +}; + + +/* + * proc interface for list the supported pcm formats + */ +static void proc_dump_substream_formats(snd_usb_substream_t *subs, snd_info_buffer_t *buffer) +{ + struct list_head *p; + static char *sync_types[4] = { + "NONE", "ASYNC", "ADAPTIVE", "SYNC" + }; + + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + snd_iprintf(buffer, " Interface %d\n", fp->iface); + snd_iprintf(buffer, " Altset %d\n", fp->altset_idx); + snd_iprintf(buffer, " Format: %s\n", snd_pcm_format_name(fp->format)); + snd_iprintf(buffer, " Channels: %d\n", fp->channels); + snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", + fp->endpoint & USB_ENDPOINT_NUMBER_MASK, + fp->endpoint & USB_DIR_IN ? "IN" : "OUT", + sync_types[(fp->ep_attr & EP_ATTR_MASK) >> 2]); + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) { + snd_iprintf(buffer, " Rates: %d - %d (continuous)\n", + fp->rate_min, fp->rate_max); + } else { + unsigned int i; + snd_iprintf(buffer, " Rates: "); + for (i = 0; i < fp->nr_rates; i++) { + if (i > 0) + snd_iprintf(buffer, ", "); + snd_iprintf(buffer, "%d", fp->rate_table[i]); + } + snd_iprintf(buffer, "\n"); + } + } +} + +static void proc_dump_substream_status(snd_usb_substream_t *subs, snd_info_buffer_t *buffer) +{ + if (subs->running) { + unsigned int i; + snd_iprintf(buffer, " Status: Running\n"); + snd_iprintf(buffer, " Interface = %d\n", subs->interface); + snd_iprintf(buffer, " Altset = %d\n", subs->format); + snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs); + for (i = 0; i < subs->nurbs; i++) + snd_iprintf(buffer, "%d ", subs->dataurb[i].packets); + snd_iprintf(buffer, "]\n"); + snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize); + snd_iprintf(buffer, " Momentary freq = %d,%03d Hz\n", + subs->freqm >> 14, + ((subs->freqm & ((1 << 14) - 1)) * 1000) / ((1 << 14) - 1)); + } else { + snd_iprintf(buffer, " Status: Stop\n"); + } +} + +static void proc_pcm_format_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_usb_stream_t *stream = snd_magic_cast(snd_usb_stream_t, entry->private_data, return); + + snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name); + + if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) { + snd_iprintf(buffer, "\nPlayback:\n"); + proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); + proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); + } + if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) { + snd_iprintf(buffer, "\nCapture:\n"); + proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); + proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); + } +} + +static void proc_pcm_format_add(snd_usb_stream_t *stream) +{ + snd_info_entry_t *entry; + char name[32]; + snd_card_t *card = stream->chip->card; + + sprintf(name, "stream%d", stream->pcm_index); + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, stream, proc_pcm_format_read); +} + + +/* + * initialize the substream instance. + */ + +static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat *fp) +{ + snd_usb_substream_t *subs = &as->substream[stream]; + + INIT_LIST_HEAD(&subs->fmt_list); + spin_lock_init(&subs->lock); + + subs->stream = as; + subs->direction = stream; + subs->dev = as->chip->dev; + subs->ops = audio_urb_ops[stream]; + snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream, + 64 * 1024, 128 * 1024, GFP_ATOMIC); + snd_pcm_set_ops(as->pcm, stream, + stream == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_usb_playback_ops : &snd_usb_capture_ops); + + list_add_tail(&fp->list, &subs->fmt_list); + subs->formats |= 1ULL << fp->format; + subs->endpoint = fp->endpoint; + subs->num_formats++; +} + + +/* + * free a substream + */ +static void free_substream(snd_usb_substream_t *subs) +{ + struct list_head *p, *n; + + if (! subs->num_formats) + return; /* not initialized */ + list_for_each_safe(p, n, &subs->fmt_list) { + struct audioformat *fp = list_entry(p, struct audioformat, list); + if (fp->rate_table) + kfree(fp->rate_table); + kfree(fp); + } +} + + +/* + * free a usb stream instance + */ +static void snd_usb_audio_stream_free(snd_usb_stream_t *stream) +{ + free_substream(&stream->substream[0]); + free_substream(&stream->substream[1]); + list_del(&stream->list); + snd_magic_kfree(stream); +} + +static void snd_usb_audio_pcm_free(snd_pcm_t *pcm) +{ + snd_usb_stream_t *stream = pcm->private_data; + if (stream) { + stream->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); + snd_usb_audio_stream_free(stream); + } +} + + +/* + * add this endpoint to the chip instance. + * if a stream with the same endpoint already exists, append to it. + * if not, create a new pcm stream. + */ +static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audioformat *fp) +{ + struct list_head *p; + snd_usb_stream_t *as; + snd_usb_substream_t *subs; + snd_pcm_t *pcm; + int err; + + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, snd_usb_stream_t, list); + subs = &as->substream[stream]; + if (! subs->endpoint) + break; + if (subs->endpoint == fp->endpoint) { + list_add_tail(&fp->list, &subs->fmt_list); + subs->num_formats++; + subs->formats |= 1ULL << fp->format; + return 0; + } + } + /* look for an empty stream */ + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, snd_usb_stream_t, list); + subs = &as->substream[stream]; + if (subs->endpoint) + continue; + err = snd_pcm_new_stream(as->pcm, stream, 1); + if (err < 0) + return err; + init_substream(as, stream, fp); + return 0; + } + + /* create a new pcm */ + as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL); + if (! as) + return -ENOMEM; + memset(as, 0, sizeof(*as)); + as->pcm_index = chip->pcm_devs; + as->chip = chip; + err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, + stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, + stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, + &pcm); + if (err < 0) { + snd_magic_kfree(as); + return err; + } + as->pcm = pcm; + pcm->private_data = as; + pcm->private_free = snd_usb_audio_pcm_free; + pcm->info_flags = 0; + if (chip->pcm_devs > 0) + sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); + else + strcpy(pcm->name, "USB Audio"); + + init_substream(as, stream, fp); + + list_add(&as->list, &chip->pcm_list); + chip->pcm_devs++; + + proc_pcm_format_add(as); + + return 0; +} + + +/* + * parse the audio format type descriptor + * and returns the corresponding pcm format + */ +static int parse_audio_format_type(struct usb_device *dev, int iface_no, int altno, + int format, unsigned char *fmt) +{ + int format_type = fmt[3]; + int pcm_format; + + /* FIXME: needed support for TYPE II and III */ + if (format_type != USB_FORMAT_TYPE_I) { + snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n", + dev->devnum, iface_no, altno, format_type); + return -1; + } + + /* FIXME: correct endianess and sign? */ + pcm_format = -1; + switch (format) { + case 0: /* some devices don't define this correctly... */ + snd_printd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", + dev->devnum, iface_no, altno); + /* fall-through */ + case USB_AUDIO_FORMAT_PCM: + /* check the format byte size */ + switch (fmt[6]) { + case 8: + pcm_format = SNDRV_PCM_FORMAT_U8; + break; + case 16: + pcm_format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 18: + case 20: + if (fmt[5] == 3) + pcm_format = SNDRV_PCM_FORMAT_S24_3LE; + else + snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n", + dev->devnum, iface_no, altno, fmt[6], fmt[5]); + break; + case 24: + if (fmt[5] == 4) + /* FIXME: correct? or S32_LE? */ + pcm_format = SNDRV_PCM_FORMAT_S24_LE; + else if (fmt[5] == 3) + pcm_format = SNDRV_PCM_FORMAT_S24_3LE; + else + snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n", + dev->devnum, iface_no, altno, format, fmt[5]); + break; + case 32: + pcm_format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", + dev->devnum, iface_no, altno, fmt[6], fmt[5]); + break; + } + break; + case USB_AUDIO_FORMAT_PCM8: + /* Dallas DS4201 workaround */ + if (dev->descriptor.idVendor == 0x04fa && dev->descriptor.idProduct == 0x4201) + pcm_format = SNDRV_PCM_FORMAT_S8; + else + pcm_format = SNDRV_PCM_FORMAT_U8; + break; + case USB_AUDIO_FORMAT_IEEE_FLOAT: + pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; + break; + case USB_AUDIO_FORMAT_ALAW: + pcm_format = SNDRV_PCM_FORMAT_A_LAW; + break; + case USB_AUDIO_FORMAT_MU_LAW: + pcm_format = SNDRV_PCM_FORMAT_MU_LAW; + break; + default: + snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", + dev->devnum, iface_no, altno, format); + break; + } + return pcm_format; +} + + +static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no) +{ + struct usb_device *dev; + struct usb_host_config *config; + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + int i, altno, err, stream; + int channels, nr_rates, pcm_format, format; + struct audioformat *fp; + unsigned char *fmt, *csep; + + dev = chip->dev; + config = dev->actconfig; + + /* parse the interface's altsettings */ + iface = &config->interface[iface_no]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + altsd = get_iface_desc(alts); + /* skip invalid one */ + if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING || + altsd->bNumEndpoints < 1) + continue; + /* must be isochronous */ + if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_ISOC) + continue; + /* check direction */ + stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + altno = altsd->bAlternateSetting; + + /* get audio formats */ + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL); + if (!fmt) { + snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n", + dev->devnum, iface_no, altno); + continue; + } + + if (fmt[0] < 7) { + snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n", + dev->devnum, iface_no, altno); + continue; + } + + format = (fmt[6] << 8) | fmt[5]; /* remember the format value */ + + /* get format type */ + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, FORMAT_TYPE); + if (!fmt) { + snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n", + dev->devnum, iface_no, altno); + continue; + } + if (fmt[0] < 8) { + snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", + dev->devnum, iface_no, altno); + continue; + } + + pcm_format = parse_audio_format_type(dev, iface_no, altno, format, fmt); + if (pcm_format < 0) + continue; + + channels = fmt[4]; + if (channels < 1) { + snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", + dev->devnum, iface_no, altno, channels); + continue; + } + + nr_rates = fmt[7]; + if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) { + snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", + dev->devnum, iface_no, altno); + continue; + } + + csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); + if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { + snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", + dev->devnum, iface_no, altno); + continue; + } + + fp = kmalloc(sizeof(*fp), GFP_KERNEL); + if (! fp) { + snd_printk(KERN_ERR "cannot malloc\n"); + return -ENOMEM; + } + + memset(fp, 0, sizeof(*fp)); + fp->iface = iface_no; + fp->altsetting = altno; + fp->altset_idx = i; + fp->format = pcm_format; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->channels = channels; + fp->attributes = csep[3]; + + if (nr_rates) { + /* + * build the rate table and bitmap flags + */ + int r, idx, c; + /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */ + static unsigned int conv_rates[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, + 64000, 88200, 96000, 176400, 192000 + }; + fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); + if (fp->rate_table == NULL) { + snd_printk(KERN_ERR "cannot malloc\n"); + kfree(fp); + break; + } + + fp->nr_rates = nr_rates; + fp->rate_min = fp->rate_max = combine_triple(&fmt[8]); + for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) { + unsigned int rate = fp->rate_table[r] = combine_triple(&fmt[idx]); + if (rate < fp->rate_min) + fp->rate_min = rate; + else if (rate > fp->rate_max) + fp->rate_max = rate; + for (c = 0; c < 13; c++) { + if (rate == conv_rates[c]) { + fp->rates |= (1 << c); + break; + } + } +#if 0 // FIXME - we need to define constraint + if (c >= 13) + fp->rates |= SNDRV_PCM_RATE_KNOT; /* unconventional rate */ +#endif + } + + } else { + /* continuous rates */ + fp->rates = SNDRV_PCM_RATE_CONTINUOUS; + fp->rate_min = combine_triple(&fmt[8]); + fp->rate_max = combine_triple(&fmt[11]); + } + + + snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint 0x%x\n", dev->devnum, iface_no, i, fp->endpoint); + err = add_audio_endpoint(chip, stream, fp); + if (err < 0) { + if (fp->rate_table) + kfree(fp->rate_table); + kfree(fp); + return err; + } + /* try to set the interface... */ + usb_set_interface(chip->dev, iface_no, i); + init_usb_pitch(chip->dev, iface_no, alts, fp); + init_usb_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max); + } + return 0; +} + + +/* + * parse audio control descriptor and create pcm/midi streams + */ +static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif) +{ + struct usb_device *dev = chip->dev; + struct usb_host_config *config; + struct usb_host_interface *host_iface; + struct usb_interface *iface; + unsigned char *p1; + int i, j; + + /* find audiocontrol interface */ + config = dev->actconfig; + host_iface = &config->interface[ctrlif].altsetting[0]; + if (!(p1 = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, NULL, HEADER))) { + snd_printk(KERN_ERR "cannot find HEADER\n"); + return -EINVAL; + } + if (! p1[7] || p1[0] < 8 + p1[7]) { + snd_printk(KERN_ERR "invalid HEADER\n"); + return -EINVAL; + } + + /* + * parse all USB audio streaming interfaces + */ + for (i = 0; i < p1[7]; i++) { + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + j = p1[8 + i]; + if (j >= get_cfg_desc(config)->bNumInterfaces) { + snd_printk(KERN_ERR "%d:%u:%d : does not exist\n", + dev->devnum, ctrlif, j); + continue; + } + iface = &config->interface[j]; + if (usb_interface_claimed(iface)) { + snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n", dev->devnum, ctrlif, j); + continue; + } + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && + altsd->bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) { + if (snd_usb_create_midi_interface(chip, iface, NULL) < 0) { + snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j); + continue; + } + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + continue; + } + if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) { + snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", dev->devnum, ctrlif, j, altsd->bInterfaceClass); + /* skip non-supported classes */ + continue; + } + if (! parse_audio_endpoints(chip, j)) { + usb_set_interface(dev, j, 0); /* reset the current interface */ + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + } + } + + return 0; +} + +/* + * create a stream for an endpoint/altsetting without proper descriptors + */ +static int create_fixed_stream_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) +{ + struct audioformat *fp; + struct usb_host_interface *alts; + int stream, err; + + fp = kmalloc(sizeof(*fp), GFP_KERNEL); + if (! fp) { + snd_printk(KERN_ERR "cannot malloc\n"); + return -ENOMEM; + } + memcpy(fp, quirk->data, sizeof(*fp)); + stream = (fp->endpoint & USB_DIR_IN) + ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + err = add_audio_endpoint(chip, stream, fp); + if (err < 0) { + kfree(fp); + return err; + } + alts = &iface->altsetting[fp->altset_idx]; + usb_set_interface(chip->dev, fp->iface, 0); + init_usb_pitch(chip->dev, fp->iface, alts, fp); + init_usb_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max); + return 0; +} + +/* + * create a stream for an interface with proper descriptors + */ +static int create_standard_interface_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface) +{ + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + int err; + + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + switch (altsd->bInterfaceSubClass) { + case USB_SUBCLASS_AUDIO_STREAMING: + err = parse_audio_endpoints(chip, altsd->bInterfaceNumber); + if (!err) + usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0); /* reset the current interface */ + break; + case USB_SUBCLASS_MIDI_STREAMING: + err = snd_usb_create_midi_interface(chip, iface, NULL); + break; + default: + snd_printk(KERN_ERR "if %d: non-supported subclass %d\n", + altsd->bInterfaceNumber, altsd->bInterfaceSubClass); + return -ENODEV; + } + if (err < 0) { + snd_printk(KERN_ERR "cannot setup if %d: error %d\n", + altsd->bInterfaceNumber, err); + return err; + } + return 0; +} + +static int snd_usb_create_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk); + +/* + * handle the quirks for the contained interfaces + */ +static int create_composite_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) +{ + struct usb_host_config *config = chip->dev->actconfig; + int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + int err; + + for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + if (quirk->ifnum >= get_cfg_desc(config)->bNumInterfaces) + continue; + iface = &config->interface[quirk->ifnum]; + if (quirk->ifnum != probed_ifnum && + usb_interface_claimed(iface)) + continue; + err = snd_usb_create_quirk(chip, iface, quirk); + if (err < 0) + return err; + if (quirk->ifnum != probed_ifnum) + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + } + return 0; +} + + +/* + * boot quirks + */ + +#define EXTIGY_FIRMWARE_SIZE_OLD 794 +#define EXTIGY_FIRMWARE_SIZE_NEW 483 + +static int snd_usb_boot_quirk(struct usb_device *dev, + struct usb_interface *intf, + const snd_usb_audio_quirk_t *quirk) +{ + struct usb_host_config *config = dev->actconfig; + + /* FIXME: we need to handle composite type quirks later.. */ + switch (quirk->type) { + case QUIRK_BOOT_EXTIGY: + snd_printdd(KERN_INFO "extigy_boot: boot length = %d\n", get_cfg_desc(config)->wTotalLength); + if (get_cfg_desc(config)->wTotalLength == EXTIGY_FIRMWARE_SIZE_OLD || + get_cfg_desc(config)->wTotalLength == EXTIGY_FIRMWARE_SIZE_NEW) { + /* Send message to force it to reconnect with full interface. */ + usb_control_msg(dev, usb_sndctrlpipe(dev,0), + 0x10, 0x43, 0x0001, 0x000a, NULL, 0, HZ); + usb_get_device_descriptor(dev); + usb_set_configuration(dev, get_cfg_desc(config)->bConfigurationValue); + return -ENODEV; /* quit this anyway */ + } + return 0; + } + return 0; +} + + +/* + * audio-interface quirks + */ +static int snd_usb_create_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) +{ + if (quirk->type & QUIRK_BOOT_MASK) + return 1; /* continue as the normal device */ + + switch (quirk->type) { + case QUIRK_MIDI_FIXED_ENDPOINT: + case QUIRK_MIDI_YAMAHA: + case QUIRK_MIDI_MIDIMAN: + return snd_usb_create_midi_interface(chip, iface, quirk); + case QUIRK_COMPOSITE: + return create_composite_quirk(chip, iface, quirk); + case QUIRK_AUDIO_FIXED_ENDPOINT: + return create_fixed_stream_quirk(chip, iface, quirk); + case QUIRK_STANDARD_INTERFACE: + return create_standard_interface_quirk(chip, iface); + default: + snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); + return -ENXIO; + } +} + + +/* + * free the chip instance + * + * here we have to do not much, since pcm and controls are already freed + * + */ + +static int snd_usb_audio_free(snd_usb_audio_t *chip) +{ + down(®ister_mutex); + usb_chip[chip->index] = NULL; + up(®ister_mutex); + snd_magic_kfree(chip); + return 0; +} + +static int snd_usb_audio_dev_free(snd_device_t *device) +{ + snd_usb_audio_t *chip = snd_magic_cast(snd_usb_audio_t, device->device_data, return -ENXIO); + return snd_usb_audio_free(chip); +} + + +/* + * create a chip instance and set its names. + */ +static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, + const snd_usb_audio_quirk_t *quirk, + snd_usb_audio_t **rchip) +{ + snd_usb_audio_t *chip; + int err, len; + static snd_device_ops_t ops = { + .dev_free = snd_usb_audio_dev_free, + }; + + *rchip = NULL; + chip = snd_magic_kcalloc(snd_usb_audio_t, 0, GFP_KERNEL); + if (! chip) + return -ENOMEM; + + chip->dev = dev; + chip->card = card; + INIT_LIST_HEAD(&chip->pcm_list); + INIT_LIST_HEAD(&chip->midi_list); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_usb_audio_free(chip); + return err; + } + + strcpy(card->driver, "USB-Audio"); + + /* retrieve the device string as shortname */ + if (dev->descriptor.iProduct) + len = usb_string(dev, dev->descriptor.iProduct, + card->shortname, sizeof(card->shortname)); + else + len = 0; + if (len <= 0) { + if (quirk && quirk->product_name) { + strncpy(card->shortname, quirk->product_name, sizeof(card->shortname) - 1); + card->shortname[sizeof(card->shortname) - 1] = '\0'; + } else { + sprintf(card->shortname, "USB Device %#04x:%#04x", + dev->descriptor.idVendor, dev->descriptor.idProduct); + } + } + + /* retrieve the vendor and device strings as longname */ + if (dev->descriptor.iManufacturer) + len = usb_string(dev, dev->descriptor.iManufacturer, + card->longname, sizeof(card->longname) - 1); + else + len = 0; + if (len <= 0) { + if (quirk && quirk->vendor_name) { + strncpy(card->longname, quirk->vendor_name, sizeof(card->longname) - 2); + card->longname[sizeof(card->longname) - 2] = '\0'; + len = strlen(card->longname); + } else { + len = 0; + } + } + if (len > 0) { + card->longname[len] = ' '; + len++; + } + card->longname[len] = '\0'; + if ((!dev->descriptor.iProduct + || usb_string(dev, dev->descriptor.iProduct, + card->longname + len, sizeof(card->longname) - len) <= 0) + && quirk && quirk->product_name) { + strncpy(card->longname + len, quirk->product_name, sizeof(card->longname) - len - 1); + card->longname[sizeof(card->longname) - 1] = '\0'; + } + /* add device path to longname */ + len = strlen(card->longname); + if (sizeof(card->longname) - len > 10) { + strcpy(card->longname + len, " at "); + len += 4; + usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); + } + + *rchip = chip; + return 0; +} + + +/* + * probe the active usb device + * + * note that this can be called multiple times per a device, when it + * includes multiple audio control interfaces. + * + * thus we check the usb device pointer and creates the card instance + * only at the first time. the successive calls of this function will + * append the pcm interface to the corresponding card. + */ +static void *snd_usb_audio_probe(struct usb_device *dev, + struct usb_interface *intf, + const struct usb_device_id *usb_id) +{ + struct usb_host_config *config = dev->actconfig; + const snd_usb_audio_quirk_t *quirk = (const snd_usb_audio_quirk_t *)usb_id->driver_info; + int i, err; + snd_card_t *card; + snd_usb_audio_t *chip; + struct usb_host_interface *alts; + int ifnum; + + alts = &intf->altsetting[0]; + ifnum = get_iface_desc(alts)->bInterfaceNumber; + + if (quirk && quirk->ifnum != QUIRK_ANY_INTERFACE && ifnum != quirk->ifnum) + goto __err_val; + + if (usb_set_configuration(dev, get_cfg_desc(config)->bConfigurationValue) < 0) { + snd_printk(KERN_ERR "cannot set configuration (value 0x%x)\n", get_cfg_desc(config)->bConfigurationValue); + goto __err_val; + } + + if (quirk) { + /* try to check the boot quirk */ + if (snd_usb_boot_quirk(dev, intf, quirk) < 0) + goto __err_val; + } + + /* + * found a config. now register to ALSA + */ + + /* check whether it's already registered */ + chip = NULL; + down(®ister_mutex); + for (i = 0; i < SNDRV_CARDS; i++) { + if (usb_chip[i] && usb_chip[i]->dev == dev) { + chip = usb_chip[i]; + if (chip->shutdown) { + snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n"); + goto __error; + } + break; + } + } + if (! chip) { + /* it's a fresh one. + * now look for an empty slot and create a new card instance + */ + for (i = 0; i < SNDRV_CARDS; i++) + if (enable[i] && ! usb_chip[i] && + (vid[i] == -1 || vid[i] == dev->descriptor.idVendor) && + (pid[i] == -1 || pid[i] == dev->descriptor.idProduct)) { + card = snd_card_new(index[i], id[i], THIS_MODULE, 0); + if (card == NULL) { + snd_printk(KERN_ERR "cannot create a card instance %d\n", i); + goto __error; + } + if (snd_usb_audio_create(card, dev, quirk, &chip) < 0) { + snd_card_free(card); + goto __error; + } + chip->index = i; + usb_chip[i] = chip; + break; + } + if (! chip) { + snd_printk(KERN_ERR "no available usb audio device\n"); + goto __error; + } + } + + err = 1; /* continue */ + if (quirk) { + /* need some special handlings */ + if ((err = snd_usb_create_quirk(chip, intf, quirk)) < 0) + goto __error; + } + + if (err > 0) { + /* create normal USB audio interfaces */ + if (snd_usb_create_streams(chip, ifnum) < 0 || + snd_usb_create_mixer(chip, ifnum) < 0) { + goto __error; + } + } + + /* we are allowed to call snd_card_register() many times */ + if (snd_card_register(chip->card) < 0) { + if (! chip->num_interfaces) + snd_card_free(chip->card); + goto __error; + } + + chip->num_interfaces++; + up(®ister_mutex); + return chip; + + __error: + up(®ister_mutex); + __err_val: + return NULL; +} + +/* + * we need to take care of counter, since disconnection can be called also + * many times as well as usb_audio_probe(). + */ +static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) +{ + snd_usb_audio_t *chip; + snd_card_t *card; + struct list_head *p; + + if (ptr == (void *)-1L) + return; + + chip = snd_magic_cast(snd_usb_audio_t, ptr, return); + card = chip->card; + down(®ister_mutex); + chip->shutdown = 1; + chip->num_interfaces--; + if (chip->num_interfaces <= 0) { + snd_card_disconnect(card); + /* release the pcm resources */ + list_for_each(p, &chip->pcm_list) { + snd_usb_stream_t *as; + int idx; + as = list_entry(p, snd_usb_stream_t, list); + for (idx = 0; idx < 2; idx++) { + snd_usb_substream_t *subs; + subs = &as->substream[idx]; + if (!subs->num_formats) + continue; + release_substream_urbs(subs, 1); + subs->interface = -1; + } + } + /* release the midi resources */ + list_for_each(p, &chip->midi_list) { + snd_usbmidi_disconnect(p); + } + up(®ister_mutex); + snd_card_free_in_thread(card); + } else { + up(®ister_mutex); + } +} + +#ifndef OLD_USB +/* + * new 2.5 USB kernel API + */ +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + void *chip; + chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); + if (chip) { + dev_set_drvdata(&intf->dev, chip); + return 0; + } else + return -EIO; +} + +static void usb_audio_disconnect(struct usb_interface *intf) +{ + snd_usb_audio_disconnect(interface_to_usbdev(intf), + dev_get_drvdata(&intf->dev)); +} +#endif + + + +static int __init snd_usb_audio_init(void) +{ + usb_register(&usb_audio_driver); + return 0; +} + + +static void __exit snd_usb_audio_cleanup(void) +{ + usb_deregister(&usb_audio_driver); +} + +module_init(snd_usb_audio_init); +module_exit(snd_usb_audio_cleanup); + +#ifndef MODULE +/* + * format is snd-usb-audio=enable,index,id,vid,pid + */ +static int __init snd_usb_audio_module_setup(char* str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str, &enable[nr_dev]) == 2 && + get_option(&str, &index[nr_dev]) == 2 && + get_id(&str, &id[nr_dev]) == 2 && + get_option(&str, &vid[nr_dev]) == 2 && + get_option(&str, &pid[nr_dev]) == 2); + ++nr_dev; + return 1; +} + +__setup("snd-usb-audio=", snd_usb_audio_module_setup); + +#endif /* !MODULE */ diff -urN linux-2.4.21-rc1.orig/sound/usb/usbaudio.h linux/sound/usb/usbaudio.h --- linux-2.4.21-rc1.orig/sound/usb/usbaudio.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/usbaudio.h 2003-02-28 07:54:40.000000000 -0700 @@ -0,0 +1,228 @@ +#ifndef __USBAUDIO_H +#define __USBAUDIO_H +/* + * (Tentative) USB Audio Driver for ALSA + * + * Copyright (c) 2002 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + */ + +#define USB_SUBCLASS_AUDIO_CONTROL 0x01 +#define USB_SUBCLASS_AUDIO_STREAMING 0x02 +#define USB_SUBCLASS_MIDI_STREAMING 0x03 + +#define USB_DT_CS_DEVICE 0x21 +#define USB_DT_CS_CONFIG 0x22 +#define USB_DT_CS_STRING 0x23 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 + +#define CS_AUDIO_UNDEFINED 0x20 +#define CS_AUDIO_DEVICE 0x21 +#define CS_AUDIO_CONFIGURATION 0x22 +#define CS_AUDIO_STRING 0x23 +#define CS_AUDIO_INTERFACE 0x24 +#define CS_AUDIO_ENDPOINT 0x25 + +#define HEADER 0x01 +#define INPUT_TERMINAL 0x02 +#define OUTPUT_TERMINAL 0x03 +#define MIXER_UNIT 0x04 +#define SELECTOR_UNIT 0x05 +#define FEATURE_UNIT 0x06 +#define PROCESSING_UNIT 0x07 +#define EXTENSION_UNIT 0x08 + +#define AS_GENERAL 0x01 +#define FORMAT_TYPE 0x02 +#define FORMAT_SPECIFIC 0x03 + +#define EP_GENERAL 0x01 + +#define MS_GENERAL 0x01 +#define MIDI_IN_JACK 0x02 +#define MIDI_OUT_JACK 0x03 + +/* endpoint attributes */ +#define EP_ATTR_MASK 0x0c +#define EP_ATTR_ASYNC 0x04 +#define EP_ATTR_ADAPTIVE 0x08 +#define EP_ATTR_SYNC 0x0c + +/* cs endpoint attributes */ +#define EP_CS_ATTR_SAMPLE_RATE 0x01 +#define EP_CS_ATTR_PITCH_CONTROL 0x02 +#define EP_CS_ATTR_FILL_MAX 0x80 + +/* Audio Class specific Request Codes */ + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +/* Terminal Control Selectors */ + +#define COPY_PROTECT_CONTROL 0x01 + +/* Endpoint Control Selectors */ + +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 + +/* Format Types */ +#define USB_FORMAT_TYPE_I 0x01 +#define USB_FORMAT_TYPE_II 0x02 +#define USB_FORMAT_TYPE_III 0x03 + +/* type I */ +#define USB_AUDIO_FORMAT_PCM 0x01 +#define USB_AUDIO_FORMAT_PCM8 0x02 +#define USB_AUDIO_FORMAT_IEEE_FLOAT 0x03 +#define USB_AUDIO_FORMAT_ALAW 0x04 +#define USB_AUDIO_FORMAT_MU_LAW 0x05 + +/* type II */ +#define USB_AUDIO_FORMAT_MPEG 0x1001 +#define USB_AUDIO_FORMAT_AC3 0x1002 + +/* type III */ +#define USB_AUDIO_FORMAT_IEC1937_AC3 0x2001 +#define USB_AUDIO_FORMAT_IEC1937_MPEG1_LAYER1 0x2002 +#define USB_AUDIO_FORMAT_IEC1937_MPEG2_NOEXT 0x2003 +#define USB_AUDIO_FORMAT_IEC1937_MPEG2_EXT 0x2004 +#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER1_LS 0x2005 +#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER23_LS 0x2006 + + +/* maximum number of endpoints per interface */ +#define MIDI_MAX_ENDPOINTS 2 + +/* + */ + +typedef struct snd_usb_audio snd_usb_audio_t; + +struct snd_usb_audio { + + int index; + struct usb_device *dev; + snd_card_t *card; + int shutdown; + int num_interfaces; + + struct list_head pcm_list; /* list of pcm streams */ + int pcm_devs; + + struct list_head midi_list; /* list of midi interfaces */ + int next_midi_device; +}; + +/* + * Information about devices with broken descriptors + */ + +#define QUIRK_ANY_INTERFACE -1 + +#define QUIRK_MIDI_FIXED_ENDPOINT 0 +#define QUIRK_MIDI_YAMAHA 1 +#define QUIRK_MIDI_MIDIMAN 2 +#define QUIRK_COMPOSITE 3 +#define QUIRK_AUDIO_FIXED_ENDPOINT 4 +#define QUIRK_STANDARD_INTERFACE 5 + +#define QUIRK_BOOT_MASK 0x80 +#define QUIRK_BOOT_EXTIGY (QUIRK_BOOT_MASK | 0) + +typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t; +typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t; + +struct snd_usb_audio_quirk { + const char *vendor_name; + const char *product_name; + int16_t ifnum; + int16_t type; + const void *data; +}; + +/* data for QUIRK_MIDI_FIXED_ENDPOINT */ +struct snd_usb_midi_endpoint_info { + int8_t out_ep, in_ep; /* ep number, 0 autodetect */ + uint16_t out_cables; /* bitmask */ + uint16_t in_cables; /* bitmask */ +}; + +/* for QUIRK_MIDI_YAMAHA, data is NULL */ + +/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info + * structure (out_cables and in_cables only) */ + +/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk + * structures, terminated with .ifnum = -1 */ + +/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */ + +/* for QUIRK_STANDARD_INTERFACE, data is NULL */ + +/* + */ + +#define combine_word(s) ((*s) | ((unsigned int)(s)[1] << 8)) +#define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16)) +#define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24)) + +unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size); + +void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype); +void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype); + +int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif); + +int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk); +void snd_usbmidi_disconnect(struct list_head *p); + +/* + * retrieve usb_interface descriptor from the host interface + * (conditional for compatibility with the older API) + */ +#ifndef get_iface_desc +#define get_iface_desc(iface) (&iface->desc) +#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc) +#define get_ep_desc(ep) (&(ep)->desc) +#define get_cfg_desc(cfg) (&(cfg)->desc) +#endif + +#ifndef usb_pipe_needs_resubmit +#define usb_pipe_needs_resubmit(pipe) 1 +#endif + +#ifndef snd_usb_complete_callback +#define snd_usb_complete_callback(x) (x) +#endif + +#endif /* __USBAUDIO_H */ diff -urN linux-2.4.21-rc1.orig/sound/usb/usbmidi.c linux/sound/usb/usbmidi.c --- linux-2.4.21-rc1.orig/sound/usb/usbmidi.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/usbmidi.c 2003-03-05 04:33:21.000000000 -0700 @@ -0,0 +1,1104 @@ +/* + * usbmidi.c - ALSA USB MIDI driver + * + * Copyright (c) 2002 Clemens Ladisch + * All rights reserved. + * + * Based on the OSS usb-midi driver by NAGANO Daisuke, + * NetBSD's umidi driver by Takuya SHIOZAKI, + * the "USB Device Class Definition for MIDI Devices" by Roland + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed and/or modified under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbaudio.h" + +struct usb_ms_header_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bcdMSC[2]; + __u16 wTotalLength; +} __attribute__ ((packed)); + +struct usb_ms_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bNumEmbMIDIJack; + __u8 baAssocJackID[0]; +} __attribute__ ((packed)); + +typedef struct snd_usb_midi snd_usb_midi_t; +typedef struct snd_usb_midi_endpoint snd_usb_midi_endpoint_t; +typedef struct snd_usb_midi_out_endpoint snd_usb_midi_out_endpoint_t; +typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t; +typedef struct usbmidi_out_port usbmidi_out_port_t; +typedef struct usbmidi_in_port usbmidi_in_port_t; + +struct snd_usb_midi { + snd_usb_audio_t *chip; + struct usb_interface *iface; + const snd_usb_audio_quirk_t *quirk; + snd_rawmidi_t* rmidi; + struct list_head list; + + struct snd_usb_midi_endpoint { + snd_usb_midi_out_endpoint_t *out; + snd_usb_midi_in_endpoint_t *in; + } endpoints[MIDI_MAX_ENDPOINTS]; +}; + +struct snd_usb_midi_out_endpoint { + snd_usb_midi_t* umidi; + struct urb* urb; + int max_transfer; /* size of urb buffer */ + struct tasklet_struct tasklet; + + spinlock_t buffer_lock; + + struct usbmidi_out_port { + snd_usb_midi_out_endpoint_t* ep; + snd_rawmidi_substream_t* substream; + int active; + uint8_t cable; /* cable number << 4 */ + uint8_t state; +#define STATE_UNKNOWN 0 +#define STATE_1PARAM 1 +#define STATE_2PARAM_1 2 +#define STATE_2PARAM_2 3 +#define STATE_SYSEX_0 4 +#define STATE_SYSEX_1 5 +#define STATE_SYSEX_2 6 + uint8_t data[2]; + } ports[0x10]; +}; + +struct snd_usb_midi_in_endpoint { + snd_usb_midi_t* umidi; + struct urb* urb; + struct usbmidi_in_port { + snd_rawmidi_substream_t* substream; + } ports[0x10]; +}; + +static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep); + +static const uint8_t snd_usbmidi_cin_length[] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +}; + +/* + * Submits the URB, with error handling. + */ +static int snd_usbmidi_submit_urb(struct urb* urb, int flags) +{ + int err = usb_submit_urb(urb, flags); + if (err < 0 && err != -ENODEV) + snd_printk(KERN_ERR "usb_submit_urb: %d\n", err); + return err; +} + +/* + * Error handling for URB completion functions. + */ +static int snd_usbmidi_urb_error(int status) +{ + if (status == -ENOENT) + return status; /* killed */ + if (status == -ENODEV || + status == -EILSEQ || + status == -ETIMEDOUT) + return -ENODEV; /* device removed */ + snd_printk(KERN_ERR "urb status %d\n", status); + return 0; /* continue */ +} + +/* + * Receives a USB MIDI packet. + */ +static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep, + uint8_t packet[4]) +{ + int cable = packet[0] >> 4; + usbmidi_in_port_t* port = &ep->ports[cable]; + + if (!port->substream) { + snd_printd("unexpected port %d!\n", cable); + return; + } + if (!port->substream->runtime || + !port->substream->runtime->trigger) + return; + snd_rawmidi_receive(port->substream, &packet[1], + snd_usbmidi_cin_length[packet[0] & 0x0f]); +} + +/* + * Processes the data read from the device. + */ +static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs) +{ + snd_usb_midi_in_endpoint_t* ep = snd_magic_cast(snd_usb_midi_in_endpoint_t, urb->context, return); + + if (urb->status == 0) { + uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer; + int i; + + for (i = 0; i + 4 <= urb->actual_length; i += 4) + if (buffer[i] != 0) + snd_usbmidi_input_packet(ep, &buffer[i]); + } else { + if (snd_usbmidi_urb_error(urb->status) < 0) + return; + } + + if (usb_pipe_needs_resubmit(urb->pipe)) { + urb->dev = ep->umidi->chip->dev; + snd_usbmidi_submit_urb(urb, GFP_ATOMIC); + } +} + +/* + * Converts the data read from a Midiman device to standard USB MIDI packets. + */ +static void snd_usbmidi_in_midiman_complete(struct urb* urb, struct pt_regs *regs) +{ + if (urb->status == 0) { + uint8_t* buffer = (uint8_t*)urb->transfer_buffer; + int i; + + for (i = 0; i + 4 <= urb->actual_length; i += 4) { + if (buffer[i + 3] != 0) { + /* + * snd_usbmidi_input_packet() doesn't check the + * contents of the message, so we simply use + * some random CIN with the desired length. + */ + static const uint8_t cin[4] = { + 0x0, 0xf, 0x2, 0x3 + }; + uint8_t ctl = buffer[i + 3]; + buffer[i + 3] = buffer[i + 2]; + buffer[i + 2] = buffer[i + 1]; + buffer[i + 1] = buffer[i + 0]; + buffer[i + 0] = (ctl & 0xf0) | cin[ctl & 3]; + } else { + buffer[i + 0] = 0; + } + } + } + snd_usbmidi_in_urb_complete(urb, regs); +} + +static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs) +{ + snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return); + + if (urb->status < 0) { + if (snd_usbmidi_urb_error(urb->status) < 0) + return; + } + snd_usbmidi_do_output(ep); +} + +/* + * Converts standard USB MIDI packets to what Midman devices expect. + */ +static void snd_usbmidi_convert_to_midiman(struct urb* urb) +{ + uint8_t* buffer = (uint8_t*)urb->transfer_buffer; + int i; + + for (i = 0; i + 4 <= urb->transfer_buffer_length; i += 4) { + uint8_t cin = buffer[i]; + buffer[i + 0] = buffer[i + 1]; + buffer[i + 1] = buffer[i + 2]; + buffer[i + 2] = buffer[i + 3]; + buffer[i + 3] = (cin & 0xf0) | snd_usbmidi_cin_length[cin & 0x0f]; + } +} + +/* + * Adds one USB MIDI packet to the output buffer. + */ +static inline void output_packet(struct urb* urb, + uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3) +{ + + uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length; + buf[0] = p0; + buf[1] = p1; + buf[2] = p2; + buf[3] = p3; + urb->transfer_buffer_length += 4; +} + +/* + * Converts MIDI commands to USB MIDI packets. + */ +static void snd_usbmidi_transmit_byte(usbmidi_out_port_t* port, + uint8_t b, struct urb* urb) +{ + uint8_t p0 = port->cable; + + if (b >= 0xf8) { + output_packet(urb, p0 | 0x0f, b, 0, 0); + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + port->state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + port->state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + port->state = STATE_UNKNOWN; + break; + case 0xf6: + output_packet(urb, p0 | 0x05, 0xf6, 0, 0); + port->state = STATE_UNKNOWN; + break; + case 0xf7: + switch (port->state) { + case STATE_SYSEX_0: + output_packet(urb, p0 | 0x05, 0xf7, 0, 0); + break; + case STATE_SYSEX_1: + output_packet(urb, p0 | 0x06, port->data[0], 0xf7, 0); + break; + case STATE_SYSEX_2: + output_packet(urb, p0 | 0x07, port->data[0], port->data[1], 0xf7); + break; + } + port->state = STATE_UNKNOWN; + break; + } + } else if (b >= 0x80) { + port->data[0] = b; + if (b >= 0xc0 && b <= 0xdf) + port->state = STATE_1PARAM; + else + port->state = STATE_2PARAM_1; + } else { /* b < 0x80 */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + } else { + p0 |= 0x02; + port->state = STATE_UNKNOWN; + } + output_packet(urb, p0, port->data[0], b, 0); + break; + case STATE_2PARAM_1: + port->data[1] = b; + port->state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + port->state = STATE_2PARAM_1; + } else { + p0 |= 0x03; + port->state = STATE_UNKNOWN; + } + output_packet(urb, p0, port->data[0], port->data[1], b); + break; + case STATE_SYSEX_0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + port->state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + output_packet(urb, p0 | 0x04, port->data[0], port->data[1], b); + port->state = STATE_SYSEX_0; + break; + } + } +} + +/* + * Moves data from one substream buffer to the URB transfer buffer. + */ +static void snd_usbmidi_transmit(snd_usb_midi_out_endpoint_t* ep, int port_idx) +{ + struct urb* urb = ep->urb; + usbmidi_out_port_t* port = &ep->ports[port_idx]; + + while (urb->transfer_buffer_length < ep->max_transfer) { + uint8_t b; + if (snd_rawmidi_transmit_peek(port->substream, &b, 1) != 1) { + port->active = 0; + break; + } + snd_usbmidi_transmit_byte(port, b, urb); + snd_rawmidi_transmit_ack(port->substream, 1); + } +} + +/* + * This is called when some data should be transferred to the device + * (from one or more substreams). + */ +static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep) +{ + int p; + struct urb* urb = ep->urb; + unsigned long flags; + + spin_lock_irqsave(&ep->buffer_lock, flags); + if (urb->status == -EINPROGRESS || ep->umidi->chip->shutdown) { + spin_unlock_irqrestore(&ep->buffer_lock, flags); + return; + } + + urb->transfer_buffer_length = 0; + for (p= 0; p < 0x10; ++p) + if (ep->ports[p].active) + snd_usbmidi_transmit(ep, p); + + if (urb->transfer_buffer_length > 0) { + if (ep->umidi->quirk && ep->umidi->quirk->type == QUIRK_MIDI_MIDIMAN) + snd_usbmidi_convert_to_midiman(urb); + + urb->dev = ep->umidi->chip->dev; + snd_usbmidi_submit_urb(urb, GFP_ATOMIC); + } + spin_unlock_irqrestore(&ep->buffer_lock, flags); +} + +static void snd_usbmidi_out_tasklet(unsigned long data) +{ + snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, (void*)data, return); + + snd_usbmidi_do_output(ep); +} + +static int snd_usbmidi_output_open(snd_rawmidi_substream_t* substream) +{ + snd_usb_midi_t* umidi = snd_magic_cast(snd_usb_midi_t, substream->rmidi->private_data, return -ENXIO); + usbmidi_out_port_t* port = NULL; + int i, j; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) + if (umidi->endpoints[i].out) + for (j = 0; j < 0x10; ++j) + if (umidi->endpoints[i].out->ports[j].substream == substream) { + port = &umidi->endpoints[i].out->ports[j]; + break; + } + if (!port) { + snd_BUG(); + return -ENXIO; + } + substream->runtime->private_data = port; + port->state = STATE_UNKNOWN; + return 0; +} + +static int snd_usbmidi_output_close(snd_rawmidi_substream_t* substream) +{ + return 0; +} + +static void snd_usbmidi_output_trigger(snd_rawmidi_substream_t* substream, int up) +{ + usbmidi_out_port_t* port = (usbmidi_out_port_t*)substream->runtime->private_data; + + port->active = up; + if (up) + tasklet_hi_schedule(&port->ep->tasklet); +} + +static int snd_usbmidi_input_open(snd_rawmidi_substream_t* substream) +{ + return 0; +} + +static int snd_usbmidi_input_close(snd_rawmidi_substream_t* substream) +{ + return 0; +} + +static void snd_usbmidi_input_trigger(snd_rawmidi_substream_t* substream, int up) +{ +} + +static snd_rawmidi_ops_t snd_usbmidi_output_ops = { + .open = snd_usbmidi_output_open, + .close = snd_usbmidi_output_close, + .trigger = snd_usbmidi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_usbmidi_input_ops = { + .open = snd_usbmidi_input_open, + .close = snd_usbmidi_input_close, + .trigger = snd_usbmidi_input_trigger +}; + +/* + * Frees an input endpoint. + * May be called when ep hasn't been initialized completely. + */ +static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep) +{ + if (ep->urb) { + if (ep->urb->transfer_buffer) + kfree(ep->urb->transfer_buffer); + usb_free_urb(ep->urb); + } + snd_magic_kfree(ep); +} + +/* + * For Roland devices, use the alternate setting which uses interrupt + * transfers for input. + */ +static struct usb_endpoint_descriptor* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + + if (umidi->chip->dev->descriptor.idVendor != 0x0582) + return NULL; + intf = umidi->iface; + if (!intf || intf->num_altsetting != 2) + return NULL; + + hostif = &intf->altsetting[0]; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints != 2 || + (get_endpoint(hostif, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK || + (get_endpoint(hostif, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) + return NULL; + + hostif = &intf->altsetting[1]; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints != 2 || + (get_endpoint(hostif, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK || + (get_endpoint(hostif, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + return NULL; + + snd_printdd(KERN_INFO "switching to altsetting %d with int ep\n", + intfd->bAlternateSetting); + usb_set_interface(umidi->chip->dev, intfd->bInterfaceNumber, + intfd->bAlternateSetting); + return get_endpoint(hostif, 1); +} + +static struct usb_endpoint_descriptor* snd_usbmidi_get_midiman_int_epd(snd_usb_midi_t* umidi) +{ + struct usb_interface* intf = umidi->iface; + struct usb_host_interface *hostif; + struct usb_interface_descriptor *intfd; + if (!intf) + return NULL; + hostif = &intf->altsetting[0]; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints < 1) + return NULL; + return get_endpoint(hostif, 0); +} + +/* + * Creates an input endpoint. + */ +static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* ep_info, + snd_usb_midi_endpoint_t* rep) +{ + snd_usb_midi_in_endpoint_t* ep; + struct usb_endpoint_descriptor* int_epd; + void* buffer; + unsigned int pipe; + int length; + + rep->in = NULL; + ep = snd_magic_kcalloc(snd_usb_midi_in_endpoint_t, 0, GFP_KERNEL); + if (!ep) + return -ENOMEM; + ep->umidi = umidi; + + if (umidi->quirk && umidi->quirk->type == QUIRK_MIDI_MIDIMAN) + int_epd = snd_usbmidi_get_midiman_int_epd(umidi); + else + int_epd = snd_usbmidi_get_int_epd(umidi); + + ep->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ep->urb) { + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; + } + if (int_epd) + pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep); + else + pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep); + length = usb_maxpacket(umidi->chip->dev, pipe, 0); + buffer = kmalloc(length, GFP_KERNEL); + if (!buffer) { + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; + } + if (int_epd) + usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, + snd_usb_complete_callback(snd_usbmidi_in_urb_complete), + ep, int_epd->bInterval); + else + usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, + snd_usb_complete_callback(snd_usbmidi_in_urb_complete), + ep); + + rep->in = ep; + return 0; +} + +static int snd_usbmidi_count_bits(uint16_t x) +{ + int i, bits = 0; + + for (i = 0; i < 16; ++i) + bits += (x & (1 << i)) != 0; + return bits; +} + +/* + * Frees an output endpoint. + * May be called when ep hasn't been initialized completely. + */ +static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep) +{ + if (ep->tasklet.func) + tasklet_kill(&ep->tasklet); + if (ep->urb) { + if (ep->urb->transfer_buffer) + kfree(ep->urb->transfer_buffer); + usb_free_urb(ep->urb); + } + snd_magic_kfree(ep); +} + +/* + * Creates an output endpoint, and initializes output ports. + */ +static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* ep_info, + snd_usb_midi_endpoint_t* rep) +{ + snd_usb_midi_out_endpoint_t* ep; + int i; + unsigned int pipe; + void* buffer; + + rep->out = NULL; + ep = snd_magic_kcalloc(snd_usb_midi_out_endpoint_t, 0, GFP_KERNEL); + if (!ep) + return -ENOMEM; + ep->umidi = umidi; + + ep->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ep->urb) { + snd_usbmidi_out_endpoint_delete(ep); + return -ENOMEM; + } + pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep); + ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1) & ~3; + buffer = kmalloc(ep->max_transfer, GFP_KERNEL); + if (!buffer) { + snd_usbmidi_out_endpoint_delete(ep); + return -ENOMEM; + } + usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, + ep->max_transfer, + snd_usb_complete_callback(snd_usbmidi_out_urb_complete), ep); + + spin_lock_init(&ep->buffer_lock); + tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); + + for (i = 0; i < 0x10; ++i) + if (ep_info->out_cables & (1 << i)) { + ep->ports[i].ep = ep; + ep->ports[i].cable = i << 4; + } + + rep->out = ep; + return 0; +} + +/* + * Frees everything. + */ +static void snd_usbmidi_free(snd_usb_midi_t* umidi) +{ + int i; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + if (ep->out) + snd_usbmidi_out_endpoint_delete(ep->out); + if (ep->in) + snd_usbmidi_in_endpoint_delete(ep->in); + } + snd_magic_kfree(umidi); +} + +/* + * Unlinks all URBs (must be done before the usb_device is deleted). + */ +void snd_usbmidi_disconnect(struct list_head* p) +{ + snd_usb_midi_t* umidi; + int i; + + umidi = list_entry(p, snd_usb_midi_t, list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + if (ep->out && ep->out->urb) + usb_unlink_urb(ep->out->urb); + if (ep->in && ep->in->urb) + usb_unlink_urb(ep->in->urb); + } +} + +static void snd_usbmidi_rawmidi_free(snd_rawmidi_t* rmidi) +{ + snd_usb_midi_t* umidi = snd_magic_cast(snd_usb_midi_t, rmidi->private_data, return); + snd_usbmidi_free(umidi); +} + +static snd_rawmidi_substream_t* snd_usbmidi_find_substream(snd_usb_midi_t* umidi, + int stream, int number) +{ + struct list_head* list; + + list_for_each(list, &umidi->rmidi->streams[stream].substreams) { + snd_rawmidi_substream_t* substream = list_entry(list, snd_rawmidi_substream_t, list); + if (substream->number == number) + return substream; + } + return NULL; +} + +static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, + int stream, int number, + snd_rawmidi_substream_t** rsubstream) +{ + snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number); + if (!substream) { + snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number); + return; + } + /* TODO: read port name from jack descriptor */ + snprintf(substream->name, sizeof(substream->name), + "%s Port %d", umidi->chip->card->shortname, number); + *rsubstream = substream; +} + +/* + * Creates the endpoints and their ports. + */ +static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* endpoints) +{ + int i, j, err; + int out_ports = 0, in_ports = 0; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + if (endpoints[i].out_cables) { + err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i], + &umidi->endpoints[i]); + if (err < 0) + return err; + } + if (endpoints[i].in_cables) { + err = snd_usbmidi_in_endpoint_create(umidi, &endpoints[i], + &umidi->endpoints[i]); + if (err < 0) + return err; + } + + for (j = 0; j < 0x10; ++j) { + if (endpoints[i].out_cables & (1 << j)) { + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_OUTPUT, out_ports, + &umidi->endpoints[i].out->ports[j].substream); + ++out_ports; + } + if (endpoints[i].in_cables & (1 << j)) { + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_INPUT, in_ports, + &umidi->endpoints[i].in->ports[j].substream); + ++in_ports; + } + } + } + snd_printdd(KERN_INFO "created %d output and %d input ports\n", + out_ports, in_ports); + return 0; +} + +/* + * Returns MIDIStreaming device capabilities. + */ +static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* endpoints) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + struct usb_ms_header_descriptor* ms_header; + struct usb_host_endpoint *hostep; + struct usb_endpoint_descriptor* ep; + struct usb_ms_endpoint_descriptor* ms_ep; + int i, epidx; + + intf = umidi->iface; + if (!intf) + return -ENXIO; + hostif = &intf->altsetting[0]; + intfd = get_iface_desc(hostif); + ms_header = (struct usb_ms_header_descriptor*)hostif->extra; + if (hostif->extralen >= 7 && + ms_header->bLength >= 7 && + ms_header->bDescriptorType == USB_DT_CS_INTERFACE && + ms_header->bDescriptorSubtype == HEADER) + snd_printdd(KERN_INFO "MIDIStreaming version %02x.%02x\n", + ms_header->bcdMSC[1], ms_header->bcdMSC[0]); + else + snd_printk(KERN_WARNING "MIDIStreaming interface descriptor not found\n"); + + epidx = 0; + for (i = 0; i < intfd->bNumEndpoints; ++i) { + hostep = &hostif->endpoint[i]; + ep = get_ep_desc(hostep); + if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) + continue; + ms_ep = (struct usb_ms_endpoint_descriptor*)hostep->extra; + if (hostep->extralen < 4 || + ms_ep->bLength < 4 || + ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || + ms_ep->bDescriptorSubtype != MS_GENERAL) + continue; + if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + if (endpoints[epidx].out_ep) { + if (++epidx >= MIDI_MAX_ENDPOINTS) { + snd_printk(KERN_WARNING "too many endpoints\n"); + break; + } + } + endpoints[epidx].out_ep = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; + snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", + ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); + } else { + if (endpoints[epidx].in_ep) { + if (++epidx >= MIDI_MAX_ENDPOINTS) { + snd_printk(KERN_WARNING "too many endpoints\n"); + break; + } + } + endpoints[epidx].in_ep = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; + snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", + ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); + } + } + return 0; +} + +/* + * If the endpoints aren't specified, use the first bulk endpoints in the + * first alternate setting of the interface. + */ +static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* endpoint) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + struct usb_endpoint_descriptor* epd; + int i; + + intf = umidi->iface; + if (!intf || intf->num_altsetting < 1) + return -ENOENT; + hostif = intf->altsetting; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints < 1) + return -ENOENT; + + for (i = 0; i < intfd->bNumEndpoints; ++i) { + epd = get_endpoint(hostif, i); + if ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) + continue; + if (!endpoint->out_ep && endpoint->out_cables && + (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) + endpoint->out_ep = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + if (!endpoint->in_ep && endpoint->in_cables && + (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + endpoint->in_ep = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + } + return 0; +} + +/* + * Detects the endpoints and ports of Yamaha devices. + */ +static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* endpoint) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + uint8_t* cs_desc; + + intf = umidi->iface; + if (!intf) + return -ENOENT; + hostif = intf->altsetting; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints < 1) + return -ENOENT; + + for (cs_desc = hostif->extra; + cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2; + cs_desc += cs_desc[0]) { + if (cs_desc[1] == CS_AUDIO_INTERFACE) { + if (cs_desc[2] == MIDI_IN_JACK) + endpoint->in_cables = (endpoint->in_cables << 1) | 1; + else if (cs_desc[2] == MIDI_OUT_JACK) + endpoint->out_cables = (endpoint->out_cables << 1) | 1; + } + } + if (!endpoint->in_cables && !endpoint->out_cables) + return -ENOENT; + + return snd_usbmidi_detect_endpoint(umidi, endpoint); +} + +/* + * Creates the endpoints and their ports for Midiman devices. + */ +static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, + snd_usb_midi_endpoint_info_t* endpoint) +{ + snd_usb_midi_endpoint_info_t ep_info; + struct usb_interface* intf; + struct usb_host_interface *hostif; + struct usb_interface_descriptor* intfd; + struct usb_endpoint_descriptor* epd; + int cable, err; + + intf = umidi->iface; + if (!intf) + return -ENOENT; + hostif = intf->altsetting; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints < (endpoint->out_cables > 0x0001 ? 5 : 3)) { + snd_printdd(KERN_ERR "not enough endpoints\n"); + return -ENOENT; + } + + epd = get_endpoint(hostif, 0); + if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN || + (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) { + snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n"); + return -ENXIO; + } + epd = get_endpoint(hostif, 2); + if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT || + (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n"); + return -ENXIO; + } + if (endpoint->out_cables > 0x0001) { + epd = get_endpoint(hostif, 4); + if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT || + (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n"); + return -ENXIO; + } + } + + ep_info.out_ep = get_endpoint(hostif, 2)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_cables = endpoint->out_cables & 0x5555; + err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); + if (err < 0) + return err; + + ep_info.in_ep = get_endpoint(hostif, 0)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.in_cables = endpoint->in_cables; + err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); + if (err < 0) + return err; + umidi->endpoints[0].in->urb->complete = snd_usb_complete_callback(snd_usbmidi_in_midiman_complete); + + if (endpoint->out_cables > 0x0001) { + ep_info.out_ep = get_endpoint(hostif, 4)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_cables = endpoint->out_cables & 0xaaaa; + err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[1]); + if (err < 0) + return err; + } + + for (cable = 0; cable < 0x10; ++cable) { + if (endpoint->out_cables & (1 << cable)) + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_OUTPUT, cable, + &umidi->endpoints[cable & 1].out->ports[cable].substream); + if (endpoint->in_cables & (1 << cable)) + snd_usbmidi_init_substream(umidi, SNDRV_RAWMIDI_STREAM_INPUT, cable, + &umidi->endpoints[0].in->ports[cable].substream); + } + return 0; +} + +static int snd_usbmidi_create_rawmidi(snd_usb_midi_t* umidi, + int out_ports, int in_ports) +{ + snd_rawmidi_t* rmidi; + int err; + + err = snd_rawmidi_new(umidi->chip->card, "USB MIDI", + umidi->chip->next_midi_device++, + out_ports, in_ports, &rmidi); + if (err < 0) + return err; + strcpy(rmidi->name, umidi->chip->card->shortname); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = umidi; + rmidi->private_free = snd_usbmidi_rawmidi_free; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input_ops); + + umidi->rmidi = rmidi; + return 0; +} + +/* + * Creates and registers everything needed for a MIDI streaming interface. + */ +int snd_usb_create_midi_interface(snd_usb_audio_t* chip, + struct usb_interface* iface, + const snd_usb_audio_quirk_t* quirk) +{ + snd_usb_midi_t* umidi; + snd_usb_midi_endpoint_info_t endpoints[MIDI_MAX_ENDPOINTS]; + int out_ports, in_ports; + int i, err; + + umidi = snd_magic_kcalloc(snd_usb_midi_t, 0, GFP_KERNEL); + if (!umidi) + return -ENOMEM; + umidi->chip = chip; + umidi->iface = iface; + umidi->quirk = quirk; + + /* detect the endpoint(s) to use */ + memset(endpoints, 0, sizeof(endpoints)); + if (!quirk) { + err = snd_usbmidi_get_ms_info(umidi, endpoints); + } else { + switch (quirk->type) { + case QUIRK_MIDI_FIXED_ENDPOINT: + memcpy(&endpoints[0], quirk->data, + sizeof(snd_usb_midi_endpoint_info_t)); + err = snd_usbmidi_detect_endpoint(umidi, &endpoints[0]); + break; + case QUIRK_MIDI_YAMAHA: + err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]); + break; + case QUIRK_MIDI_MIDIMAN: + memcpy(&endpoints[0], quirk->data, + sizeof(snd_usb_midi_endpoint_info_t)); + err = 0; + break; + default: + snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); + err = -ENXIO; + break; + } + } + if (err < 0) { + snd_magic_kfree(umidi); + return err; + } + + /* create rawmidi device */ + out_ports = 0; + in_ports = 0; + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + out_ports += snd_usbmidi_count_bits(endpoints[i].out_cables); + in_ports += snd_usbmidi_count_bits(endpoints[i].in_cables); + } + err = snd_usbmidi_create_rawmidi(umidi, out_ports, in_ports); + if (err < 0) { + snd_magic_kfree(umidi); + return err; + } + + /* create endpoint/port structures */ + if (quirk && quirk->type == QUIRK_MIDI_MIDIMAN) + err = snd_usbmidi_create_endpoints_midiman(umidi, &endpoints[0]); + else + err = snd_usbmidi_create_endpoints(umidi, endpoints); + if (err < 0) { + snd_usbmidi_free(umidi); + return err; + } + + list_add(&umidi->list, &umidi->chip->midi_list); + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) + if (umidi->endpoints[i].in) + snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb, + GFP_KERNEL); + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/usb/usbmixer.c linux/sound/usb/usbmixer.c --- linux-2.4.21-rc1.orig/sound/usb/usbmixer.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/usbmixer.c 2003-02-28 07:54:40.000000000 -0700 @@ -0,0 +1,1505 @@ +/* + * (Tentative) USB Audio Driver for ALSA + * + * Mixer control part + * + * Copyright (c) 2002 by Takashi Iwai + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbaudio.h" + + +/* + */ + +/* ignore error from controls - for debugging */ +/* #define IGNORE_CTL_ERROR */ + +typedef struct usb_mixer_build mixer_build_t; +typedef struct usb_audio_term usb_audio_term_t; +typedef struct usb_mixer_elem_info usb_mixer_elem_info_t; + + +struct usb_audio_term { + int id; + int type; + int channels; + unsigned int chconfig; + int name; +}; + +struct usbmix_name_map; + +struct usb_mixer_build { + snd_usb_audio_t *chip; + unsigned char *buffer; + unsigned int buflen; + unsigned int ctrlif; + unsigned short vendor; + unsigned short product; + DECLARE_BITMAP(unitbitmap, 32*32); + usb_audio_term_t oterm; + const struct usbmix_name_map *map; +}; + +struct usb_mixer_elem_info { + snd_usb_audio_t *chip; + unsigned int ctrlif; + unsigned int id; + unsigned int control; /* CS or ICN (high byte) */ + unsigned int cmask; /* channel mask bitmap: 0 = master */ + int channels; + int val_type; + int min, max, res; + unsigned int initialized: 1; +}; + + +enum { + USB_FEATURE_NONE = 0, + USB_FEATURE_MUTE = 1, + USB_FEATURE_VOLUME, + USB_FEATURE_BASS, + USB_FEATURE_MID, + USB_FEATURE_TREBLE, + USB_FEATURE_GEQ, + USB_FEATURE_AGC, + USB_FEATURE_DELAY, + USB_FEATURE_BASSBOOST, + FSB_FEATURE_LOUDNESS +}; + +enum { + USB_MIXER_BOOLEAN, + USB_MIXER_INV_BOOLEAN, + USB_MIXER_S8, + USB_MIXER_U8, + USB_MIXER_S16, + USB_MIXER_U16, +}; + +enum { + USB_PROC_UPDOWN = 1, + USB_PROC_UPDOWN_SWITCH = 1, + USB_PROC_UPDOWN_MODE_SEL = 2, + + USB_PROC_PROLOGIC = 2, + USB_PROC_PROLOGIC_SWITCH = 1, + USB_PROC_PROLOGIC_MODE_SEL = 2, + + USB_PROC_3DENH = 3, + USB_PROC_3DENH_SWITCH = 1, + USB_PROC_3DENH_SPACE = 2, + + USB_PROC_REVERB = 4, + USB_PROC_REVERB_SWITCH = 1, + USB_PROC_REVERB_LEVEL = 2, + USB_PROC_REVERB_TIME = 3, + USB_PROC_REVERB_DELAY = 4, + + USB_PROC_CHORUS = 5, + USB_PROC_CHORUS_SWITCH = 1, + USB_PROC_CHORUS_LEVEL = 2, + USB_PROC_CHORUS_RATE = 3, + USB_PROC_CHORUS_DEPTH = 4, + + USB_PROC_DCR = 6, + USB_PROC_DCR_SWITCH = 1, + USB_PROC_DCR_RATIO = 2, + USB_PROC_DCR_MAX_AMP = 3, + USB_PROC_DCR_THRESHOLD = 4, + USB_PROC_DCR_ATTACK = 5, + USB_PROC_DCR_RELEASE = 6, +}; + +#define MAX_CHANNELS 10 /* max logical channels */ + + +/* + * manual mapping of mixer names + * if the mixer topology is too complicated and the parsed names are + * ambiguous, add the entries in usbmixer_maps.c. + */ +#include "usbmixer_maps.c" + +/* get the mapped name if the unit matches */ +static int check_mapped_name(mixer_build_t *state, int unitid, int control, char *buf, int buflen) +{ + const struct usbmix_name_map *p; + + if (! state->map) + return 0; + + for (p = state->map; p->id; p++) { + if (p->id == unitid && + (! control || ! p->control || control == p->control)) { + buflen--; + strncpy(buf, p->name, buflen); + buf[buflen] = 0; + return strlen(buf); + } + } + return 0; +} + + +/* + * find an audio control unit with the given unit id + */ +static void *find_audio_control_unit(mixer_build_t *state, unsigned char unit) +{ + unsigned char *p; + + p = NULL; + while ((p = snd_usb_find_desc(state->buffer, state->buflen, p, + USB_DT_CS_INTERFACE)) != NULL) { + if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit) + return p; + } + return NULL; +} + + +/* + * copy a string with the given id + */ +static int snd_usb_copy_string_desc(mixer_build_t *state, int index, char *buf, int maxlen) +{ + int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + buf[len] = 0; + return len; +} + +/* + * convert from the byte/word on usb descriptor to the zero-based integer + */ +static int convert_signed_value(usb_mixer_elem_info_t *cval, int val) +{ + switch (cval->val_type) { + case USB_MIXER_BOOLEAN: + return !!val; + case USB_MIXER_INV_BOOLEAN: + return !val; + case USB_MIXER_U8: + val &= 0xff; + break; + case USB_MIXER_S8: + val &= 0xff; + if (val >= 0x80) + val -= 0x100; + break; + case USB_MIXER_U16: + val &= 0xffff; + break; + case USB_MIXER_S16: + val &= 0xffff; + if (val >= 0x8000) + val -= 0x10000; + break; + } + return val; +} + +/* + * convert from the zero-based int to the byte/word for usb descriptor + */ +static int convert_bytes_value(usb_mixer_elem_info_t *cval, int val) +{ + switch (cval->val_type) { + case USB_MIXER_BOOLEAN: + return !!val; + case USB_MIXER_INV_BOOLEAN: + return !val; + case USB_MIXER_S8: + case USB_MIXER_U8: + return val & 0xff; + case USB_MIXER_S16: + case USB_MIXER_U16: + return val & 0xffff; + } + return 0; /* not reached */ +} + +static int get_relative_value(usb_mixer_elem_info_t *cval, int val) +{ + if (! cval->res) + cval->res = 1; + if (val < cval->min) + return 0; + else if (val > cval->max) + return (cval->max - cval->min) / cval->res; + else + return (val - cval->min) / cval->res; +} + +static int get_abs_value(usb_mixer_elem_info_t *cval, int val) +{ + if (val < 0) + return cval->min; + if (! cval->res) + cval->res = 1; + val *= cval->res; + val += cval->min; + if (val > cval->max) + return cval->max; + return val; +} + + +/* + * retrieve a mixer value + */ + +static int get_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, int *value_ret) +{ + unsigned char buf[2]; + int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; + int timeout = 10; + + while (timeout-- > 0) { + if (usb_control_msg(cval->chip->dev, usb_rcvctrlpipe(cval->chip->dev, 0), + request, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, cval->ctrlif | (cval->id << 8), + buf, val_len, HZ / 10) >= 0) { + *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); + return 0; + } + } + snd_printdd(KERN_ERR "cannot get ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type); + return -EINVAL; +} + +static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value) +{ + return get_ctl_value(cval, GET_CUR, validx, value); +} + +/* channel = 0: master, 1 = first channel */ +inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value) +{ + return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value); +} + +/* + * set a mixer value + */ + +static int set_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, int value_set) +{ + unsigned char buf[2]; + int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; + int timeout = 10; + + value_set = convert_bytes_value(cval, value_set); + buf[0] = value_set & 0xff; + buf[1] = (value_set >> 8) & 0xff; + while (timeout -- > 0) + if (usb_control_msg(cval->chip->dev, usb_sndctrlpipe(cval->chip->dev, 0), + request, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + validx, cval->ctrlif | (cval->id << 8), + buf, val_len, HZ / 10) >= 0) + return 0; + snd_printdd(KERN_ERR "cannot set ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d, data = 0x%x/0x%x\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]); + return -EINVAL; +} + +static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value) +{ + return set_ctl_value(cval, SET_CUR, validx, value); +} + +inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value) +{ + return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value); +} + + +/* + * parser routines begin here... + */ + +static int parse_audio_unit(mixer_build_t *state, int unitid); + + +/* + * check if the input/output channel routing is enabled on the given bitmap. + * used for mixer unit parser + */ +static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_outs) +{ + int idx = ich * num_outs + och; + return bmap[-(idx >> 3)] & (0x80 >> (idx & 7)); +} + + +/* + * add an alsa control element + * search and increment the index until an empty slot is found. + * + * if failed, give up and free the control instance. + */ + +static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl) +{ + int err; + while (snd_ctl_find_id(card, &kctl->id)) + kctl->id.index++; + if ((err = snd_ctl_add(card, kctl)) < 0) { + snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); + snd_ctl_free_one(kctl); + } + return err; +} + + +/* + * get a terminal name string + */ + +static struct iterm_name_combo { + int type; + char *name; +} iterm_names[] = { + { 0x0300, "Output" }, + { 0x0301, "Speaker" }, + { 0x0302, "Headphone" }, + { 0x0303, "HMD Audio" }, + { 0x0304, "Desktop Speaker" }, + { 0x0305, "Room Speaker" }, + { 0x0306, "Com Speaker" }, + { 0x0307, "LFE" }, + { 0x0600, "External In" }, + { 0x0601, "Analog In" }, + { 0x0602, "Digital In" }, + { 0x0603, "Line" }, + { 0x0604, "Legacy In" }, + { 0x0605, "IEC958 In" }, + { 0x0606, "1394 DA Stream" }, + { 0x0607, "1394 DV Stream" }, + { 0x0700, "Embedded" }, + { 0x0701, "Noise Source" }, + { 0x0702, "Equalization Noise" }, + { 0x0703, "CD" }, + { 0x0704, "DAT" }, + { 0x0705, "DCC" }, + { 0x0706, "MiniDisk" }, + { 0x0707, "Analog Tape" }, + { 0x0708, "Phonograph" }, + { 0x0709, "VCR Audio" }, + { 0x070a, "Video Disk Audio" }, + { 0x070b, "DVD Audio" }, + { 0x070c, "TV Tuner Audio" }, + { 0x070d, "Satellite Rec Audio" }, + { 0x070e, "Cable Tuner Audio" }, + { 0x070f, "DSS Audio" }, + { 0x0710, "Radio Receiver" }, + { 0x0711, "Radio Transmitter" }, + { 0x0712, "Multi-Track Recorder" }, + { 0x0713, "Synthesizer" }, + { 0 }, +}; + +static int get_term_name(mixer_build_t *state, usb_audio_term_t *iterm, + unsigned char *name, int maxlen, int term_only) +{ + struct iterm_name_combo *names; + + if (iterm->name) + return snd_usb_copy_string_desc(state, iterm->name, name, maxlen); + + /* virtual type - not a real terminal */ + if (iterm->type >> 16) { + if (term_only) + return 0; + switch (iterm->type >> 16) { + case SELECTOR_UNIT: + strcpy(name, "Selector"); return 8; + case PROCESSING_UNIT: + strcpy(name, "Process Unit"); return 12; + case EXTENSION_UNIT: + strcpy(name, "Ext Unit"); return 8; + case MIXER_UNIT: + strcpy(name, "Mixer"); return 5; + default: + return sprintf(name, "Unit %d", iterm->id); + } + } + + switch (iterm->type & 0xff00) { + case 0x0100: + strcpy(name, "PCM"); return 3; + case 0x0200: + strcpy(name, "Mic"); return 3; + case 0x0400: + strcpy(name, "Headset"); return 7; + case 0x0500: + strcpy(name, "Phone"); return 5; + } + + for (names = iterm_names; names->type; names++) + if (names->type == iterm->type) { + strcpy(name, names->name); + return strlen(names->name); + } + return 0; +} + + +/* + * parse the source unit recursively until it reaches to a terminal + * or a branched unit. + */ +static int check_input_term(mixer_build_t *state, int id, usb_audio_term_t *term) +{ + unsigned char *p1; + + memset(term, 0, sizeof(*term)); + while ((p1 = find_audio_control_unit(state, id)) != NULL) { + term->id = id; + switch (p1[2]) { + case INPUT_TERMINAL: + term->type = combine_word(p1 + 4); + term->channels = p1[7]; + term->chconfig = combine_word(p1 + 8); + term->name = p1[11]; + return 0; + case FEATURE_UNIT: + id = p1[4]; + break; /* continue to parse */ + case MIXER_UNIT: + term->type = p1[2] << 16; /* virtual type */ + term->channels = p1[5 + p1[4]]; + term->chconfig = combine_word(p1 + 6 + p1[4]); + term->name = p1[p1[0] - 1]; + return 0; + case SELECTOR_UNIT: + /* call recursively to retrieve the channel info */ + if (check_input_term(state, p1[5], term) < 0) + return -ENODEV; + term->type = p1[2] << 16; /* virtual type */ + term->id = id; + term->name = p1[9 + p1[0] - 1]; + return 0; + case PROCESSING_UNIT: + case EXTENSION_UNIT: + if (p1[6] == 1) { + id = p1[7]; + break; /* continue to parse */ + } + term->type = p1[2] << 16; /* virtual type */ + term->channels = p1[7 + p1[6]]; + term->chconfig = combine_word(p1 + 8 + p1[6]); + term->name = p1[12 + p1[6] + p1[11 + p1[6]]]; + return 0; + default: + return -ENODEV; + } + } + return -ENODEV; +} + + +/* + * Feature Unit + */ + +/* feature unit control information */ +struct usb_feature_control_info { + const char *name; + unsigned int type; /* control type (mute, volume, etc.) */ +}; + +static struct usb_feature_control_info audio_feature_info[] = { + { "Mute", USB_MIXER_INV_BOOLEAN }, + { "Volume", USB_MIXER_S16 }, + { "Tone Control - Bass", USB_MIXER_S8 }, + { "Tone Control - Mid", USB_MIXER_S8 }, + { "Tone Control - Treble", USB_MIXER_S8 }, + { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */ + { "Auto Gain Control", USB_MIXER_BOOLEAN }, + { "Delay Control", USB_MIXER_U16 }, + { "Bass Boost", USB_MIXER_BOOLEAN }, + { "Loudness", USB_MIXER_BOOLEAN }, +}; + + +/* private_free callback */ +static void usb_mixer_elem_free(snd_kcontrol_t *kctl) +{ + if (kctl->private_data) { + snd_magic_kfree((void *)kctl->private_data); + kctl->private_data = 0; + } +} + + +/* + * interface to ALSA control for feature/mixer units + */ + +/* + * retrieve the minimum and maximum values for the specified control + */ +static int get_min_max(usb_mixer_elem_info_t *cval, int default_min) +{ + /* for failsafe */ + cval->min = default_min; + cval->max = cval->min + 1; + cval->res = 1; + + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) { + cval->initialized = 1; + } else { + int minchn = 0; + if (cval->cmask) { + int i; + for (i = 0; i < MAX_CHANNELS; i++) + if (cval->cmask & (1 << i)) { + minchn = i + 1; + break; + } + } + if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { + snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id); + return -EINVAL; + } + if (get_ctl_value(cval, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) { + cval->res = 1; + } else { + int last_valid_res = cval->res; + + while (cval->res > 1) { + if (set_ctl_value(cval, SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0) + break; + cval->res /= 2; + } + if (get_ctl_value(cval, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) + cval->res = last_valid_res; + } + if (cval->res == 0) + cval->res = 1; + cval->initialized = 1; + } + return 0; +} + + +/* get a feature/mixer unit info */ +static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = cval->channels; + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) { + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } else { + if (! cval->initialized) + get_min_max(cval, 0); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (cval->max - cval->min) / cval->res; + } + return 0; +} + +/* get the current value from feature/mixer unit */ +static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + int c, cnt, val, err; + + if (cval->cmask) { + cnt = 0; + for (c = 0; c < MAX_CHANNELS; c++) { + if (cval->cmask & (1 << c)) { + err = get_cur_mix_value(cval, c + 1, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } +#endif + if (err < 0) { + snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); + return err; + } + val = get_relative_value(cval, val); + ucontrol->value.integer.value[cnt] = val; + cnt++; + } + } + } else { + /* master channel */ + err = get_cur_mix_value(cval, 0, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } +#endif + if (err < 0) { + snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err); + return err; + } + val = get_relative_value(cval, val); + ucontrol->value.integer.value[0] = val; + } + return 0; +} + +/* put the current value to feature/mixer unit */ +static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + int c, cnt, val, oval, err; + int changed = 0; + + if (cval->cmask) { + cnt = 0; + for (c = 0; c < MAX_CHANNELS; c++) { + if (cval->cmask & (1 << c)) { + err = get_cur_mix_value(cval, c + 1, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif + if (err < 0) + return err; + val = ucontrol->value.integer.value[cnt]; + val = get_abs_value(cval, val); + if (oval != val) { + set_cur_mix_value(cval, c + 1, val); + changed = 1; + } + get_cur_mix_value(cval, c + 1, &val); + cnt++; + } + } + } else { + /* master channel */ + err = get_cur_mix_value(cval, 0, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif + if (err < 0) + return err; + val = ucontrol->value.integer.value[0]; + val = get_abs_value(cval, val); + if (val != oval) { + set_cur_mix_value(cval, 0, val); + changed = 1; + } + } + return changed; +} + +static snd_kcontrol_new_t usb_feature_unit_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later manually */ + .info = mixer_ctl_feature_info, + .get = mixer_ctl_feature_get, + .put = mixer_ctl_feature_put, +}; + + +/* + * build a feature control + */ + +static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, + unsigned int ctl_mask, int control, + usb_audio_term_t *iterm, int unitid) +{ + unsigned int len = 0; + int mapped_name = 0; + int nameid = desc[desc[0] - 1]; + snd_kcontrol_t *kctl; + usb_mixer_elem_info_t *cval; + + control++; /* change from zero-based to 1-based value */ + + if (control == USB_FEATURE_GEQ) { + /* FIXME: not supported yet */ + return; + } + + cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + if (! cval) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + return; + } + cval->chip = state->chip; + cval->ctrlif = state->ctrlif; + cval->id = unitid; + cval->control = control; + cval->cmask = ctl_mask; + cval->val_type = audio_feature_info[control-1].type; + if (ctl_mask == 0) + cval->channels = 1; /* master channel */ + else { + int i, c = 0; + for (i = 0; i < 16; i++) + if (ctl_mask & (1 << i)) + c++; + cval->channels = c; + } + + /* get min/max values */ + get_min_max(cval, 0); + + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + snd_magic_kfree(cval); + return; + } + kctl->private_free = usb_mixer_elem_free; + + len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name)); + mapped_name = len != 0; + if (! len && nameid) + len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); + + switch (control) { + case USB_FEATURE_MUTE: + case USB_FEATURE_VOLUME: + /* determine the control name. the rule is: + * - if a name id is given in descriptor, use it. + * - if the connected input can be determined, then use the name + * of terminal type. + * - if the connected output can be determined, use it. + * - otherwise, anonymous name. + */ + if (! len) { + len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1); + if (! len) + len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); + if (! len) + len = sprintf(kctl->id.name, "Feature %d", unitid); + } + /* determine the stream direction: + * if the connected output is USB stream, then it's likely a + * capture stream. otherwise it should be playback (hopefully :) + */ + if (! mapped_name && ! (state->oterm.type >> 16)) { + if ((state->oterm.type & 0xff00) == 0x0100) { + if (len + 8 < sizeof(kctl->id.name)) { + strcpy(kctl->id.name + len, " Capture"); + len += 8; + } + } else { + if (len + 9 < sizeof(kctl->id.name)) { + strcpy(kctl->id.name + len, " Playback"); + len += 9; + } + } + } + if (len + 7 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume"); + break; + + default: + if (! len) + strcpy(kctl->id.name, audio_feature_info[control-1].name); + break; + } + + /* quirk for UDA1321/N101 */ + /* note that detection between firmware 2.1.1.7 (N101) and later 2.1.1.21 */ + /* is not very clear from datasheets */ + /* I hope that the min value is -15360 for newer firmware --jk */ + if (((state->vendor == 0x471 && (state->product == 0x104 || state->product == 0x105 || state->product == 0x101)) || + (state->vendor == 0x672 && state->product == 0x1041)) && !strcmp(kctl->id.name, "PCM Playback Volume") && + cval->min == -15616) { + snd_printk("USB Audio: using volume control quirk for the UDA1321/N101 chip\n"); + cval->max = -256; + } + + snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", + cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); + add_control_to_empty(state->chip->card, kctl); +} + + + +/* + * parse a feature unit + * + * most of controlls are defined here. + */ +static int parse_audio_feature_unit(mixer_build_t *state, int unitid, unsigned char *ftr) +{ + int channels, i, j; + usb_audio_term_t iterm; + unsigned int master_bits, first_ch_bits; + int err, csize; + + if (ftr[0] < 7 || ! (csize = ftr[5]) || ftr[0] < 7 + csize) { + snd_printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); + return -EINVAL; + } + + /* parse the source unit */ + if ((err = parse_audio_unit(state, ftr[4])) < 0) + return err; + + /* determine the input source type and name */ + if (check_input_term(state, ftr[4], &iterm) < 0) + return -EINVAL; + + channels = (ftr[0] - 7) / csize - 1; + + master_bits = snd_usb_combine_bytes(ftr + 6, csize); + if (channels > 0) + first_ch_bits = snd_usb_combine_bytes(ftr + 6 + csize, csize); + else + first_ch_bits = 0; + /* check all control types */ + for (i = 0; i < 10; i++) { + unsigned int ch_bits = 0; + for (j = 0; j < channels; j++) { + unsigned int mask = snd_usb_combine_bytes(ftr + 6 + csize * (j+1), csize); + if (mask & (1 << i)) + ch_bits |= (1 << j); + } + if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ + build_feature_ctl(state, ftr, ch_bits, i, &iterm, unitid); + if (master_bits & (1 << i)) + build_feature_ctl(state, ftr, 0, i, &iterm, unitid); + } + + return 0; +} + + +/* + * Mixer Unit + */ + +/* + * build a mixer unit control + * + * the callbacks are identical with feature unit. + * input channel number (zero based) is given in control field instead. + */ + +static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, + int in_ch, int unitid) +{ + usb_mixer_elem_info_t *cval; + unsigned int num_ins = desc[4]; + unsigned int num_outs = desc[5 + num_ins]; + unsigned int i, len; + snd_kcontrol_t *kctl; + usb_audio_term_t iterm; + + cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + if (! cval) + return; + + if (check_input_term(state, desc[5 + in_ch], &iterm) < 0) + return; + + cval->chip = state->chip; + cval->ctrlif = state->ctrlif; + cval->id = unitid; + cval->control = in_ch + 1; /* based on 1 */ + cval->val_type = USB_MIXER_S16; + for (i = 0; i < num_outs; i++) { + if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) { + cval->cmask |= (1 << i); + cval->channels++; + } + } + + /* get min/max values */ + get_min_max(cval, 0); + + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + snd_magic_kfree(cval); + return; + } + kctl->private_free = usb_mixer_elem_free; + + len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); + if (! len) + len = get_term_name(state, &iterm, kctl->id.name, sizeof(kctl->id.name), 0); + if (! len) + len = sprintf(kctl->id.name, "Mixer Source %d", in_ch); + if (len + 7 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, " Volume"); + + snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n", + cval->id, kctl->id.name, cval->channels, cval->min, cval->max); + add_control_to_empty(state->chip->card, kctl); +} + + +/* + * parse a mixer unit + */ +static int parse_audio_mixer_unit(mixer_build_t *state, int unitid, unsigned char *desc) +{ + int num_ins, num_outs; + int i, err; + if (desc[0] < 12 || ! (num_ins = desc[4]) || ! (num_outs = desc[5 + num_ins])) + return -EINVAL; + + for (i = 0; i < num_ins; i++) { + err = parse_audio_unit(state, desc[5 + i]); + if (err < 0) + return err; + if (check_matrix_bitmap(desc + 9 + num_ins, i, 0, num_outs)) + build_mixer_unit_ctl(state, desc, i, unitid); + } + return 0; +} + + +/* + * Processing Unit / Extension Unit + */ + +/* get callback for processing/extension unit */ +static int mixer_ctl_procunit_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + int err, val; + + err = get_cur_ctl_value(cval, cval->control << 8, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } +#endif + if (err < 0) + return err; + val = get_relative_value(cval, val); + ucontrol->value.integer.value[0] = val; + return 0; +} + +/* put callback for processing/extension unit */ +static int mixer_ctl_procunit_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + int val, oval, err; + + err = get_cur_ctl_value(cval, cval->control << 8, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif + if (err < 0) + return err; + val = ucontrol->value.integer.value[0]; + val = get_abs_value(cval, val); + if (val != oval) { + set_cur_ctl_value(cval, cval->control << 8, val); + return 1; + } + return 0; +} + +/* alsa control interface for processing/extension unit */ +static snd_kcontrol_new_t mixer_procunit_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = mixer_ctl_feature_info, + .get = mixer_ctl_procunit_get, + .put = mixer_ctl_procunit_put, +}; + + +/* + * predefined data for processing units + */ +struct procunit_value_info { + int control; + char *suffix; + int val_type; + int min_value; +}; + +struct procunit_info { + int type; + char *name; + struct procunit_value_info *values; +}; + +static struct procunit_value_info updown_proc_info[] = { + { USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 }, + { 0 } +}; +static struct procunit_value_info prologic_proc_info[] = { + { USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 }, + { 0 } +}; +static struct procunit_value_info threed_enh_proc_info[] = { + { USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 }, + { 0 } +}; +static struct procunit_value_info reverb_proc_info[] = { + { USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 }, + { USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 }, + { USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 }, + { 0 } +}; +static struct procunit_value_info chorus_proc_info[] = { + { USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 }, + { USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 }, + { USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 }, + { 0 } +}; +static struct procunit_value_info dcr_proc_info[] = { + { USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 }, + { USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 }, + { USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 }, + { USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 }, + { USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 }, + { 0 } +}; + +static struct procunit_info procunits[] = { + { USB_PROC_UPDOWN, "Up Down", updown_proc_info }, + { USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info }, + { USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info }, + { USB_PROC_REVERB, "Reverb", reverb_proc_info }, + { USB_PROC_CHORUS, "Chorus", chorus_proc_info }, + { USB_PROC_DCR, "DCR", dcr_proc_info }, + { 0 }, +}; + +/* + * build a processing/extension unit + */ +static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name) +{ + int num_ins = dsc[6]; + usb_mixer_elem_info_t *cval; + snd_kcontrol_t *kctl; + int i, err, nameid, type, len; + struct procunit_info *info; + struct procunit_value_info *valinfo; + static struct procunit_value_info default_value_info[] = { + { 0x01, "Switch", USB_MIXER_BOOLEAN }, + { 0 } + }; + static struct procunit_info default_info = { + 0, NULL, default_value_info + }; + + if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) { + snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); + return -EINVAL; + } + + for (i = 0; i < num_ins; i++) { + if ((err = parse_audio_unit(state, dsc[7 + i])) < 0) + return err; + } + + type = combine_word(&dsc[4]); + if (! type) + return 0; /* undefined? */ + + for (info = list; info && info->type; info++) + if (info->type == type) + break; + if (! info || ! info->type) + info = &default_info; + + for (valinfo = info->values; valinfo->control; valinfo++) { + /* FIXME: bitmap might be longer than 8bit */ + if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) + continue; + cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + if (! cval) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + return -ENOMEM; + } + cval->chip = state->chip; + cval->ctrlif = state->ctrlif; + cval->id = unitid; + cval->control = valinfo->control; + cval->val_type = valinfo->val_type; + cval->channels = 1; + + /* get min/max values */ + get_min_max(cval, valinfo->min_value); + + kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + snd_magic_kfree(cval); + return -ENOMEM; + } + kctl->private_free = usb_mixer_elem_free; + + if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name))) + ; + else if (info->name) + strcpy(kctl->id.name, info->name); + else { + nameid = dsc[12 + num_ins + dsc[11 + num_ins]]; + len = 0; + if (nameid) + len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); + if (! len) { + strncpy(kctl->id.name, name, sizeof(kctl->id.name) - 1); + kctl->id.name[sizeof(kctl->id.name)-1] = 0; + } + } + len = strlen(kctl->id.name); + if (len + sizeof(valinfo->suffix) + 1 < sizeof(kctl->id.name)) { + kctl->id.name[len] = ' '; + strcpy(kctl->id.name + len + 1, valinfo->suffix); + } + snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n", + cval->id, kctl->id.name, cval->channels, cval->min, cval->max); + if ((err = add_control_to_empty(state->chip->card, kctl)) < 0) + return err; + } + return 0; +} + + +static int parse_audio_processing_unit(mixer_build_t *state, int unitid, unsigned char *desc) +{ + return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit"); +} + +static int parse_audio_extension_unit(mixer_build_t *state, int unitid, unsigned char *desc) +{ + return build_audio_procunit(state, unitid, desc, NULL, "Extension Unit"); +} + + +/* + * Selector Unit + */ + +/* info callback for selector unit + * use an enumerator type for routing + */ +static int mixer_ctl_selector_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + char **itemlist = (char **)kcontrol->private_value; + + snd_assert(itemlist, return -EINVAL); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = cval->max; + if ((int)uinfo->value.enumerated.item >= cval->max) + uinfo->value.enumerated.item = cval->max - 1; + strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]); + return 0; +} + +/* get callback for selector unit */ +static int mixer_ctl_selector_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + int val, err; + + err = get_cur_ctl_value(cval, 0, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.enumerated.item[0] = 0; + return 0; + } +#endif + if (err < 0) + return err; + val = get_relative_value(cval, val); + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +/* put callback for selector unit */ +static int mixer_ctl_selector_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + int val, oval, err; + + err = get_cur_ctl_value(cval, 0, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif + if (err < 0) + return err; + val = ucontrol->value.enumerated.item[0]; + val = get_abs_value(cval, val); + if (val != oval) { + set_cur_ctl_value(cval, 0, val); + return 1; + } + return 0; +} + +/* alsa control interface for selector unit */ +static snd_kcontrol_new_t mixer_selectunit_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = mixer_ctl_selector_info, + .get = mixer_ctl_selector_get, + .put = mixer_ctl_selector_put, +}; + + +/* private free callback. + * free both private_data and private_value + */ +static void usb_mixer_selector_elem_free(snd_kcontrol_t *kctl) +{ + int i, num_ins = 0; + + if (kctl->private_data) { + usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kctl->private_data,); + num_ins = cval->max; + snd_magic_kfree(cval); + kctl->private_data = 0; + } + if (kctl->private_value) { + char **itemlist = (char **)kctl->private_value; + for (i = 0; i < num_ins; i++) + kfree(itemlist[i]); + kfree(itemlist); + kctl->private_value = 0; + } +} + +/* + * parse a selector unit + */ +static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned char *desc) +{ + unsigned int num_ins = desc[4]; + unsigned int i, nameid, len; + int err; + usb_mixer_elem_info_t *cval; + snd_kcontrol_t *kctl; + char **namelist; + + if (! num_ins || desc[0] < 6 + num_ins) { + snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid); + return -EINVAL; + } + + for (i = 0; i < num_ins; i++) { + if ((err = parse_audio_unit(state, desc[5 + i])) < 0) + return err; + } + + cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + if (! cval) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + return -ENOMEM; + } + cval->chip = state->chip; + cval->ctrlif = state->ctrlif; + cval->id = unitid; + cval->val_type = USB_MIXER_U8; + cval->channels = 1; + cval->min = 1; + cval->max = num_ins; + cval->res = 1; + cval->initialized = 1; + + namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL); + if (! namelist) { + snd_printk(KERN_ERR "cannot malloc\n"); + snd_magic_kfree(cval); + return -ENOMEM; + } +#define MAX_ITEM_NAME_LEN 64 + for (i = 0; i < num_ins; i++) { + usb_audio_term_t iterm; + len = 0; + namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); + if (! namelist[i]) { + snd_printk(KERN_ERR "cannot malloc\n"); + while (--i > 0) + kfree(namelist[i]); + kfree(namelist); + snd_magic_kfree(cval); + return -ENOMEM; + } + if (check_input_term(state, desc[5 + i], &iterm) >= 0) + len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); + if (! len) + sprintf(namelist[i], "Input %d", i); + } + + kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); + if (! kctl) { + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); + snd_magic_kfree(cval); + return -ENOMEM; + } + kctl->private_value = (unsigned long)namelist; + kctl->private_free = usb_mixer_selector_elem_free; + + nameid = desc[desc[0] - 1]; + len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); + if (len) + ; + else if (nameid) + snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); + else { + len = get_term_name(state, &state->oterm, + kctl->id.name, sizeof(kctl->id.name), 0); + if (! len) + len = sprintf(kctl->id.name, "USB"); + if ((state->oterm.type & 0xff00) == 0x0100) { + if (len + 15 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, " Capture Source"); + } else { + if (len + 16 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, " Playback Source"); + } + } + + snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", + cval->id, kctl->id.name, num_ins); + if ((err = add_control_to_empty(state->chip->card, kctl)) < 0) + return err; + + return 0; +} + + +/* + * parse an audio unit recursively + */ + +static int parse_audio_unit(mixer_build_t *state, int unitid) +{ + unsigned char *p1; + + if (test_and_set_bit(unitid, state->unitbitmap)) + return 0; /* the unit already visited */ + + p1 = find_audio_control_unit(state, unitid); + if (!p1) { + snd_printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); + return -EINVAL; + } + + switch (p1[2]) { + case INPUT_TERMINAL: + return 0; /* NOP */ + case MIXER_UNIT: + return parse_audio_mixer_unit(state, unitid, p1); + case SELECTOR_UNIT: + return parse_audio_selector_unit(state, unitid, p1); + case FEATURE_UNIT: + return parse_audio_feature_unit(state, unitid, p1); + case PROCESSING_UNIT: + return parse_audio_processing_unit(state, unitid, p1); + case EXTENSION_UNIT: + return parse_audio_extension_unit(state, unitid, p1); + default: + snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return -EINVAL; + } +} + +/* + * create mixer controls + * + * walk through all OUTPUT_TERMINAL descriptors to search for mixers + */ +int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) +{ + unsigned char *desc; + mixer_build_t state; + int err; + const struct usbmix_ctl_map *map; + struct usb_device_descriptor *dev = &chip->dev->descriptor; + struct usb_host_interface *hostif = &chip->dev->actconfig->interface[ctrlif].altsetting[0]; + + strcpy(chip->card->mixername, "USB Mixer"); + + memset(&state, 0, sizeof(state)); + state.chip = chip; + state.buffer = hostif->extra; + state.buflen = hostif->extralen; + state.ctrlif = ctrlif; + state.vendor = dev->idVendor; + state.product = dev->idProduct; + + /* check the mapping table */ + for (map = usbmix_ctl_maps; map->vendor; map++) { + if (map->vendor == dev->idVendor && map->product == dev->idProduct) { + state.map = map->map; + break; + } + } + + desc = NULL; + while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) { + if (desc[0] < 9) + continue; /* invalid descriptor? */ + set_bit(desc[3], state.unitbitmap); /* mark terminal ID as visited */ + state.oterm.id = desc[3]; + state.oterm.type = combine_word(&desc[4]); + state.oterm.name = desc[8]; + err = parse_audio_unit(&state, desc[7]); + if (err < 0) + return err; + } + return 0; +} diff -urN linux-2.4.21-rc1.orig/sound/usb/usbmixer_maps.c linux/sound/usb/usbmixer_maps.c --- linux-2.4.21-rc1.orig/sound/usb/usbmixer_maps.c 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/usbmixer_maps.c 2002-12-11 08:38:11.000000000 -0700 @@ -0,0 +1,100 @@ +/* + * Additional mixer mapping + * + * Copyright (c) 2002 by 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +struct usbmix_name_map { + int id; + const char *name; + int control; +}; + +struct usbmix_ctl_map { + int vendor; + int product; + const struct usbmix_name_map *map; +}; + +/* + * USB control mappers for SB Exitigy + */ + +/* + * Topology of SB Extigy (see on the wide screen :) + +USB_IN[1] --->FU[2]------------------------------+->MU[16]-->PU[17]-+->FU[18]--+->EU[27]--+->EU[21]-->FU[22]--+->FU[23] > Dig_OUT[24] + ^ | | | | +USB_IN[3] -+->SU[5]-->FU[6]--+->MU[14] ->PU[15]->+ | | | +->FU[25] > Dig_OUT[26] + ^ ^ | | | | +Dig_IN[4] -+ | | | | +->FU[28]---------------------> Spk_OUT[19] + | | | | +Lin-IN[7] -+-->FU[8]---------+ | | +----------------------------------------> Hph_OUT[20] + | | | +Mic-IN[9] --+->FU[10]----------------------------+ | + || | + || +----------------------------------------------------+ + VV V + ++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13] +*/ + +static struct usbmix_name_map extigy_map[] = { + /* 1: IT pcm */ + { 2, "PCM Playback" }, /* FU */ + /* 3: IT pcm */ + /* 4: IT digital in */ + { 5, "Digital In Playback Source" }, /* SU */ + { 6, "Digital In" }, /* FU */ + /* 7: IT line */ + { 8, "Line Playback" }, /* FU */ + /* 9: IT mic */ + { 10, "Mic Playback" }, /* FU */ + { 11, "Capture Source" }, /* SU */ + { 12, "Capture" }, /* FU */ + /* 13: OT pcm capture */ + /* 14: MU (w/o controls) */ + /* 15: PU (3D enh) */ + /* 16: MU (w/o controls) */ + /* 17: PU (updown) */ /* FIXME: what control? */ + { 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */ + { 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */ + { 18, "Master Playback" }, /* FU; others */ + /* 19: OT speaker */ + /* 20: OT headphone */ + { 21, "Digital Out Extension" }, /* EU */ /* FIXME: what? */ + { 22, "Digital Out Playback" }, /* FU */ + { 23, "Digital Out1 Playback" }, /* FU */ /* FIXME: corresponds to 24 */ + /* 24: OT digital out */ + { 25, "Digital Out2 Playback" }, /* FU */ /* FIXME: corresponds to 26 */ + /* 26: OT digital out */ + { 27, "Output Extension" }, /* EU */ /* FIXME: what? */ + /* 28: FU (mute) */ + { 0 } /* terminator */ +}; + + +/* + * Control map entries + */ + +static struct usbmix_ctl_map usbmix_ctl_maps[] = { + { 0x41e, 0x3000, extigy_map }, + { 0 } /* terminator */ +}; + diff -urN linux-2.4.21-rc1.orig/sound/usb/usbquirks.h linux/sound/usb/usbquirks.h --- linux-2.4.21-rc1.orig/sound/usb/usbquirks.h 1969-12-31 17:00:00.000000000 -0700 +++ linux/sound/usb/usbquirks.h 2003-02-28 07:54:40.000000000 -0700 @@ -0,0 +1,761 @@ +/* + * ALSA USB Audio Driver + * + * Copyright (c) 2002 by Takashi Iwai , + * Clemens Ladisch + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The contents of this file are part of the driver's id_table. + * + * In a perfect world, this file would be empty. + */ + +/* + * Use this for devices where other interfaces are standard compliant, + * to prevent the quirk being applied to those interfaces. (To work with + * hotplugging, bDeviceClass must be set to USB_CLASS_PER_INTERFACE.) + */ +#define USB_DEVICE_VENDOR_SPEC(vend, prod) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | \ + USB_DEVICE_ID_MATCH_PRODUCT | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = vend, \ + .idProduct = prod, \ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC + +/* Yamaha devices */ +{ + USB_DEVICE(0x0499, 0x1000), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "UX256", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1001), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "MU1000", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1002), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "MU2000", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1003), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "MU500", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0499, 0x1004), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "UW500", + .ifnum = 3, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1005), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "MOTIF6", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1006), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "MOTIF7", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1007), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "MOTIF8", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1008), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "UX96", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1009), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "UX16", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0499, 0x100a), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "EOS BX", + .ifnum = 3, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x100e), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "S08", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x100f), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "CLP-150", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1010), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "CLP-170", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1011), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "P-250", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1012), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "TYROS", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1013), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "PF-500", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x1014), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "S90", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x5002), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "DME32", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x5003), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "DM2000", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, +{ + USB_DEVICE(0x0499, 0x5004), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "02R96", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, + +/* + * Roland/RolandED/Edirol devices + * + * The USB MIDI Specification has been written by Roland, + * but a 100% conforming Roland device has yet to be found. + */ +{ + USB_DEVICE(0x0582, 0x0000), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "UA-100", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x09, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = EP_CS_ATTR_FILL_MAX, + .endpoint = 0x81, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0002), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UM-4", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ + USB_DEVICE(0x0582, 0x0003), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "SC-8850", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x003f, + .in_cables = 0x003f + } + } +}, +{ + USB_DEVICE(0x0582, 0x0004), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "U-8", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0005), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UM-2", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0007), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "SC-8820", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0013, + .in_cables = 0x0013 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0008), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "PC-300", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0009), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UM-1", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x000b), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "SK-500", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0013, + .in_cables = 0x0013 + } + } +}, +{ + /* thanks to Emiliano Grilli for helping researching this data */ + USB_DEVICE(0x0582, 0x000c), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "SC-D70", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x81, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0012), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "XV-5050", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0014), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UM-880", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x01ff, + .in_cables = 0x01ff + } + } +}, +{ + USB_DEVICE(0x0582, 0x0016), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "SD-90", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ + USB_DEVICE(0x0582, 0x0023), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UM-550", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x003f, + .in_cables = 0x003f + } + } +}, +{ + /* + * This quirk is for the "Advanced Driver" mode. If off, the UA-20 + * has ID 0x0026 and is standard compliant, but has only 16-bit PCM + * and no MIDI. + */ + USB_DEVICE(0x0582, 0x0025), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UA-20", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 1, + .type = QUIRK_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_STANDARD_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ + USB_DEVICE(0x0582, 0x0027), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "SD-20", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0029), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "SD-80", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UA-700", + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE(0x0582, 0x002d), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "XV-2020", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0033), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "PCR", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, + +/* Midiman/M-Audio devices */ +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1002), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 2x2", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1011), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 1x1", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1015), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "Keystation", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1021), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 4x4", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x000f + } + } +}, +{ + /* + * For hardware revision 1.05; in the later revisions (1.10 and + * 1.21), 0x1031 is the ID for the device without firmware. + * Thanks to Olaf Giesbrecht + */ + USB_DEVICE_VER(0x0763, 0x1031, 0x0100, 0x0109), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 8x8", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x01ff, + .in_cables = 0x01ff + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1033), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 8x8", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x01ff, + .in_cables = 0x01ff + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x1041), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "MidiSport 2x4", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x000f, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "Quattro", + .ifnum = 9, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "M-Audio", + .product_name = "AudioPhile", + .ifnum = 9, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, + +/* Mark of the Unicorn devices */ +{ + /* thanks to Woodley Packard */ + USB_DEVICE(0x07fd, 0x0001), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "MOTU", + .product_name = "Fastlane", + .ifnum = 1, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, + +/* + * Boot-up quirks + */ +/* Extigy needs to send a vendor-specific control to boot up the + * correct interface + */ +{ + USB_DEVICE(0x041e, 0x3000), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "CreativeLabs", + .product_name = "Sound Blaster Extigy", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_BOOT_EXTIGY + } +}, + +#undef USB_DEVICE_VENDOR_SPEC